Spring IoC源码详解

本文中,所有spring framework源码,均采用5.0.x版本

在《Spring IoC概念分析》一文中,我们对Spring IoC的前置概念和整体流程有了一个初步的了解,在本文中,我们会解读源码,力图将spring IoC的逻辑扁平化,可视化。

1. Spring IoC容器的启动

applicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些,ApplicationContext则是“应用的容器”了;在Web应用中,我们会用到WebApplicationContext,WebApplicationContext继承自ApplicationContext;

以企业级java项目最常用的web项目为例;我们知道,在web项目中,Spring启动是在web.xml配置监听器,如下所示:

1
2
3
4
<!-- 配置Spring上下文监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

可以看到,ContextLoaderListener类是spring IoC容器启动的核心,也是整个Spring框架的启动入口:

ContextLoaderListener类实现了Tomcat容器的ServletContextListener接口,所以它与普通的Servlet监听是一样的。同样是重写到两个方法:

  • contextInitialized()方法在web容器初始化时执行
  • contextDestroyed()方法在容器销毁时执行。

WEB容器启动时会触发初始化事件,ContextLoaderListener监听到这个事件,其contextInitialized()方法会被调用,在这个方法中Spring会初始化一个root上下文,即WebApplicationContext。

WebApplicationContext是一个接口,其实际默认实现类是XmlWebApplicationContext。(重点关注红色箭头指示的两条继承/实现关系)

这个就是Spring IOC的容器,其对应bean定义的配置信息由web.xml中的context-param来指定:

1
2
3
4
5
6
<!-- 配置Spring配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml
</param-value>
</context-param>

在ContextLoaderListener类中,只是实现了ServletContextListener提供的到两个方法,Spring启动主要的逻辑在父类ContextLoader的方法initWebApplicationContext实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

public ContextLoaderListener() {
}

public ContextLoaderListener(WebApplicationContext context) {
super(context);
}

/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}

