Spring源码分析(一):IOC容器的初始化过程

Spring的容器就是bean容器也是IOC容器,它的初始化过程非常复杂,设计到复杂的类继承关系,在此简单分析一下关键的类和步骤。

一、核心数据结构

1、Resource

在Spring内部,针对于资源文件有一个统一的接口Resource表示。其主要实现类有ClassPathResource、FileSystemResource、UrlResource、ByteArrayResource、ServletContextResource和InputStreamResource。Resource接口中主要定义有以下方法:

  • exists():用于判断对应的资源是否真的存在。
  • isReadable():用于判断对应资源的内容是否可读。需要注意的是当其结果为true的时候,其内容未必真的可读,但如果返回false,则其内容必定不可读。
  • isOpen():用于判断当前资源是否代表一个已打开的输入流,如果结果为true,则表示当前资源的输入流不可多次读取,而且在读取以后需要对它进行关闭,以防止内存泄露。该方法主要针对于InputStreamResource,实现类中只有它的返回结果为true,其他都为false。
  • getURL():返回当前资源对应的URL。如果当前资源不能解析为一个URL则会抛出异常。如ByteArrayResource就不能解析为一个URL。
  • getFile():返回当前资源对应的File。如果当前资源不能以绝对路径解析为一个File则会抛出异常。如ByteArrayResource就不能解析为一个File。
  • getInputStream():获取当前资源代表的输入流。除了InputStreamResource以外,其它Resource实现类每次调用getInputStream()方法都将返回一个全新的InputStream。

实现类:

  • ClassPathResource可用来获取类路径下的资源文件。假设我们有一个资源文件test.txt在类路径下,我们就可以通过给定对应资源文件在类路径下的路径path来获取它,new ClassPathResource(“test.txt”)。
  • FileSystemResource可用来获取文件系统里面的资源。我们可以通过对应资源文件的文件路径来构建一个FileSystemResource。FileSystemResource还可以往对应的资源文件里面写内容,当然前提是当前资源文件是可写的,这可以通过其isWritable()方法来判断。FileSystemResource对外开放了对应资源文件的输出流,可以通过getOutputStream()方法获取到。
  • UrlResource可用来代表URL对应的资源,它对URL做了一个简单的封装。通过给定一个URL地址,我们就能构建一个UrlResource。
  • ByteArrayResource是针对于字节数组封装的资源,它的构建需要一个字节数组。
  • ServletContextResource是针对于ServletContext封装的资源,用于访问ServletContext环境下的资源。ServletContextResource持有一个ServletContext的引用,其底层是通过ServletContext的getResource()方法和getResourceAsStream()方法来获取资源的。
  • InputStreamResource是针对于输入流封装的资源,它的构建需要一个输入流。

2、ResourceLoader

通过上面介绍的Resource接口的实现类,我们就可以使用它们各自的构造函数创建符合需求的Resource实例。但是在Spring中提供了ResourceLoader接口,用于实现不同的Resource加载策略,即将不同Resource实例的创建交给ResourceLoader来加载,这也是ApplicationContext等高级容器中使用的策略。

接口中有两个主要的方法:

  • getResource():在ResourceLoader接口中,主要定义了一个方法:getResource(),它通过提供的资源location参数获取Resource实例,该实例可以是ClasPathResource、FileSystemResource、UrlResource等,但是该方法返回的Resource实例并不保证该Resource一定是存在的,需要调用exists方法判断。
  • getResourceByPath:这个方法被声明为protected,所以在它的子类中基本都重写了这个方法。

2.1、实现类

DefaultResourceLoader是ResourceLoader的默认实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith("/")) {
return getResourceByPath(location);
}
//处理带有classPath标识的Resource
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
//处理URL资源
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
return getResourceByPath(location);
}
}
}

其最主要的逻辑实现在getResource方法中:

  • 该方法首先判断传入的location是否以”classpath:”开头,如果是,则创建ClassPathResource(移除”classpath:”前缀)
  • 否则尝试创建UrlResource
  • 如果当前location没有定义URL的协议(即以”file:”、”zip:”等开头,比如使用相对路径”resources/META-INF/MENIFEST.MF),则创建UrlResource会抛出MalformedURLException,此时调用getResourceByPath()方法获取Resource实例
  • getResourceByPath()方法默认返回ClassPathContextResource实例,在FileSystemResourceLoader中有不同实现。

