首 页 行业资讯 新车 试驾评测 养车用车 车型库

普通类如何初始化Spring的bean?

发布网友 发布时间:2022-04-22 04:31

我来回答

1个回答

热心网友 时间:2023-08-12 21:40

先上一张类图:


这里借用在web项目中的加载过程来熟悉Spring ApplicationContext的加载过程:

1. 在一般的Web项目中,我们很多情况下是利用org.springframework.web.context.ContextLoaderListener这个类进行容器的初始化。该类会被Web容器(如Tomcat)自动实例化,并调用contextInitialized方法。

/**

* Initialize the root web application context.

*/

@Override

public void contextInitialized(ServletContextEvent event) {

initWebApplicationContext(event.getServletContext());

}

2. initWebApplicationContext方式是从父类ContextLoader中继承来的。该方法的大致的逻辑是:判定web容器中是否注册了ROOT_APPLICATION_CONTEXT_STTRIBUTE(为WebApplicationContext.class.getName()+ “.ROOT”)的属性,如果有,则抛出异常,以此保证一个Web容器中只有一个Spring根容器;创建容器的时候,要判定需要实例化哪种类来实例化当前web容器的Spring根容器,如果我们设置了名称为“contextClass”的context-param,则取我们设置的类,该类应当实现ConfigurableWebApplicationContext接口或继承自实现了该接口的子类(如XmlWebApplicationContext、GroovyWebApplicationContext和AnnotationConfigWebApplicationContext),通常我们都不会设置,Spring会默认取与ContextLoader同目录下的ContextLoader.properties中记录的类名作为根容器的类型(默认是org.springframework.web.context.support.XmlWebApplicationContext);实例化容器;配置容器;设置容器为Web容器的属性。下面代码中去掉了log和异常等部分。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {

throw new IllegalStateException(

"Cannot initialize context because there is already a root application context present - " +

"check whether you have multiple ContextLoader* definitions in your web.xml!");

}

if (this.context == null) {

this.context = createWebApplicationContext(servletContext);

}

if (this.context instanceof ConfigurableWebApplicationContext) {

ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;

if (!cwac.isActive()) {

if (cwac.getParent() == null) {

ApplicationContext parent = loadParentContext(servletContext);

cwac.setParent(parent);

}

configureAndRefreshWebApplicationContext(cwac, servletContext);

}

}

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();

if (ccl == ContextLoader.class.getClassLoader()) {

currentContext = this.context;

}

else if (ccl != null) {

currentContextPerThread.put(ccl, this.context);

}

return this.context;

}

3. configureAndRefreshWebApplicationContext方法负责对容器进行初始化,该方法的逻辑主要有一下几点:设置一个contextId(从contextId这个param获取,如果没有则默认是WebApplicationContext的类名 + “:” + servlet context的路径);设置配置位置(从contextConfigLocation 这个param获取,如果未配置,则默认是/WEB-INF/applicationContext.xml,在XmlWebApplicationContext中可以看出);自定义该congtext;调用该Context的refresh()方法。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContextwac, ServletContext sc) {

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {

String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);

if (idParam != null) {

wac.setId(idParam);

}

else {

wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +

ObjectUtils.getDisplayString(sc.getContextPath()));

}

}

wac.setServletContext(sc);

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);

if (configLocationParam != null) {

wac.setConfigLocation(configLocationParam);

}

ConfigurableEnvironment env = wac.getEnvironment();

if (env instanceof ConfigurableWebEnvironment) {

((ConfigurableWebEnvironment) env).initPropertySources(sc, null);

}

customizeContext(sc, wac);

wac.refresh();

}

4. ConfigurableApplicationContext接口的实现类AbstractApplicationContext中的refresh方法,定义了一个模版,在该方法里,会完成加载资源、配置文件解析、Bean定义的注册、组件的初始化等工作。每一步工作都定义在响应的方法中,清晰明了。下面的方法省略了异常处理。

synchronized (this.startupShutdownMonitor) {.

prepareRefresh(); //准备

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//获得一个Bean工厂

prepareBeanFactory(beanFactory); //准备好工厂

postProcessBeanFactory(beanFactory);//处理工厂时执行

invokeBeanFactoryPostProcessors(beanFactory);//执行工厂处理

registerBeanPostProcessors(beanFactory);//注册bean产生*

initMessageSource();//初始化消息源

initApplicationEventMulticaster();//初始化应用事件广播器

onRefresh();//初始化该容器子类其他特定的bean

registerListeners();//检查*并注册

finishBeanFactoryInitialization(beanFactory);// 初始化非lazy的singleton的bean

finishRefresh();//发布结束fresh消息

}