/**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}

ContextLoaderListener的作用就是启动web容器时自动装配ApplicationContext的配置信息。更细化一点讲,Spring的启动过程其实就是Spring IOC容器的启动过程。

1.1 ContextLoader剖析

上文说过,Spring启动主要的逻辑在父类ContextLoader的方法initWebApplicationContext实现。所以我们有必要重点看下ContextLoader类:

这里面,最核心的方法是initWebApplicationContext(),它被ContextLoaderListene的contextInitialized()调用,负责spring容器的初始化。

注意,该图代码非5.0.x版本,但核心逻辑变化不大,可以参考。

1.2 Context的生成门面

在这个方法中,入参ServletContext是由web容器监听器(ContextLoaderListener)提供。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* Initialize Spring's web application context for the given servlet context,
* using the application context provided at construction time, or creating a new one
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
*
* 使用构造时提供的应用程序上下文,或者根据“ {@link #CONTEXT_CLASS_PARAM contextClass}”
* 和“ {@link #CONFIG_LOCATION_PARAM contextConfigLocation}”上下文参数,
* 为给定的servlet上下文初始化Spring的Web应用程序上下文。
*
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #ContextLoader(WebApplicationContext)
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//判断给定的ServletContext是否已经存在WebApplicationContext,如果存在则抛出异常。
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!");
}

Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();

try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
//判断ContextLoader的context属性是否为空,为空表示WebApplicationContext还不存在。
if (this.context == null) {
//不存在则创建WebApplicationContext,该方法也是核心方法,我们后面详解。
this.context = createWebApplicationContext(servletContext);
}
//WebApplicationContext默认实现是XmlWebApplicationContext,
//XmlWebApplicationContext是ConfigurableWebApplicationContext子类
//所以该判断基本上都是正确的。
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {//这个context还未执行refresh方法。
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
//通过loadParentContext()方法为其设置父上下文。
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
//通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}

//将ApplicationContext放入ServletContext中,其key为
//WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {//如果当前线程的类加载器和ContextLoader类加载器一致
//那么创建的ApplicationContext赋值给ContextLoader.currentContext
//即在ContextLoader类被加载的进程中,如果创建了context的话,context赋值currentContext,方便取用
//而不是放在全局静态常量Map存起来。
currentContext = this.context;
}
else if (ccl != null) {//否则
//将ApplicationContext放入ContextLoader的全局静态常量Map中
//其中key为:Thread.currentThread().getContextClassLoader()即当前线程类加载器
//正常的context创建流程,走这里。
currentContextPerThread.put(ccl, this.context);
}

if (logger.isDebugEnabled()) {
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
}

return this.context;
}
catch (RuntimeException ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
catch (Error err) {
logger.error("Context initialization failed", err);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
throw err;
}
}

归纳一下:

  • 首先判断servlectContext中是否已经存在根上下文,如果存在,则抛出异常;
  • 否则通过createWebApplicationContext方法创建新的根上下文。
  • 然后通过loadParentContext()方法为其设置父上下文。
  • 再通过configureAndRefreshWebApplicationContext为根上下文构建bean工厂和bean对象。
  • 最后把上下文存入servletContext,并且存入currentContextPerThread。
  • 至此初始化过程完毕,接下来可以获取WebApplicationContext,进而用getBean(“bean name”)得到bean。

1.3 创建Context

我们刚刚看到,initWebApplicationContext方法主要调用createWebApplicationContext方法来创建上下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Instantiate the root WebApplicationContext for this loader, either the
* default context class or a custom context class if specified.
* <p>This implementation expects custom contexts to implement the
* {@link ConfigurableWebApplicationContext} interface.
* Can be overridden in subclasses.
*
* 实例化此加载程序的根WebApplicationContext(默认上下文类或自定义上下文类(如果已指定))。
* <p>此实现期望自定义上下文实现{@link ConfigurableWebApplicationContext}接口。 可以在子类中覆盖。
*
* <p>In addition, {@link #customizeContext} gets called prior to refreshing the
* context, allowing subclasses to perform custom modifications to the context.
* @param sc current servlet context
* @return the root WebApplicationContext
* @see ConfigurableWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//从web.xml配置的contextClass参数中获取上下文的实现类,如果contextClass为空,则使用默认的。
//下文有说明
Class<?> contextClass = determineContextClass(sc);
//根上下文的实现类必须是ConfigurableWebApplicationContext的子类,否则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
//BeanUtils.instantiateClass工具方法,通过反射得到contextClass的构造方法,根据类名创建类
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

归纳一下:

  • createWebApplicationContext方法使用determineContextClass方法,从web.xml配置的contextClass参数中,获取要创建的context的具体实现类,如果没有指定,则默认使用XmlWebApplicationContext为其实现类。
  • 限制根上下文的实现类必须是ConfigurableWebApplicationContext的子类。
  • 使用BeanUtils.instantiateClass工具方法,根据类名创建类实例。

1.4 确定Context实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Return the WebApplicationContext implementation class to use, either the
* default XmlWebApplicationContext or a custom context class if specified.
*
* 返回要使用的WebApplicationContext实现类,
* 如果指定,则为自定义上下文类。否则默认为XmlWebApplicationContext
*
* @param servletContext current servlet context
* @return the WebApplicationContext implementation class to use
* @see #CONTEXT_CLASS_PARAM
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
//从web.xml获得参数contextClass,在一般的web项目中,此参数为null,即没有指定。
//(spring源生的XmlWebApplicationContext就挺好,没必要重复造轮子)
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
try {
//如果指定了,返回类名
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
//获得根上下文WebApplicationContext的默认实现类的类名,defaultStrategies是Properties类型,
//在CotnextLoader类开头static语句块中初始化
//获取当前包下面的ContextLoader.properties文件,文件内容是:
//org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}

String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);方法获取的,就是web.xml中配置指定的IoC容器类型:

1
2
3
4
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>

截止到这里为止,WebApplicationContext的实例就已经创建出来了。然后逻辑往下走,到了configureAndRefreshWebApplicationContext(cwac, servletContext)方法,该方法为根上下文构建bean工厂和bean对象。

1.5 配置和刷新Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
//如果web.xml中配置了context的id,那么读取配置的id并使用。
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {//如果没有配置context的id,则使用默认的id。
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//把ServletContext塞入WebApplicationContext
wac.setServletContext(sc);
//读取web.xml中的<param-name>contextConfigLocation</param-name>的value值
//获取上下文的配置xml地址和文件名。
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}

// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}

//customizeContext方法的功能是:
//对从web.xml的<context-param>中读取globalInitializerClasses和contextInitializerClasses参数
//获取配置的ApplicationContextInitializer实现类,然后对他们的实现类依次回调他们的initialize方法。

//ApplicationContextInitializer是Spring框架原有的接口,这个接口的主要作用就是
//在ConfigurableApplicationContext类型(或者子类型)的ApplicationContext做refresh之前
//允许我们对ConfiurableApplicationContext的实例做进一步的设置和处理。例如,根据上下文环境注册属性源或激活概要文件。
//ApplicationContextInitializer支持Order注解,表示执行顺序,越小越早执行;
customizeContext(sc, wac);

//核心代码,开始容器的初始化
wac.refresh();
}

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);方法获取的,就是web.xml中配置指定的IoC容器配置文件:

1
2
3
4
5
6
<!-- 配置Spring配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xmlclasspath*:applicationContext-shiro.xml
</param-value>
</context-param>

我们可以看到,容器的初始化逻辑,都在wac.refresh();中。

1.6 阶段性流程图

2 refresh()核心方法

我们之前说过,WebApplicationContext方法的默认实现是XmlWebApplicationContext,而XmlWebApplicationContext的refresh()方法,继承自它的父类AbstractApplicationContext。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//刷新预处理,和主流程关系不大,就是保存了容器的启动时间,启动标志等,同时给容器设置同步标识
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
//该方法调用链路,会启动Bean定义资源文件的载入方法loadBeanDefinitions方法
//值得下文讨论。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
//为BeanFactory配置容器特性,例如类加载器、事件处理器等
//还是一些准备工作,添加了两个后置处理器:ApplicationContextAwareProcessor,ApplicationListenerDetector
//还设置了 忽略自动装配 和 允许自动装配 的接口,如果不存在某个bean的时候,spring就自动注册singleton bean
//还设置了bean表达式解析器 等
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
//这是一个空方法,给子类继承用,执行自定义的BeanPost事件
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
//调用所有注册的BeanFactoryPostProcessor的Bean
//执行自定义的BeanFactoryProcessor和内置的BeanFactoryProcessor
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
//初始化信息源,和国际化相关.
initMessageSource();

// Initialize event multicaster for this context.
//初始化容器事件传播器.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
// 空方法,给子类做定制
//调用子类的某些特殊Bean初始化方法
onRefresh();

// Check for listener beans and register them.
//为事件传播器注册事件监听器.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
//初始化所有剩余的,非懒加载的单例Bean
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
//销毁已创建的Bean
destroyBeans();

// Reset 'active' flag.
//取消refresh操作,重置容器的同步标识。
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
//重设公共缓存
resetCommonCaches();
}
}
}

3 加载xml文件的beanDefinition

refresh()方法调用了obtainFreshBeanFactory()方法,在这个方法中,会发起IoC容器对配置文件的读取,并将其加载为beanDefinition,不过在了解beanDefinition加载过程之前,我们有需要了解一些前置知识点。

3.1 Resource资源文件框架

详见文章本博客《Spring Resource资源文件体系》;

3.2 加载前的准备工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Tell the subclass to refresh the internal bean factory.
* 让子类refresh内部bean工厂。
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//核心方法在这,拆开看看
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

refreshBeanFactory()方法的实现在AbstractRefreshableApplicationContext类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
* 此实现对该上下文的基础bean工厂执行实际的刷新,
* 关闭前一个bean工厂(如果有),并为上下文生命周期的下一阶段初始化一个新的bean工厂。
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//关闭前一个bean工厂(如果有)
destroyBeans();
closeBeanFactory();
}
try {
//初始化一个新的bean工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载BeanDefinitions,核心方法,加载配置信息
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

3.3 设置和获取Reader工具类

前文说过,XmlWebApplicationContext类是默认的Context类,所以默认情况下,loadBeanDefinitions(DefaultListableBeanFactory)调用的是XmlWebApplicationContext类中实现的loadBeanDefinitions(DefaultListableBeanFactory);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//初始化XmlBeanDefinitionReader,Reader是将配置文件转为beanDefinition文件的主要工具类。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
//注意这里,将this,也就是XmlWebApplicationContext本身,作为XmlBeanDefinitionReader的ResourceLoader。
//XmlWebApplicationContext是实现了ResourceLoader接口的,并且它是ResourcePatternResolver接口的子类。
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//空方法,给自定义的context类一个变更Reader类的回调机会
initBeanDefinitionReader(beanDefinitionReader);
//核心方法,继续往下看
loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions(beanDefinitionReader)方法调用的是loadBeanDefinitions(XmlBeanDefinitionReader)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
* therefore this method is just supposed to load and/or register bean definitions.
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
* into Resource instances.

* 使用给定的XmlBeanDefinitionReader加载Bean定义。beanFactory的生命周期由refreshBeanFactory方法处理;
* 因此该方法仅应加载 或 注册Bean定义。委托ResourcePatternResolver将位置模式解析为Resource实例。

* @throws IOException if the required XML document isn't found
* @see #refreshBeanFactory
* @see #getConfigLocations
* @see #getResources
* @see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//获取web.xml配置的configLocations,即配置文件的路径
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
//遍历得到的configLocation路径,依次调用reader的loadBeanDefinitions方法解析每个路径
reader.loadBeanDefinitions(configLocation);
}
}
}

3.4 通配/完整路径解析策略

核心逻辑又进入了reader.loadBeanDefinitions(configLocation)方法,这个方法,XmlBeanDefinitionReader自己没有实现,是继承自AbstractBeanDefinitionReader的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process. May be {@code null}
* to indicate that the caller is not interested in those Resource objects.
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #getResourceLoader()
* @see #loadBeanDefinitions(org.springframework.core.io.Resource)
* @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
*/
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)
throws BeanDefinitionStoreException {
//获取ResourceLoader,ResourceLoader是用来确定location用哪种resource策略的逻辑封装。
//当前类XmlBeanDefinitionReader,在XmlWebApplicationContext类的loadBeanDefinitions(DefaultListableBeanFactory)方法中
//就已经指定了XmlBeanDefinitionReader的ResourceLoader。
//XmlBeanDefinitionReader的ResourceLoader就是XmlWebApplicationContext
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]:
no ResourceLoader available");
}
//如果该reader注入的ResourceLoader是ResourcePatternResolver的子类。
//也就是支持解析配置路径是通配符形式的location。
//在web应用场景中,ResourceLoader已经指定了是XmlWebApplicationContext
//而XmlWebApplicationContext是ResourcePatternResolver的子类,所以判断必然为真
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//那么,一个带通配符的路径,是可能返回多个Resource的
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 {
// Can only load single resources by absolute URL.
//否则,则l该reader注入的ResourceLoader只支持解析配置路径是完整路径形式
//那么一个绝对路径,只会取回来一个resource
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;
}
}