它有一个重要的子类就是AbstractApplicationContext,因为后面要介绍的ApplicationContext的几个实现类都继承自它,也就是说,比如FileSystemXMLApplicationContext这个类因为继承自DefaultResourceLoader,所以具备了它的getResource方法,在最后定位资源的时候都调用了这个方法来获取Resource。

3、BeanDefinition

一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。

作用:持有bean数据结构,是注入的bean在IoC容器中的抽象,方法列表如下

1
2
3
4
5
6
7
8
9
10
getBeanClassName;  
getFactoryBeanName;
getScope;
isLazyInit;
isSingleton;
isPrototype;
isAbstract;
isPrimary;
isAutowireCandidate;
getDescription;

相关的类与接口:

  • AbstractBeanDefinition:是BeanDefinition的一个完整实现
  • BeanDefinitionReader:用于读取bean的信息的接口。AbstractBeanDefinitionReader实现了该接口,XmlBeanDefinitionReader继承了AbstractBeanDefinitionReader。

4、ApplicationContext

高级IoC容器,除了基本的IoC容器功能外,支持不同信息源、访问资源、支持事件发布等功能。

继承了以下接口:

  • ListableBeanFactory:继承自BeanFactory,在此基础上,添加了containsBeanDefinition、getBeanDefinitionCount、getBeanDefinitionNames等方法。
  • HierarchicalBeanFactory:继承自BeanFactory,在此基础之上,添加了getParentBeanFactory、containsLocalBean这两个方法。
  • AutoWireCapableBeanFactory:继承自BeanFactory
  • MessageSource:用于获取国际化信息
  • ApplicationEventPublisher:因为ApplicationContext实现了该接口,因此spring的ApplicationContext实例具有发布事件的功能。

二、容器的初始化过程

IOC容器的初始化过程就是含有BeanDefinition的信息的Resource的定位、载入、解析、注册的四个过程,最终配置的bean以BeanDefinition的数据与结构存在于IOC容器之中,这个过程不涉及bean的依赖注入,也不产生任何bean。

以FileSystemXmlApplicationContext为例分析一下它的初始化过程。

先看一下它的类继承关系

1、Resource的定位过程

1.1、FileSystemXmlApplicationContext 类

一般当构造的时候调用使用new来创建实例,配置文件的的地址作为参数:

1
ApplicationContext ctx1=new FileSystemXmlApplicationContext("c:/bean.xml");

先来看一下最下面的实现类FileSystemXmlApplicationContext源码

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
package org.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;


public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}

//核心构造器
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();//Ioc容器的refresh()过程,是个非常复杂的过程,但不同的容器实现这里都是相似的,因此基类中就将他们封装好了
}
}

//通过构造一个FileSystemResource对象来得到一个在文件系统中定位的BeanDefinition
//采用模板方法设计模式,具体的实现用子类来完成
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}

有三部分

  • 继承自AbstractXmlApplicationContext,而它最终的父类是DefaultResourceLoader,所以它具备了Resource定义的BeanDefinition的能力
  • 调用refresh方法,这个方法在父类AbstractApplicationContext中已经封装好了。它详细描述了整个ApplicationContext的初始化过程,比如BeanFactory的更新、MessageSource和PostProcessor的注册等。这里看起来像是对ApplicationContext进行初始化的模版或执行提纲,这个执行过程为Bean的生命周期管理提供了条件。
  • 重写了getResourceByPath方法,该方法是一个模版方法,通过构造一个FileSystemResource对象来得到一个在文件系统中定位的BeanDefinition。

它的调用关系

refresh为初始化IoC容器的入口,但是具体的资源定位还是在XmlBeanDefinitionReader读入BeanDefinition时完成,loadBeanDefinitions() 加载BeanDefinition的载入。