5. 这几个方法中比较重要的方法是obtainFreshBeanFactory方法和finishBeanFactoryInitialization方法,一个用来获得bean工厂,一个用来实例化bean并注入的。这里先分析obtainFreshBeanFactory方法。在继承链中,AbstractApplicationContext实现了该方法,并定义了refreshBeanFactory方法让后代实现。AbstractRefreshableApplicationContext又实现了refreshBeanFactory方法,如果有一个BeanFactory销毁它,然后再创建一个。

protected final void refreshBeanFactory() throws BeansException {

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

try {

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(beanFactory);

loadBeanDefinitions(beanFactory);

synchronized (this.beanFactoryMonitor) {

this.beanFactory = beanFactory;

}

}

}

6. loadBeanDefinitions方法会由子类实现,在XmlWebApplicationContext中就实现了该方法。该方法的逻辑为:new一个XmlBeanDefinitionReader;设置该Reader的ResourceLoader(XmlWebApplicationContext设置的是它自己,因为它间接实现了ResourceLoader接口);、使用该Reader加载Bean定义。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throwsBeansException, IOException {

XmlBeanDefinitionReader beanDefinitionReader = newXmlBeanDefinitionReader(beanFactory);

beanDefinitionReader.setEnvironment(getEnvironment());

beanDefinitionReader.setResourceLoader(this);

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

initBeanDefinitionReader(beanDefinitionReader);

loadBeanDefinitions(beanDefinitionReader);

}

7. loadeBeanDefinitions方法会使用reader从每个配置中读取bean定义。该方法大概逻辑为:从每个location解析出一些resouce(代表特定的资源,如一个文件)。在AbstractBeanDefinitionReader中实现的loadBeanDefinitions(String,Set<Resource> actualResources) 方法如下:

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throwsBeanDefinitionStoreException {

ResourceLoader resourceLoader = getResourceLoader();

if (resourceLoader == null) {

throw new BeanDefinitionStoreException(

"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");

}

if (resourceLoader instanceof ResourcePatternResolver) {

try {

Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);

int loadCount = loadBeanDefinitions(resources);

if (actualResources != null) {

for (Resource resource : resources) {

actualResources.add(resource);

}

}

if (logger.isDebugEnabled()) {

logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" +location + "]");

}

return loadCount;

}

catch (IOException ex) {

throw new BeanDefinitionStoreException(

"Could not resolve bean definition resource pattern [" + location + "]", ex);

}

}

else {

Resource resource = resourceLoader.getResource(location);

int loadCount = loadBeanDefinitions(resource);

if (actualResources != null) {

actualResources.add(resource);

}

if (logger.isDebugEnabled()) {

logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");

}

return loadCount;

}

}

8. 在XmlBeanDefinitionReader中才是真正的加载Bean定义的实现。它从每个Resource中获得输入流,封装成InputSource;调用doLoadBeanDefinitions从该InputSource中获得一个Document,并使用registerBeanDefinitions方法根据该Document注册BeanDefinitions;registerBeanDefinitions方法中先是创建一个DefaultBeanDefinitionDocumentReader实例,再使用该实例来注册BeanDefinition;该实例会从Document的根元素开始注册BeanDefinition。值得注意的是在该类又会委派一个BeanDefinitionParserDelegate来解析Document元素。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

if (delegate.isDefaultNamespace(root)) {

NodeList nl = root.getChildNodes();

for (int i = 0; i < nl.getLength(); i++) {

Node node = nl.item(i);

if (node instanceof Element) {

Element ele = (Element) node;

if (delegate.isDefaultNamespace(ele)) {

parseDefaultElement(ele, delegate);

}

else {

delegate.parseCustomElement(ele);

}

}

}

}

else {

delegate.parseCustomElement(root);

}

}

9. BeanDefinitionParserDelegate会根据节点的命名空间使用不同的NamespaceHandler进行解析。在XmlBeanDefinitionReader创建DefaultBeanDefinitionDocumentReader时候会传入一个XmlReaderContext,该Context中保存了一个NamespaceHandlerResolver,该HandlerResolver维护一个Map,用来解析对于一个命名空间的具体Handler,其默认使用META-INF/spring.handlers 文件保存Handler的种类,Spring支持的Handler如下图:

BeanDefinitionDelegate使用获得的NamespaceHandler解析元素节点,获得BeanDefinition。

10. 不同的NamespaceHandler解析过程不一样。同时,一个NamespaceHandler中有多个Parser来解析不同种类的元素。以ContextNamspaceHandler为例,它支持的Parser如下图所示:


声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com