这里的两个分支,即resourceLoader支持解析location为通配符形式的resourceLoader支持解析location为完整路径形式的,二者分别调用了:

  1. resourceLoader支持解析location为通配符形式的
    • PathMatchingResourcePatternResolver#getResources(String locationPattern)
    • AbstractBeanDefinitionReader#loadBeanDefinitions(Resource… resources)方法
  2. resourceLoader支持解析location为完整路径形式的
    • DefaultResourceLoader#getResources(String locationPattern)
    • XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)

而AbstractBeanDefinitionReader#loadBeanDefinitions(Resource… resources)方法的逻辑很简单:

1
2
3
4
5
6
7
8
9
10
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
//遍历Resource,依次调用loadBeanDefinitions(Resource resource)
counter += loadBeanDefinitions(resource);
}
return counter;
}

所以最后殊途同归,最后都是落在了XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)方法上。

3.5 编码设置和循环依赖检测

XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)重写方法,主要就是加载xml文件配置的beanDefinition:

1
2
3
4
5
6
7
8
9
10
11
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//对传进来的Resource又进行了一次封装,变成了编码后的Resource
return loadBeanDefinitions(new EncodedResource(resource));
}

可以看到,resource在这里被封装为了EncodedResource,我们继续往下看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource);
}
//resourcesCurrentlyBeingLoaded是一个ThreadLocal,里面存放Resource包装类EncodedResource的set集合
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {//如果set不存在,new一个
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//如果set中已有这个元素则返回false,进入该条件抛出异常
if (!currentResources.add(encodedResource)) {
//只有当前线程重复加载了某个资源,这里才会抛出异常
//用来检测是否循环加载某个Resource,如果是,提醒需要检查导入的definitions
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//获取封装的InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
//没有设置编码集,跳过
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

我们看到,核心逻辑,又进入了doLoadBeanDefinitions方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* Actually load bean definitions from the specified XML file.
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//根据不同的xml约束(dtd,xsd等),将xml文件生成对应的文档对象
//这个方法里面涉及xml的解析,不赘述,简单来说:
//检测解析传入的xml文件(也就是resource)时该用哪种验证方式
//如果这个文件有DOCTYPE声明,那么就用DTD验证,否则就使用XSD验证模式。
//使用标准JAXP配置XML解析器,加载InputSource的Document对象,然后返回一个新的DOM对象
//注意,这个Document对象,是W3C定义的标准XML对象,跟spring无关。
Document doc = doLoadDocument(inputSource, resource);
//核心方法,beanDefinitions的注册
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}

OK,两个核心方法,doLoadDocument和registerBeanDefinitions方法,前者负责解析xml文件,后者负责注册BeanDefinitions,我们分别来分析。先看doLoadDocument()方法

3.6 解析XML文件

此方法在XmlBeanDefinitionReader类中

1
2
3
4
5
6
7
8
9
10
11
/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from --从中读取的SAX输入源
* @param resource the resource descriptor for the XML file --xml文件的资源描述符
* @return the DOM Document DOM文档对象
*
* 使用配置好的DocumentLoader文档加载器加载指定的文档
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());