1.2、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
protected final void refreshBeanFactory() throws BeansException {
//判断是否已经创建了BeanFactory,如果创建了则销毁关闭该BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建DefaultListableBeanFactory实例对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//加载BeanDefinition信息
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException;

通过loadBeanDefinitions(beanFactory);加载BeanDefinition信息,BeanDefinition就是在这里定义的。

AbstractRefreshableApplicationContext对loadBeanDefinitions仅仅只是定义了一个抽象的方法,真正的实现是在子类AbstractXmlApplicationContext中的。

1.3、AbstractXmlApplicationContext

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
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//创建bean的读取器(Reader),即XmlBeanDefinitionReader,并通过回调设置到容器中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

beanDefinitionReader.setEnvironment(getEnvironment());
//为Bean读取器设置Spring资源加载器
beanDefinitionReader.setResourceLoader(this);
//为Bean读取器设置xml解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

initBeanDefinitionReader(beanDefinitionReader);
//Bean读取器真正实现的地方
loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {//因为我们分析的FileSystemXmlApplicationContext没有重写下面的 getConfigResources方法,所以返回为空,这里不会执行
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configResources);
}


//如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

//调用子类的获取Bean定义资源定位的方法
//该方法在ClassPathXmlApplicationContext中进行实现,对于我们
//举例分析源码的FileSystemXmlApplicationContext没有使用该方法
protected Resource[] getConfigResources() {
return null;
}

可以看出:

  • 首先创建一个BeanDefinitionReader
  • 调用本类中的loadBeanDefinitions(XmlBeanDefinitionReader reader)
  • 在loadBeanDefinitions(XmlBeanDefinitionReader reader)中,先获取资源的定位,然后将资源的位置作为参数,调用reader的reader.loadBeanDefinitions(configLocations)。这个方法在AbstractBeanDefinitionReader类里面实现

注意这个方法中的两个loadBeanDefinitions的参数是不一样的,一个是Resource数组,而另一个是String数组,数组里是bean资源定位的路径

1.4、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
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取ResourceLoader资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}

//
if (resourceLoader instanceof ResourcePatternResolver) {
try {
//调用DefaultResourceLoader的getResourceByPath完成具体的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 {
//调用DefaultResourceLoader的getResourceByPath完成具体的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,
  • 然后调用resourceLoader.getResource(location),这样就完成了从String到Recourse的过程。
  • resourceLoader.getResource(location)在最上面介绍的DefaultResourceLoader类中实现。

1.5、总结

至此,Resource的定位过程就完全完成了。

从最开始的FileSystemXmlApplicationContext类中重写的getResourceByPath方法的调用,可以看出它完整的过程。

  • 从构造函数开始调用各个父类的方法
  • 而各个父类方法在getResourceByPath方法时使用了模版方法,需要子类自己实现
  • 所以,随后又调用到自己重写的这个getResourceByPath方法,完成Resource的定位。

可以感觉到,虽然各个类的继承关系特别复杂,一时间难以理清,但是理解之后发现这种面向对象的编程思想:父类定义整个流程,而具体的实现细节通过模版模式由子类具体实现,这样子类在实现的时候有着很大的灵活性,扩展起来极其方便。

这个图是最标准的方法调用过程。

2、BeanDefinition的载入

关于载入过程,要再次回到AbstractXmlApplicationContext的loadBeanDefinitions方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//获取Bean定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的Bean定义资源
reader.loadBeanDefinitions(configResources);
}


//如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

本来上面分析的是Resource[] configResources = getConfigResources();为空,所以不会执行但是不知道为什么都说是从这里开始执行加载过程的,虽然从逻辑上来看,确实是获取到Resource后开始载入过程。

2.1、XmlBeanDefinitionReader

首先看一下XmlBeanDefinitionReader中loadBeanDefinitions(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
30
31
32
33
34
35
36
37
38
39
40
//XmlBeanDefinitionReader加载资源的入口方法  
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//将读入的XML资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}
//这里是载入XML形式Bean定义资源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
try {
//将资源文件转为InputStream的IO流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从InputStream中得到XML的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的读取过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
//关闭从Resource中得到的IO流
inputStream.close();
}
}
.........
}
//从特定XML文件中实际载入Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//将XML文件转换为DOM对象,解析过程由documentLoader实现
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
//这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
return registerBeanDefinitions(doc, resource);
}
.......
}

可以看出,载入过程相对没有那么复杂,

  • 先获取Resource的流
  • 将流中的XML文件解析为Document对象,这个过程由documentLoader完成。

DocumentLoader将Bean定义资源转换成Document对象的源码如下:

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
//使用标准的JAXP将载入的Bean定义资源转换成document对象  
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//创建文件解析器工厂
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建文档解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//解析Spring的Bean定义资源
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
//创建文档解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//设置解析XML的校验
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}

