1. 目录

[TOC]

2. 说明

3. 源码解析

3.1. AbstractRefreshableApplicationContext#loadBeanDefinitions

AbstractRefreshableApplicationContext 不提供加载beanDefinition的具体逻辑,交由子类实现,xml加载的方式的子类为XmlWebApplicationContext,

loadBeanDefinitions 加载Bean定义,此类在AbstractRefreshableApplicationContext为抽象方法,因此需要找到实现类方法XmlWebApplicationContext#loadBeanDefinitions() 实现类加载Bean定义

3.2. 加载入口方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建 BeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

//写入需要的配置
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

//初始化initBeanDefinitionReader,此处为空,方法为空方法
initBeanDefinitionReader(beanDefinitionReader);
//加载BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}

3.3. loadBeanDefinitions() 加载BeanDefinition

1
2
3
4
5
6
7
8
9
10
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//获取配置信息 一般为classpath:applicationContext.xml
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
//加载bean定义,由XmlBeanDefinitionReader负责解析,见后文
reader.loadBeanDefinitions(configLocation);
}
}
}

从这里可以看出来,此时开始获取到Spring的配置文件,准备从配置文件中加载Bean的定义相关的信息

4. XmlBeanDefinitionReader 解析Spring的xml文件配置信息

XmlBeanDefinitionReader继承至AbstractBeanDefinitionReader,其加载BeanDefinition的主要方法由抽象类完成

4.1. AbstractBeanDefinitionReader#loadBeanDefinitions()

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
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
//重载方法
return loadBeanDefinitions(location, null);
}
//加载Bean定义
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//拿到资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}

//xml方式进入此if分支中
if (resourceLoader instanceof ResourcePatternResolver) {
try {
//将applicationContext.xml配置文件转换为资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

//加载资源下的BeanDefinitions
//此处经历loadBeanDefinitions的各种重载函数,最后其实到了loadBeanDefinitions(EncodeResource resource)方法
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
//根据资源加载BeanDefinition
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
//重载方法,根据资源加载BeanDefinition
Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
count += loadBeanDefinitions(resource);
}
return count;
}
/*
重载方法,根据资源加载BeanDefinition
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/*
重载方法,根据EncodedResource加载BeanDefinition,
EncodedResource 其实就是Resource的封装,内部细节可以不用深挖
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}

//ThreadLocal中缓存已经读取的,检测不重复读取
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}

//开始读流,将Resource真正转换成可用的输入流
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//执行加载BeanDefinition操作
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

4.2. doLoadBeanDefinitions() 读取xml docment 加载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
27
28
29
30
31
32
33
34
35
36
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 通过resource加载Document,内部有documentLoader加载器,至于从inputstream加载成Document则可以暂时不考虑
Document doc = doLoadDocument(inputSource, resource);
// 注册BeanDefinition
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
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);
}
}

4.3. registerBeanDefinitions() 注册BeanDefinition

注册BeanDefinition

1
2
3
4
5
6
7
8
9
10
11
12
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建Reader,此处默认加载Spring命名空间的默认BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获取条数,为了得到此次注册后的差异而已
int countBefore = getRegistry().getBeanDefinitionCount();

//注册方法,通过默认的加载,将bean加载出来根据不同的命名空间注册成不同的BeanDefinition,见下
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

//获取条数,为了得到此次注册后的差异而已
return getRegistry().getBeanDefinitionCount() - countBefore;
}

4.4. DefaultBeanDefinitionDocumentReader#registerBeanDefinitions()

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
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {

//创建代理类,目的是为了把父信息保留下来
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);

//处理profile,不知道干嘛的,不重要
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//预处理,此处为空方法,无逻辑
preProcessXml(root);
//解析beanBeanDefinitions
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);

this.delegate = parent;
}

4.5. 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
//这里的root最开始是beans,后面递归根据嵌套的内容发生变化
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//解析默认命名空间,大部分用的 是默认命名空间 beans
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
//默认命名空间,如bean,beans,import,resource,
//<bean class="com.kewen.service.HelloService" id="helloService"/>
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
//非默认的命名空间,如context
//<context:component-scan base-package="com.kewen"/>
//注意,解析context时会加载ReaderContext中的NamespaceHandler,此类用来扫描注解中的@Component注解并加入BeanDefinition
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

4.6. DefaultBeanDefinitionDocumentReader#parseDefaultElement()解析默认的标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
doRegisterBeanDefinitions(ele);
}
}

此处根据默认的bean类型解析对应的标签,具体逻辑就暂时忽略了

4.7. BeanDefinitionParserDelegate#parseCustomElement()解析非默认的标签

非默认的标签,即没有在 xmlns="http://www.springframework.org/schema/beans空间下的,需要单独引入的,如context需引入xmlns:context="http://www.springframework.org/schema/context"

spring 提供的自定义标签解析器如图:
20230713103128

代码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//命名空间uri
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//拿到命名空间解析器,主要逻辑就在这内部,
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

此处对应拿到不通命名空间的各自的NamespaceHandler,开始各自的解析逻辑

至此,xml标签的解析就完成了

对于程序中的@Component注解,xml需配置扫描<context:component-scan/>加上这标签的则会在拿到命名空间时拿到ContextNameSpaceHandler,用于扫描包并解析BeanDefinition

5. 注意要点

此处只是针对xml文件下的对应命名空间下的BeanDefinition的加载,至于使用注解@Component的加载是通过一个扫描器执行,后续再进行讲解
//todo 基于注解的

6. 总结

加载Bean定义主要就是先拿到配置的文件地址,再转换成流,再转换成Docment元素,使用xml文档的标准解析得到对应的节点元素,分析元素类型再进行解析成对应的BeanDefinition