3.6.1 参数1

上文中的getEntityResolver() 方法返回 XmlBeanDefinitionReader 类的 entityResolver 属性。

entityResolver 属性在 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 方法中被赋值。

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

和resourceLoader一样,this拿的是XmlWebApplicationContext实例,我们再来看下ResourceEntityResolver的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Create a ResourceEntityResolver for the specified ResourceLoader
* (usually, an ApplicationContext).
* @param resourceLoader the ResourceLoader (or ApplicationContext)
* to load XML entity includes with
*
* 为指定的ResourceLoade(通常是应用上下文)r创建一个ResourceEntityResolver
*/
public ResourceEntityResolver(ResourceLoader resourceLoader) {
super(resourceLoader.getClassLoader());
//此处解析器拿到了上下文的引用
this.resourceLoader = resourceLoader;
}

调用了父类构造,再跟进一层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Create a new DelegatingEntityResolver that delegates to
* a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}.
* <p>Configures the {@link PluggableSchemaResolver} with the supplied
* {@link ClassLoader}.
* @param classLoader the ClassLoader to use for loading
* (can be {@code null}) to use the default ClassLoader)
*/
public DelegatingEntityResolver(ClassLoader classLoader) {
//这两个解析器和约束的类型有关,DTD
this.dtdResolver = new BeansDtdResolver();
//可插拔的Schema解析器,拿的上下文的类加载器
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}

中间的this.errorHandler参数可忽略。

3.6.2 参数2

ok,然后是getValidationModeForResource(resource)入参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Gets the validation mode for the specified {@link Resource}. If no explicit
* validation mode has been configured then the validation mode is
* {@link #detectValidationMode detected}.
* <p>Override this method if you would like full control over the validation
* mode, even when something other than {@link #VALIDATION_AUTO} was set.
*
* 通过给定Resource给出验证模式。如果没有明确配置验证模式,那么调用detectValidationMode方法去检测。
*/
protected int getValidationModeForResource(Resource resource) {
//默认自动验证,为1
int validationModeToUse = getValidationMode();
//如果有给出具体验证方式,则返回结果
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//检测验证模式,进入这个方法
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
// 如果实在不能判断验证模式是那种就使用XSD方式,
// 因为检测完后还是没有发现DTD模式的声明(在查找document的根标签之前)。
// 值为3
return VALIDATION_XSD;
}

getValidationModeForResource的核心方法是detectValidationMode(),我们继续:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Detects which kind of validation to perform on the XML file identified
* by the supplied {@link Resource}. If the file has a {@code DOCTYPE}
* definition then DTD validation is used otherwise XSD validation is assumed.
* <p>Override this method if you would like to customize resolution
* of the {@link #VALIDATION_AUTO} mode.
*
* 检测执行xml文件时该用哪种验证方式,这个xml由Resource对象提供
* 如果这个文件有DOCTYPE声明,那么就用DTD验证,否则就默认使用XSD。
* 如果你想要自定义自动验证模式的解决方式,你可以覆盖这个方法
*/
protected int detectValidationMode(Resource resource) {
//默认false
if (resource.isOpen()) {
throw new BeanDefinitionStoreException(
"Passed-in Resource [" + resource + "] contains an open stream: " +
"cannot determine validation mode automatically. Either pass in a Resource " +
"that is able to create fresh streams, or explicitly specify the validationMode " +
"on your XmlBeanDefinitionReader instance.");
}
InputStream inputStream;
try {
inputStream = resource.getInputStream();
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
"Did you attempt to load directly from a SAX InputSource without specifying the " +
"validationMode on your XmlBeanDefinitionReader instance?", ex);
}

try {
//XmlBeanDefinitionReader的validationModeDetector属性有默认实现XmlValidationModeDetector
//核心方法,接下来进入这个方法看下
return this.validationModeDetector.detectValidationMode(inputStream);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
resource + "]: an error occurred whilst reading from the InputStream.", ex);
}
}

所以来看validationModeDetector调用的detectValidationMode方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* Detect the validation mode for the XML document in the supplied {@link InputStream}.
* Note that the supplied {@link InputStream} is closed by this method before returning.
*
* 在提供的InputStream中检测XML文档的验证模式
* 注意,提供的InputStream在这个方法return之前会被关闭
*/
public int detectValidationMode(InputStream inputStream) throws IOException {
// Peek into the file to look for DOCTYPE.
// 查找文件的DOCTYPE
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
boolean isDtdValidated = false;
String content;
while ((content = reader.readLine()) != null) {
//读一行字符串就干掉字符串里面的注释,如果全是注释全干掉
//主要为了剥离注释,因为非注释内容要么是DOCTYPE声明要么是文档的根元素对象
content = consumeCommentTokens(content);
//剥离注释后完全没内容就继续循环
if (this.inComment || !StringUtils.hasText(content)) {
continue;
}
//有DOCTYPE声明,就跳出去
if (hasDoctype(content)) {
isDtdValidated = true;
break;
}
//注释不能进去。开头是"<",后面第一个字符是字母,就进入。
//比如'<beans xmlns="http://www.springframework.org/schema/beans"'
//进去后跳出循环
if (hasOpeningTag(content)) {
// End of meaningful data...
break;
}
}
//当遍历到名称空间了也就是"<beans xmlns=...>"还没有DOCTYPE声明,
//那么就判定他为XSD验证
return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
}
catch (CharConversionException ex) {
// Choked on some character encoding...
// Leave the decision up to the caller.
return VALIDATION_AUTO;
}
finally {
//关流
reader.close();
}
}