该解析过程调用JavaEE标准的JAXP标准进行处理。至此Spring IoC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。

注意这个只是XML文件的解析,而不是BeanDefinition的解析。对BeanDefinition的解析是下一步,在registerBeanDefinitions(doc, resource); 中完成

2.2、总结

可以看出,载入过程实际上就是Resource对象转换成Document对象的过程,也就是一个XML文件解析的过程,Spring中使用的是JAXP解析,生成的Document文件就是org.w3c.dom.Document中的Document文件。

3、BeanDefinition的解析

在上面的XmlBeanDefinitionReader类中完成载入工作后,得到了Document对象,接下来调用registerBeanDefinitions(doc, resource)启动Spring IoC容器对Bean定义的解析过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构  
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得容器中注册的Bean数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,//具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//统计解析的Bean数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}

//创建BeanDefinitionDocumentReader对象,解析Document对象
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

该方法先获得一个BeanDefinitionDocumentReader,然后调用documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法,这个方法是在DefaultBeanDefinitionDocumentReader类中实现的。

3.1、DefaultBeanDefinitionDocumentReader

BeanDefinitionDocumentReader接口通过registerBeanDefinitions方法调用其实现类DefaultBeanDefinitionDocumentReader对Document对象进行解析,

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
//根据Spring DTD对Bean的定义规则解析Bean定义Document对象  
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//获得XML描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//获得Document的根元素
Element root = doc.getDocumentElement();
//具体的解析过程由BeanDefinitionParserDelegate实现,
//BeanDefinitionParserDelegate中定义了Spring Bean定义XML文件的各种元素
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
//在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
//从Document的根元素开始进行Bean定义的Document对象
parseBeanDefinitions(root, delegate);
//在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
}
//创建BeanDefinitionParserDelegate,用于完成真正的解析过程
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//BeanDefinitionParserDelegate初始化Document根元素
delegate.initDefaults(root);
return delegate;
}
//使用Spring的Bean规则从Document的根元素开始进行Bean定义的Document对象
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean定义的Document对象使用了Spring默认的XML命名空间
if (delegate.isDefaultNamespace(root)) {
//获取Bean定义的Document对象根元素的所有子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//获得Document节点是XML元素节点
if (node instanceof Element) {
Element ele = (Element) node;
//Bean定义的Document的元素节点使用的是Spring默认的XML命名空间
if (delegate.isDefaultNamespace(ele)) {
//使用Spring的Bean规则解析元素节点
parseDefaultElement(ele, delegate);
}
else {
//没有使用Spring默认的XML命名空间,则使用用户自定义的解//析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
}
else {
//Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的
//解析规则解析Document根节点
delegate.parseCustomElement(root);
}
}
//使用Spring的Bean规则解析Document元素节点
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>元素,
//按照Spring的Bean规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
}
//解析<Import>导入元素,从给定的导入路径加载Bean定义资源到Spring IoC容器中
protected void importBeanDefinitionResource(Element ele) {
//获取给定的导入元素的location属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//如果导入元素的location属性值为空,则没有导入任何资源,直接返回
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//使用系统变量值解析location属性值
location = SystemPropertyUtils.resolvePlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
//标识给定的导入元素的location是否是绝对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
//给定的导入元素的location不是绝对路径
}
//给定的导入元素的location是绝对路径
if (absoluteLocation) {
try {
//使用资源读入器加载给定路径的Bean定义资源
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
//给定的导入元素的location是相对路径
try {
int importCount;
//将给定导入元素的location封装为相对路径资源
Resource relativeResource = getReaderContext().getResource().createRelative(location);
//封装的相对路径资源存在
if (relativeResource.exists()) {
//使用资源读入器加载Bean定义资源
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
//封装的相对路径资源不存在
else {
//获取Spring IoC容器资源读入器的基本路径
String baseLocation = getReaderContext().getResource().getURL().toString();
//根据Spring IoC容器资源读入器的基本路径加载给定导入
//路径的资源
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
ele, ex);
}
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
//在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}
//解析<Alias>别名元素,为Bean向Spring IoC容器注册别名
protected void processAliasRegistration(Element ele) {
//获取<Alias>别名元素中name的属性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取<Alias>别名元素中alias的属性值
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
//<alias>别名元素的name属性值为空
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
//<alias>别名元素的alias属性值为空
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
//向容器的资源读入器注册别名
getReaderContext().getRegistry().registerAlias(name, alias);
}
catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
//在解析完<Alias>元素之后,发送容器别名处理完成事件
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
//解析Bean定义资源Document对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
//对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//向Spring IoC容器注册解析得到的Bean定义,这是Bean定义向IoC容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//在完成向Spring IoC容器注册解析得到的Bean定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

可以看出,Spring采用DOM方式解析xml文件,

  1. 从根节点开始逐个获得子节点Element,然后判断是不是根据Spring命名空间的
    • 如果是,那么调用parseDefaultElement(ele, delegate); 使用Spring的Bean规则解析元素节点
    • 如果不是,那么调用delegate.parseCustomElement(ele); 则使用用户自定义的解析规则解析元素节点
  2. 在Spring的Bean规则解析Document元素节点方法parseDefaultElement中:
    • 如果元素节点是<Import>导入元素,调用importBeanDefinitionResource(ele)方法进行导入解析
    • 如果元素节点是<Alias>别名元素,调用processAliasRegistration(ele)方法进行别名解析
    • 对于既不是<Import>元素,又不是<Alias>元素的元素,即Spring配置文件中普通的<Bean>元素的解析,那么调用processBeanDefinition(ele, delegate)方法
  3. 在processBeanDefinition方法中,定义了一个BeanDefinitionHolder对象,用来持有对bean属性的处理结果,这个处理过程则由BeanDefinitionParserDelegate类中的parseBeanDefinitionElement方法来实现。
  4. 解析完成之后,就会将这个BeanDefinitionHolder对象作为参数调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法,来启动注册的过程。

关于import和ailas书中没有具体分析,主要是介绍了BeanDefinitionParserDelegate中的parseBeanDefinitionElement方法来解析Bean定义资源文件中的<Bean>元素的过程

3.2、BeanDefinitionParserDelegate

1、id、name、alias属性的解析

processBeanDefinitionfang方法源码:

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
//解析Bean定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的id,name  
//和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//获取<Bean>元素中的id属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取<Bean>元素中的name属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//获取<Bean>元素中的alias属性值
List<String> aliases = new ArrayList<String>();
//将<Bean>元素中的所有name属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果<Bean>元素中没有配置id属性时,将别名中的第一个值赋值给beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//检查<Bean>元素所配置的id或者name的唯一性,containingBean标识<Bean>
//元素中是否包含子<Bean>元素
if (containingBean == null) {
//检查<Bean>元素所配置的id、name或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
//详细对<Bean>元素中配置的Bean定义进行解析的地方,这个beanDefinition终于定义出来了
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果<Bean>元素中没有配置id、别名或者name,且没有包含子//<Bean>元素,为解析的Bean生成一个唯一beanName并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果<Bean>元素中没有配置id、别名或者name,且包含了子//<Bean>元素,为解析的Bean使用别名向IoC容器注册
beanName = this.readerContext.generateBeanName(beanDefinition);
//为解析的Bean使用别名注册时,为了向后兼容Spring1.2/2.0,给别名添加类名后缀
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//当解析出错时,返回null
return null;
}

到了这里,在IOC容器中存在的数据结构AbstractBeanDefinition beanDefinition终于被定义出来了。

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

2、其他属性的解析

parseBeanDefinitionElement方法是生成这个AbstractBeanDefinition 的地方:

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
//详细对<Bean>元素中配置的Bean定义其他属性进行解析,由于上面的方法中已经对Bean的id、name和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据  
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//记录解析的<Bean>
this.parseState.push(new BeanEntry(beanName));
//这里只读取<Bean>元素中配置的class名字,然后载入到BeanDefinition中去
//只是记录配置的class名字,不做实例化,对象的实例化在依赖注入时完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//如果<Bean>元素中配置了parent属性,则获取parent属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
//为载入Bean定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//为<Bean>元素解析的Bean设置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//对<Bean>元素的meta(元信息)属性解析
parseMetaElements(ele, bd);
//对<Bean>元素的lookup-method属性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//对<Bean>元素的replaced-method属性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<Bean>元素的构造方法设置
parseConstructorArgElements(ele, bd);
//解析<Bean>元素的<property>设置
parsePropertyElements(ele, bd);
//解析<Bean>元素的qualifier属性
parseQualifierElements(ele, bd);
//为当前解析的Bean设置所需的资源和依赖对象
bd.setResource(this.readerContext.getResource());
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();
}
//解析<Bean>元素出错时,返回null
return null;
}

这里是BeanDefinition生成的地方,大部分属性都被解析出来,下面再次以<property>属性为例,分析一下这个属性的解析过程。

3、property元素的解析

在解析过程中,这些属性值会被封装成PropertyValue对象并设置到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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//解析<Bean>元素中的<property>子元素  
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取<Bean>元素中所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果子元素是<property>子元素,则调用解析<property>子元素方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}

//解析<property>元素
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取<property>元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果一个Bean中已经有同名的property存在,则不进行解析,直接返回。
//即如果在同一个Bean中配置同名的property,则只有第一个起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析获取property的值
Object val = parsePropertyValue(ele, bd, propertyName);
//根据property的名字和值创建property实例
PropertyValue pv = new PropertyValue(propertyName, val);
//解析<property>元素中的属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}

//解析获取property值
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
//获取<property>的所有子元素,只能是其中一种类型:ref,value,list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//子元素不是description和meta属性
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {//当前<property>元素包含有子元素
subElement = (Element) node;
}
}
}
//判断property的属性值是ref还是value,不允许既是ref又是value
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
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,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//一个指向运行时所依赖对象的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//设置这个ref的数据对象是被当前的property对象所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果属性是value,创建一个value的数据对象TypedStringValue,这个对象封装了value信息
else if (hasValueAttribute) {
//一个持有String类型值的对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//设置这个value数据对象是被当前的property对象所引用
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//如果当前<property>元素还有子元素
else if (subElement != null) {
//解析<property>的子元素
return parsePropertySubElement(subElement, bd);
}
else {
//propery属性中既不是ref,也不是value属性,解析出错返回null error(elementName + " must specify a ref or value", ele);
return null;
}
}