3.6.3 解析的核心逻辑

讲完核心的两个入参后进入正主,loadDocument方法。

1
2
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());

documentLoader属性的默认实现是DefaultDocumentLoader;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*
* 使用标准JAXP配置XML解析器加载InputSource的Document对象
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

//创建文档构建器工厂对象,并初始化一些属性
//如果验证模式为XSD,那么强制支持XML名称空间,并加上schema属性
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}

//创建一个JAXP文档构建器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);

//按照XML文档解析给定inputSource的内容,然后返回一个新的DOM对象
return builder.parse(inputSource);
}

最后一步,DocumentBuilder的默认实现是DocumentBuilderImpl,这个是jdk里面的xml解析器了,不再赘述。

至此,我们拿到了Document对象

3.7 准备注册BeanDefinitions

两个核心方法,看完了doLoadDocument方法,我们再来看registerBeanDefinitions方法,后者负责注册BeanDefinitions,我们分别来分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* 注册包含在给定DOM文档中的bean定义。由{@code loadBeanDefinitions}调用。<p>创建解析器类的新实例,并在其上调用{@code registerBeanDefinitions}。
*
* 注册包含在给定DOM文档对象中的 bean definition
* 被loadBeanDefinitions方法所调用
* 解析class后创建一个新的实例,并调用registerBeanDefinitions方法
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//其实就是new了一个DefaultBeanDefinitionDocumentReader工具类。
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//getRegistry()方法拿的是bean工厂对象,beanDefinition注册在工厂中
//这个方法就是返回已经被注册在工厂中的beanDefinitions数量
int countBefore = getRegistry().getBeanDefinitionCount();
//核心方法
//createReaderContext创建了XmlReaderContext对象
//XmlReaderContext对象是BeanDefinition读取过程中传递的上下文,封装相关的的配置和状态
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回上文这个核心方法真正注册在工厂中的beanDefinition数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}

核心方法documentReader.registerBeanDefinitions实现在DefaultBeanDefinitionDocumentReader类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 这个方法在刚创建的DefaultBeanDefinitionDocumentReader中
*
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*
* 根据“spring-beans"的XSD(或者DTD)去解析bean definition
* 打开一个DOM文档,然后初始化在<beans/>层级上指定的默认设置,然后解析包含在其中的bean definitions
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//入参时创建的XmlReaderContext对象
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//拿到了xml文档对象的根元素
Element root = doc.getDocumentElement();
//进入这个方法进行查看
doRegisterBeanDefinitions(root);
}

再看DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* Register each bean definition within the given root {@code <beans/>} element.
*
* 从<beans />配置中注册每一个bean,如果有嵌套的beans,那么递归执行这个方法。
*/
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.

// delegate可以理解为配置参数/属性,传递的上下文,封装相关的的配置和状态等信息的集合对象
// 任何被嵌套的<beans>元素都会导致此方法的递归。为了正确的传播和保存delegate内的信息,所以这里需要做delegate交接
// 它可能为null
// 为了能够回退,新的(子)delegate具有父的引用,最终会重置this.delegate回到它的初始(父)引用。
BeanDefinitionParserDelegate parent = this.delegate;

//重要方法,创建一个新的代理,继承父delegate,并初始化一些默认值
this.delegate = createDelegate(getReaderContext(), root, parent);

//默认明明空间是"http://www.springframework.org/schema/beans"
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);

//如果当前层级的<beans />配置有profile属性,那么处理这个属性。不是很重要不赘述
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}

//xml预处理,子类没有重写里面就是空实现
preProcessXml(root);

//重要方法,生成BeanDefinition,并注册在工厂中
parseBeanDefinitions(root, this.delegate);

//xml后处理,子类没有重写里面就是空实现
postProcessXml(root);

this.delegate = parent;
}

好,这里两个核心方法,我们一个一个来

3.8 生成代理和传递基础配置

先看DefaultBeanDefinitionDocumentReader类的createDelegate方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
//生成用来解析XML bean definition的有状态Delegate类,用来被主解析器和其他扩展使用。
//上下文readerContext继承在其内部
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//给新生成的delegate做继承和赋初值
delegate.initDefaults(root, parentDelegate);
return delegate;
}


/**
* Initialize the default lazy-init, autowire, dependency check settings,
* init-method, destroy-method and merge settings. Support nested 'beans'
* element use cases by falling back to the given parent in case the
* defaults are not explicitly set locally.
*
* 用默认的值填充DocumentDefaultsDefinition实例
* 通过使用parentDefaults(父代理的default属性),来解决嵌套的'beans'元素情况,以防默认值在局部设定不明确
*/
public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {

//重要方法,构造当前delegate的默认配置信息封装类,为其赋初值。
//this.defaults,也就是当前delegate的默认配置信息封装类,它的类型是DocumentDefaultsDefinition
//如果有父delegate,也要让新生成的defaults继承父delegate的defaults
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);

//默认没做任何实现
this.readerContext.fireDefaultsRegistered(this.defaults);
}