可以看出,对<property>解析的时候,先根据

  1. 先根据propertyName 来确定<property>的名字,然后封装一个PropertyEntry对象
  2. 如果已经存在一个同名的则直接返回。可知:如果在同一个Bean中配置同名的property,则只有第一个起作用
  3. 如果没有同名的,则继续解析property的值 ,并将其封装成PropertyValue对象
  4. 解析<property>的所有子元素,只能是其中一种类型:ref,value,list等,
    • 对值的属性进行判断,是ref还是value,不能同时是这两者
    • 如果属性是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
    • 如果属性是value,创建一个value的数据对象TypedStringValue,这个对象封装了value信息
    • ref和value都通过ref(或valueHolder).setSource(extractSource(ele));方法将属性值/引用与所引用的属性关联起来。
    • 不是ref或value而是子元素,则继续通过parsePropertySubElement方法解析

4、property子元素的解析

下面是对property子元素的解析过程,Array、List、Set、Map等元素都会在这里进行解析:

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
//解析<property>元素中ref,value或者集合等子元素  
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析//内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果子元素是bean,则使用解析<Bean>元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果子元素是ref,ref中只能有以下3个属性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//获取<property>元素中的bean属性值,引用其他解析的Bean的名称
//可以不再同一个Spring配置文件中,具体请参考Spring对ref的配置规则
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中的local属性值,引用同一个Xml文件中配置
//的Bean的id,local和ref不同,local只能引用同一个配置文件中的Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中parent属性值,引用父级容器中的Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
//没有配置ref的目标属性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
//创建ref类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//设置引用类型值是被当前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果子元素是<idref>,使用解析ref元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//如果子元素是<value>,使用解析value元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是null,为<property>设置一个封装null值的字符串数据
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//如果子元素是<array>,使用解析array集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//如果子元素是<list>,使用解析list集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//如果子元素是<set>,使用解析set集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//如果子元素是<map>,使用解析map集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//如果子元素是<props>,使用解析props集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
//既不是ref,又不是value,也不是集合,则子元素配置错误,返回null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}

这个方法很明白,就是根据子元素可能的属性进行判断,然后分别调用对应的方法进行解析。