/**
* Populate the given DocumentDefaultsDefinition instance with the default lazy-init,
* autowire, dependency check settings, init-method, destroy-method and merge settings.
* Support nested 'beans' element use cases by falling back to {@code parentDefaults}
* in case the defaults are not explicitly set locally.
* 用默认的值填充DocumentDefaultsDefinition实例
* 通过使用parentDefaults(父代理的default属性),来解决嵌套的'beans'元素情况,以防默认值在局部设定不明确
* @param defaults the defaults to populate
* @param parentDefaults the parent BeanDefinitionParserDelegate (if any) defaults to fall back to
* @param root the root element of the current bean definition document (or nested beans element)
*/
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
//根元素上如果没有设定值,则返回"default"字符串
String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
//如果为"default",先看parentDefaults有没有,有用它的,没有用"false"
if (isDefaultValue(lazyInit)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
// 可能从外部<beans>继承,否则返回false
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
}
defaults.setLazyInit(lazyInit);

//下面的逻辑和lazyInit差不多,即看本级是否有显性配置,没有的话,看父类有没有继承,也没有就设默认值。

String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
if (isDefaultValue(merge)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
}
defaults.setMerge(merge);

String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
if (isDefaultValue(autowire)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
}
defaults.setAutowire(autowire);

if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
}

if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setInitMethod(parentDefaults.getInitMethod());
}

if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
}
else if (parentDefaults != null) {
defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
}
//extractSource方法这里没有做任何实现,默认返回null
defaults.setSource(this.readerContext.extractSource(root));
}

3.9 拆解根层级,并根据子标签命名空间做不同处理

我们再来看另一个核心方法:DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* Parse the elements at the root level in the document:
* 将文档中<beans/>层级下的元素一层层剥开,为子标签的不同命名空间选择不同策略。
* 主要两种命名空间:beans命名空间 +其他命名空间(context/aop等命名空间)

* "import", "alias", "bean"等标签都属于beans命名空间
*
* 这里判断是否是不同的命名空间,不同命名空间,后续会使用不同解析器来解析。
*
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//判断默认命名空间是"http://www.springframework.org/schema/beans"
if (delegate.isDefaultNamespace(root)) {
//获取根元素(<beans />)下的子Node,注意,Node不一定是子标签,可能是回车,可能是注释
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
//拿到了<beans>下的子标签
Element ele = (Element) node;
//如果该标签属于beans的名称空间,则进入这个方法
//xmlns="http://www.springframework.org/schema/beans"
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
//如果该标签属于其他的名称空间比如:context,aop等
//xmlns:aop="http://www.springframework.org/schema/aop"
//xmlns:context="http://www.springframework.org/schema/context"
//比如我们使用注解注入的话,会在spring的配置文件加<context:annotation-config/>
//这个启用注解的标签,就属于context命名空间
//在接下来的逻辑中,不同命名空间,使用不同处理器来解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

3.10 解析beans命名空间的配置

前文我们说到,如果该标签属于beans的名称空间,则进入parseDefaultElement(ele, delegate)方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//<import>标签进入这个方法
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}//<alias>标签进入这个方法
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}//<bean>标签进入这个方法
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//核心方法
processBeanDefinition(ele, delegate);
}//又嵌套一层<beans>标签进入这个方法
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
// 如果是嵌套的beans,那么就会重新调用doRegisterBeanDefinitions进行递归
doRegisterBeanDefinitions(ele);
}
}

上述几个标签中,我们主要来看<bean>标签的解析方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* 处理bean元素,解析成bean definition并注册到工厂中
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//使用delegate的内容(默认配置)来解析bean元素,核心方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//如果有要求的话渲染beanDefinition
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//注册最终被渲染的实例到工厂中
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 发送注册事件
// 这里是空实现
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

3.11 解析并生成BeanDefinition实例

进入BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*
* 解析bean元素。如果解析过程中发生错误则返回空
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}


public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//拿bean标签上的id
String id = ele.getAttribute(ID_ATTRIBUTE);
//拿bean标签上的name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
//有name属性进入
if (StringUtils.hasLength(nameAttr)) {
//name属性对应的name值如果有分隔符",; ",那么切分成数组
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
//这些name值就是别名
aliases.addAll(Arrays.asList(nameArr));
}
//指定了id就用id值作为bean名称
String beanName = id;
//如果没有id,但是指定了name,就用name值作为bean名称
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
//拿第一个name值作为bean名称,其余的还是别名
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}

if (containingBean == null) {
//检查bean名称和别名是否已经被使用了,如果用了就报错
//同时把这个bean的名称和别名加入代理的usedNames属性中
//private final Set<String> usedNames = new HashSet<String>();
checkNameUniqueness(beanName, aliases, ele);
}
//直接进入这个方法
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
//既没有指定id,也没有指定name就走这里面
if (!StringUtils.hasText(beanName)) {
try {
//前面containingBean传递时为null,这里不走这个方法
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//生成一个bean名称,beanName
//如果这个bean是内部bean,全限定名后加#号再加哈希值
//如果是顶层bean,那么后面加#号再从0开始加数字,id已被注册数字就增1,直到唯一
//比如:tk.mybatis.spring.mapper.MapperScannerConfigurer#0
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
//如果可能的话,如果生成器返回类名加后缀,则注册一个别名,这个别名就是该类的类名。
//这是为了向后兼容
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
//如果该类名没有被使用,那么注册该类名作为别名,比如:
//tk.mybatis.spring.mapper.MapperScannerConfigurer作为
//tk.mybatis.spring.mapper.MapperScannerConfigurer#0的别名
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
//返回beanDefinition的持有者
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}

在上文中,核心方法是这句话:

AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* 不关注名称和别名,只解析bean definition自身
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//栈结构,解析的时候放入bean标签,解析完成弹出
//如果还嵌套有子标签,则后续放入子标签
//栈结构当然后进先出,所以子标签先弹出
this.parseState.push(new BeanEntry(beanName));

String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
//如果有指定class属性,则拿到class属性值
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
//如果有指定parent属性,则拿到parent属性值
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}

try {
//创建BeanDefinition并设置两属性,核心方法
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//将bean标签上的属性设置到bean definition中,核心方法2
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//如果bean标签下有子标签为description,拿到标签中的文本,设置到bean definition中
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//如果bean标签下有子标签为meta,拿到他的key和value属性,设置到bean definition中
parseMetaElements(ele, bd);
//如果bean标签下有子标签为lookup-method,拿到他的name和bean属性,设置到bean definition中
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//如果bean标签下有子标签为replaced-method,设置bean definition
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//如果bean标签下有子标签为constructor-arg,设置bean definition的构造方式
parseConstructorArgElements(ele, bd);
//这个标签比较常用,为Property标签
//解析Property的属性设置到bean definition中
parsePropertyElements(ele, bd);
//有qualifier子标签才走这个方法
parseQualifierElements(ele, bd);
//设置资源
bd.setResource(this.readerContext.getResource());
//这里为null
bd.setSource(extractSource(ele));

return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
//解析的时候放入,解析完成弹出
this.parseState.pop();
}
return null;
}

两个重要的核心方法

1
2
3
4
5
6
7
8
//创建BeanDefinition并设置两属性,核心方法
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//将bean标签上的属性设置到bean definition中,核心方法2
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
...
//这个标签比较常用,为Property标签
//解析Property的属性设置到bean definition中
parsePropertyElements(ele, bd);

我们依次来看:

3.11.1 创建实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Create a bean definition for the given class name and parent name.
* 通过给定的className和parentName创建beanDefinition
*
* @param className the name of the bean class
* @param parentName the name of the bean's parent bean
* @return the newly created bean definition
* @throws ClassNotFoundException if bean class resolution was attempted but failed
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {

return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}

调用BeanDefinitionReaderUtils的静态方法createBeanDefinition():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Create a new GenericBeanDefinition for the given parent name and class name,
* eagerly loading the bean class if a ClassLoader has been specified.

* 通过给定的parentName和className穿件一个新的GenericBeanDefinition
* 如果指定了ClassLoader,就提前加载bean class

* @param parentName the name of the parent bean, if any
* @param className the name of the bean class, if any
* @param classLoader the ClassLoader to use for loading bean classes
* (can be {@code null} to just register bean classes by name)
* @return the bean definition
* @throws ClassNotFoundException if the bean class could not be loaded
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader)
throws ClassNotFoundException {

GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
//有classloader,则通过反射,动态加载一个实例返回
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
//没有classloader,先存个className
bd.setBeanClassName(className);
}
}
return bd;
}

3.11.2 标签的属性注入

看完了create,我们再来看parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);方法,该方法会将bean标签的属性值注入到BeanDefinition实例中,也就是给BeanDefinition赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/**
* Apply the attributes of the given bean element to the given bean * definition.
* 将bean标签上的属性设置到bean definition中
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//bean标签上已经没有singleton属性了,新版用scope代替,所以出现singleton就报错提醒
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
//如果设置了scope就拿其值
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
//此处containingBean为空
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
// 如果bd是一个内部的beanDefinition,用包含它的bean的配置
bd.setScope(containingBean.getScope());
}
//如果设置了abstract就拿其值
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//lazyInit如果没有设置则为默认值,默认值用的代理类中defaults属性,
//也就是this.defaults
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//拿autowire配置,无则用默认值,默认值用的代理类中defaults属性,即不进行autowire
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//拿depends-on配置
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
//是否有autowire-candidate属性,没有或者为默认则不设置
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
//是否有primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//是否有init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}//没有init-method属性,就拿代理类defaults属性的
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
//是否有destroy-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}//没有destroy-method属性,就拿代理类defaults属性的
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
//是否有factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
//是否有factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}

return bd;
}

这样,bean标签上的属性也就解析完成了,对其属性的描述不管设置了还是没有设置的,都有相应的值对应到bean definition中。

3.11.3 bean标签下的property注入

1
2
3
4
<bean id="student" class="com.sgcc.bean.Student">
<property name="name" value="无敌"/>
<property name="age" value="20"/>
</bean>

如上述配置,我们知道property的存在,parsePropertyElements()负责解析Property的属性设置到beanDefinition中,此方法在BeanDefinitionParserDelegate类中实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/**
* Parse property sub-elements of the given bean element.
* 解析bean标签下property子标签
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
//循环查找元素的子元素,也就是bean标签的子元素
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//标签名为property才能进入,进入这个方法
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}

/**
* Parse a property element.
* 解析property元素,核心的属性只有name和value
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//拿到property标签的name属性
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
//解析的时候放入,解析完成弹出,这里放入property标签,
//注意,此时这里还存有bean父标签,子标签解析完成后先弹出
this.parseState.push(new PropertyEntry(propertyName));
try {
//bean标签下可以有多个property,但是不能重复name属性,有重复报错
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//核心方法,解析property标签属性
Object val = parsePropertyValue(ele, bd, propertyName);
//将name属性和对应的value封装为PropertyValue对象
PropertyValue pv = new PropertyValue(propertyName, val);
//解析property标签的子标签meta,
//拿到meta的key和value属性,设置到PropertyValue中
parseMetaElements(ele, pv);
//这里没有实现,为null
pv.setSource(extractSource(ele));
//将PropertyValue添加到bean definition中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
//解析的时候放入,解析完成弹出,这里弹出的是property标签
this.parseState.pop();
}
}



/**
* Get the value of a property element. May be a list etc.
* Also used for constructor arguments, "propertyName" being null in this case.
* 拿到property标签的value值,可能是list
* 也可能被constructor标签使用,这种情况propertyName为null
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
//如果propertyName为null,则ele是constructor-arg标签
//否则为property标签
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");

// Should only have one child element: ref, value, list, etc.
//不管是哪种标签,下面都应该只有一个子标签: ref, value, list等.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
//除开description和meta标签,子标签最多只能有一个
//如果该判断为真,说明不止一个除开description和meta的标签
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
//得到除开description和meta之外的子标签
//比如下面这种配置,那么<value>helloworld</value>就是子元素subElement。
//<property name="Nnnn">
// <value>helloworld</value>
//</property>
subElement = (Element) node;
}
}
}
//看标签属性用的是value还是ref
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//value和ref属性不能同时存在,如果有子标签,则value和ref都不能存在,否则报错
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//用的ref的情况,如<property name="name" ref="......"/>
if (hasRefAttribute) {
//拿到ref属性
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//通过ref属性来构建一个RuntimeBeanReference实例对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
//用的value的情况,如<property name="name" value="..."/>
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//子标签不为null的情况,比如
//<property name="Nnnn">
// <value>helloworld</value>
//</property>
else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
//没指定ref或者value或者子标签,返回null
error(elementName + " must specify a ref or value", ele);
return null;
}
}



/**
* Parse a value, ref or collection sub-element of a property or
* constructor-arg element.
* 解析property或者constructor-arg标签的子标签,可能为value, ref或者集合
* @param ele subelement of property element; we don't know which yet
* @param bd the current bean definition (if any)
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}


/**
* Parse a value, ref or collection sub-element of a property or
* constructor-arg element.
* 解析property或者constructor-arg标签的子标签,可能为value, ref或者集合
* @param ele subelement of property element; we don't know which yet
* @param bd the current bean definition (if any)
* @param defaultValueType the default type (class name) for any
* {@code <value>} tag that might be created
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {//如果这个子标签不属于beans的名称空间,则走这个方法
return parseNestedCustomElement(ele, bd);
}//如果是bean子标签,则走这个方法
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}//如果是ref子标签,则走这个方法
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}//如果是idref子标签,则走这个方法
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}//如果是value子标签,则走这个方法
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
//以这个方法作为演示,其他的方法都是大同小异,进入。
return parseValueElement(ele, defaultValueType);
}//如果是null子标签,则走这个方法
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}//如果是array子标签,则走这个方法
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}//如果是list子标签,则走这个方法
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}//如果是set子标签,则走这个方法
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}//如果是map子标签,则走这个方法
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}//如果是props子标签,则走这个方法
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}//否则返回null,报错
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}



/**
* Return a typed String value Object for the given value element.
* 通过指定的value标签,返回指定的字符串value对象
*/
public Object parseValueElement(Element ele, @Nullable String defaultTypeName) {
// It's a literal value.
//拿到value中的文本,包括回车、tab制表符、空格
String value = DomUtils.getTextValue(ele);
//有无type属性
String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE);
String typeName = specifiedTypeName;
if (!StringUtils.hasText(typeName)) {
//没有就用入参defaultTypeName,其实这里defaultTypeName也是null
typeName = defaultTypeName;
}
try {
//构建一个value的封装类。
TypedStringValue typedValue = buildTypedStringValue(value, typeName);
//这里设置为空
typedValue.setSource(extractSource(ele));
//这里为空字符串
typedValue.setSpecifiedTypeName(specifiedTypeName);
//返回typedValue
return typedValue;
}
catch (ClassNotFoundException ex) {
error("Type class [" + typeName + "] not found for <value> element", ele, ex);
return value;
}
}