下面以list的解析为例:

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
//解析<list>集合子元素  
public List parseListElement(Element collectionEle, BeanDefinition bd) {
//获取<list>元素中的value-type属性,即获取集合元素的数据类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
//获取<list>集合元素中的所有子节点
NodeList nl = collectionEle.getChildNodes();
//Spring中将List封装为ManagedList
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
//设置集合目标数据类型
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具体的<list>元素解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
//具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
//遍历集合所有节点
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//节点不是description节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//将解析的元素加入集合中,递归调用下一个子元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}

可以看出,这个方法返回的是一个List对象,而具体的类是Spring定义的ManagedList,作为封装List这类配置定义的数据封装。

这应该是最后一层了,至此,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition,它是Bean定义资源文件中配置的POJO对象在Spring IoC容器中的映射,我们可以通过AbstractBeanDefinition为入口,让IoC容器进行索引、查询和操作。

通过Spring IoC容器对Bean定义资源的解析后,IoC容器大致完成了管理Bean对象的准备工作,即初始化过程,但是最为重要的依赖注入还没有发生,现在在IoC容器中BeanDefinition存储的只是一些静态信息,接下来需要向容器注册Bean定义信息才能全部完成IoC容器的初始化过程

4、BeanDefinition的注册

在3.2的DefaultBeanDefinitionDocumentReader类中提到,在完成解析之后,就会将这个BeanDefinitionHolder对象作为参数调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法,来启动注册的过程。

下面来看一下这个registerBeanDefinition方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//将解析的BeanDefinitionHold注册到容器中 
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//获取解析的BeanDefinition的名称
String beanName = definitionHolder.getBeanName();
//向IoC容器注册BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果解析的BeanDefinition有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}

当调用BeanDefinitionReaderUtils向IoC容器注册解析的BeanDefinition时,真正完成注册功能的是DefaultListableBeanFactory。在DefaultListableBeanFactory中,是通过一个ConcurrentHashMap来持有这个BeanDefinition的,

1
2
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

4.1、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
//存储注册的俄BeanDefinition  
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//向IoC容器注册解析的BeanDefiniton
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");
//校验解析的BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//注册的过程中需要线程同步,以保证数据的一致性
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册,并且不允许覆盖已注册的Bean,则抛出注册失败异常
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
//IoC容器中没有已经注册同名的Bean,按正常注册流程注册
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
//重置所有已经注册过的BeanDefinition的缓存
resetBeanDefinition(beanName);
}
}

至此,Bean定义资源文件中配置的Bean被解析过后,已经注册到IoC容器中,被容器管理起来,真正完成了IoC容器初始化所做的全部工作。现在IoC容器中已经建立了整个Bean的配置信息,这些BeanDefinition信息已经可以使用,并且可以被检索,IoC容器的作用就是对这些注册的Bean定义信息进行处理和维护。这些的注册的Bean定义信息是IoC容器控制反转的基础,正是有了这些注册的数据,容器才可以进行依赖注入。

三、总结

这篇源码分析应该是我用时最长的一篇,因为实在是太复杂了,写到这里最上面的已经有些记不清了,不知道再过几天还能记住多少,不过确实收获了很多东西

  1. 开始最难的就是各个类定义的比较多,找不到对应的关系,但是弄明白以后发现这样多层的继承关系有着很强的可扩展性,学到了一个设计模式就是模版模式,在父类中定义好整体流程,具体的方法留在各个不同的子类中实现,各种不同资源的定位就是用的这个思想
  2. 载入和解析过程主要就是XML文件的解析,这里又学习了Java中XML文件的解析方法,DOM和SAX两种方法的区别,Spring中用的是JAXP的方法,而不是很有名的JDOM或者DOM4J。
  3. 其他就是明白了整个bean从定义,到变成容器中数据结构的整体流程
  4. 学习是一个厚积薄发的过程,开始的时候先不要看一些超出自己能力太多的东西,比如从刚开学的时候就想学Spring的源码,看了Spring技术内幕和很多博客,但是怎么看都看不懂。过了半个学期以后基本能勉强看懂了,当然,和Spring技术内幕这本书排版太乱也有一定关系
  5. 主要参考的还是http://www.cnblogs.com/ITtangtang/p/3978349.html这篇博客,感谢作者。

四、参考地址

http://blog.csdn.net/randyjiawenjie/article/details/8315861

http://elim.iteye.com/blog/2016305

http://www.blogjava.net/DLevin/archive/2012/12/01/392337.html

http://www.cnblogs.com/chenssy/p/5619170.html

http://www.cnblogs.com/ITtangtang/p/3978349.html

https://my.oschina.net/kaywu123/blog/614325