3.12 注册BeanDefinition到工厂中

好,回到processBeanDefinition方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* 处理bean元素,解析成bean definition并注册到工厂中
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//使用delegate的内容(默认配置)来解析bean元素,核心方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//如果有要求的话渲染beanDefinition,不是很重要
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
//注册最终被渲染的实例到工厂中
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 发送注册事件
// 这里是空实现
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);这一句我们已经在3.11章节中详细介绍了,接下来我们视角继续往下,看下BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Register the given bean definition with the given bean factory.
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
// 注册beanDefinition的beanName
// 比如tk.mybatis.spring.mapper.MapperScannerConfigurer#0
String beanName = definitionHolder.getBeanName();
// 核心方法,比较重要,待会儿详解
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
// 如果有别名的话,为bean name注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

主流程由此进入了registry.registerBeanDefinition中,其中registry实例是DefaultListableBeanFactory的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");

if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//做一个验证,静态工厂方法和覆盖方法不能组合使用
//如果bean definition中的beanClass属性不是String类型而是Class类型
//那么就要验证和准备这个bean定义的覆盖方法,检查指定名称的方法是否存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//查看beanName是否已经被注册在工厂的beanDefinitionMap属性中
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
//已经被注册的情况走这个方法,覆盖或者抛异常
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + existingDefinition + "] bound.");
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isWarnEnabled()) {
logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isInfoEnabled()) {
logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//这场景表示beanName还没有被注册
//然后根据阶段不同又有一层判断
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//这个阶段是bean已经开始创建,启动阶段不会进入这里
//如果在非启动阶段注册beanDefinition,那么要加锁后才能操作beanDefinitionMap、beanDefinitionNames
//和manualSingletonNames
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// 仍然处于启动时的注册阶段
// 所以这里走这个方法
// beanDefinitionMap是工厂的一个属性,ConcurrentHashMap类型
//他保存所有解析好的bean Definition的名称和实例的映射
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanName也单独使用了一个ArrayList来保存,方便遍历
this.beanDefinitionNames.add(beanName);
//如果该beanDefinition是手动注册的,还要从manualSingletonNames中移除beanDefinition的beanName
//manualSingletonNames是LinkedHashSet
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}

if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}

再看别名的注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
if (alias.equals(name)) {
//移除别名中的beanName
//aliasMap是ConcurrentHashMap类型,保存别名和beanName的映射
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
//如果别名对应beanName已经被注册,则不需要再注册一次
//别名不允许被覆盖
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isInfoEnabled()) {
logger.info("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
//再检查一遍,aliasMap中不能已经存在name和alias
checkForAliasCircle(name, alias);
//工厂的aliasMap属性保存别名,那么alias已被注册
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}

3.13 加载xml文件beanDefinition流程图

4 加载注解配置的beanDefinition

0%