XmlBeanFactory加载BeanDefinition的流程

XmlBeanFactory 是一个最简单的 BeanFactory 实现,它支持通过 xml 的形式配置和管理 bean。本文主要关注其解析 xml 和加载 BeanDefinition 的流程。

XmlBeanFactory 继承自 DefaultListableBeanFactory,后者是一个功能完备的 BeanFactory 实现类,XmlBeanFactory 在它的基础上增加了读取 xml 解析 BeanDefinition 的功能。XmlBeanFactory 并没有自己完成这些工作,而是交给了 XmlBeanDefinitionReader 类来实现。

1
2
3
4
5
6
7
8
9
10
11
public class XmlBeanFactory extends DefaultListableBeanFactory {
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

// 省略其他无关代码。。。

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 解析 xml,加载 bean definition
this.reader.loadBeanDefinitions(resource);
}
}

Document 的加载

在获取到表示资源的 Resource 对象之后,首先要从中读取 inputStream 并转换成用于表示 xml 文档的 Document 对象。

Document 的加载是委托给 DefaultDocumentLoader 类来处理的。DefaultDocumentLoaderDocumentLoader 的默认实现,它使用标准的 JAXP XML 解析器加载 Document。它的类继承结构如下图所示:

image-20200410011926136

loadDocument 方法用于解析输入流,生成 Document 对象,主要流程如下:

  1. 构建 DocumentBuilderFactory
  2. 通过 DocumentBuilderFactory 构建 DocumentBuilder
  3. 使用 builder 解析输入流,生成 Document 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

// 构建 DocumentBuilderFactory
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}

// 构建 DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);

// 解析输入流
return builder.parse(inputSource);
}

DocumentBuilderFactory 的构建

DocumentBuilderFactory 是通过它的工厂方法创建的,根据验证模式(DTD模式、XSD 模式)的不同需设置不同的属性

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 DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);

if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);

// XSD模式的独有逻辑
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
factory.setNamespaceAware(true);
try {
// 设置固定属性
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
// 忽略异常的构建
throw pcex;
}
}
}

return factory;
}

DocumentBuilder 的构建

DocumentBuilder 是通过 DocumentBuilderFactory 创建的,另外需要设置 EntityResolver 和 ErrorHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {

// 创建一个新的 DocumentBuilder
// 通过调用 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
// 创建 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl 实例
DocumentBuilder docBuilder = factory.newDocumentBuilder();

// 设置 SAX EntityResolver
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}

// 设置 SAX ErrorHandler
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}

其中 EntityResolver 用于解析外部实体,需要根据 publicId 和 systemId 定位到资源,并将其解析为 InputResource 对象,如下是一个示例

1
2
3
4
5
6
7
8
9
10
11
12
public class MyResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId) {
if (systemId.equals("http://www.myhost.com/today")) {
// return a special input source
MyReader reader = new MyReader();
return new InputSource(reader);
} else {
// use the default behaviour
return null;
}
}
}

Spring 通过 DelegatingEntityResolver 来完成 DTD 或 XSD 文档的解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
// dtd 格式文档解析
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
// xsd 格式文档解析,默认到 META-INF/spring.schemas 中查找
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}

DelegatingEntityResolver 并不是一个真正的执行者,它将 DTD 和 XSD 格式的文档的解析分别委托给 BeansDtdResolverPluggableSchemaResolver

如下为 PluggableSchemaResolver 的解析方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public InputSource resolveEntity(String publicId, @Nullable String systemId) throws IOException {
if (systemId != null) {
// 获取 systemId 对应的本地文件位置
String resourceLocation = getSchemaMappings().get(systemId);
if (resourceLocation != null) {
// 加载文件
Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
try {
// 封装为 InputSource
InputSource source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);

return source;
}
catch (FileNotFoundException ex) {
// 忽略日志
}
}
}
return null;
}

它的 getSchemaMappings 方法会从 META-INF/spring.schemas 文件中读取配置信息(systemId 到 XSD 文档的映射关系),这样 resolveEntity 方法就可以根据 systemId 找到 class path 下的 XSD 文档并加载为 InputSource,从而避免了从网络下载文档的不稳定性

1
2
3
// 加载 META-INF/spring.schemas 文件配置
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);

解析 inputSource

有了 DocumentBuilder 后就可以解析输入流了

1
return builder.parse(inputSource);

这里的 inputSource 是一个 InputSource 类型的实例,它封装了 inputStream,并且设置了编码。具体的 parse 流程就不贴出来了。

1
2
3
4
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}

创建 BeanDefinitionDocumentReader

上一步已经构建好了 Document 对象,接下来看下要怎么读取、解析和注册 BeanDefinition。

BeanDefinitionDocumentReader

BeanDefinitionDocumentReader 用于读取 Document 对象,完成 BeanDefintion 的注册。

DefaultBeanDefinitionDocumentReaderBeanDefinitionDocumentReader 的一个默认实现类,它的继承结构如下图所示:

image-20200410014832813

createBeanDefinitionDocumentReader

如下是创建 DefaultBeanDefinitionDocumentReader 实例的代码,可以看出是通过反射创建的。

1
2
3
4
5
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
DefaultBeanDefinitionDocumentReader.class;
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanUtils.instantiateClass(this.documentReaderClass);
}

registerBeanDefinitions

有了 BeanDefinitionDocumentReader 之后就可以处理 Document 了,这是通过它的 registerBeanDefinitions 方法实现的

1
2
void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
throws BeanDefinitionStoreException;

createReaderContext

registerBeanDefinitions 入参中有一个 XmlReaderContext 对象,它是通过如下方式创建的:

1
2
3
4
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}

注意到最后一个参数是通过 getNamespaceHandlerResolver 方法获取到的,该方法返回了一个 NamespaceHandlerResolver 类型的对象,它的默认类是 DefaultNamespaceHandlerResolver

image-20200410015824553

NamespaceHandlerResolver 接口提供了一个 resolve 方法,它可以从 META-INF/spring.handlers 文件中读取 namespaceUriNamespaceHandler 的映射关系,在使用时根据 namespaceUri 返回对应的 NamespaceHandler,这可以用于自定义 namespace 处理逻辑。如下是一个 META-INF/spring.handlers 文件的示例:

1
2
3
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

createDelegate

创建委托类,用于解析 XML bean 定义

1
2
3
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
delegate.initDefaults(root, parentDelegate);
return delegate;

initDefaults 方法会为 DocumentDefaultsDefinition 设置默认属性值,设置完之后发送事件通知

1
2
3
4
5
6
7
public void initDefaults(Element root, @Nullable BeanDefinitionParserDelegate parent) {
// 填充默认值
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);

// 通知已设置完默认值
this.readerContext.fireDefaultsRegistered(this.defaults);
}

填充默认值

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
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
// Potentially inherited from outer <beans> sections, otherwise falling back to false.
// lazyInit默认值为false
lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
}
defaults.setLazyInit(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默认值为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());
}

defaults.setSource(this.readerContext.extractSource(root));
}

处理 profile

如果是默认的 namespace,则检查 profile 是否生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (this.delegate.isDefaultNamespace(root)) {
// 解析 profile 属性,比如每个环境对应一套 bean 配置
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {

return;
}
}
}

parseBeanDefinitions

到这里要开始处理 Document 文档,解析 BeanDefinition 了。如果是默认的 namespace,则循环遍历节点。如果 Element 是默认 namespace 的,则需按照默认元素处理。其他情况按照自定义元素处理

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
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 默认处理逻辑
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析默认元素
parseDefaultElement(ele, delegate);
}
else {
// 解析自定义节点元素
delegate.parseCustomElement(ele);
}
}
}
}
// 处理自定义元素
else {
// 解析自定义节点元素
delegate.parseCustomElement(root);
}
}

默认 namspace 的处理

根据节点名的不同分别进行处理

解析 import 元素

  1. 解析 location,替换 placeholder,得到实际的 location

  2. 判断是否为绝对路径,如果是绝对路径,则直接通过 XmlBeanDefinitionReader 加载 BeanDefinition

    1
    int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
  3. 否则,按照相对路径处理

    1. 根据相对路径加载资源

      1
      Resource relativeResource = getReaderContext().getResource().createRelative(location)
    2. 判断 Resource 是否存在,若存在则可以直接加载

      1
      2
      3
      4
      if (relativeResource.exists()) {
      importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
      actualResources.add(relativeResource);
      }
    3. 否则,通过URL拼接路径后加载

      1
      2
      3
      String baseLocation = getReaderContext().getResource().getURL().toString();
      importCount = getReaderContext().getReader().loadBeanDefinitions(
      StringUtils.applyRelativePath(baseLocation, location), actualResources);
  4. 发送 import 完成事件

    1
    2
    Resource[] actResArray = actualResources.toArray(new Resource[0]);
    getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));

解析 alias 元素

主要就是获取 alias name,然后往 AliasRegistry 中注册

1
getReaderContext().getRegistry().registerAlias(name, alias);

同样也会发送完成事件

1
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));

解析 bean 元素

这个是注册 BeanDefinition的关键

委托 delegate 解析元素
1
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

主要就是解析 xml 元素的值,然后设置到 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
// 创建beanDefinition,仅设置类相关信息
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

// 解析并设置bean属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

// 解析meta数据
parseMetaElements(ele, bd);

// Lookup元素
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

// 方法覆盖
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

// 构造函数参数
parseConstructorArgElements(ele, bd);

// 属性
parsePropertyElements(ele, bd);

// qualifier
parseQualifierElements(ele, bd);

bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));

return bd;

如果 xml 中未设置 bean 的 id 和 name,还要生成一个唯一的 id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (containingBean != null) {
// 生成一个在bean容器中唯一的名字
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
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)) {
aliases.add(beanClassName);
}
}

最终的结果会封装为 BeanDefinitionHolder

1
2
// 封装结果并返回
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
进行必要的装饰
1
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

前面提到了 NamespaceHandler 的注册,这里就是用它进行 decorate 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// 忽略
}
}
注册最终装饰过的实例
1
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

如下是 BeanDefintion 注册的相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
// 注册beanDefinition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

主要是往 BeanDefinitionRegistry 中注册 beanName 到 BeanDefintion 的映射关系,如果有别名,还需要一并注册进去

发送注册通知事件
1
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))

解析嵌套 bean 元素

递归处理元素

1
doRegisterBeanDefinitions(ele);

自定义 namspace 的处理

Spring 还可以自定义 XSD 元素,并提供对应的元素处理器

getNamespaceURI

获取 element 的 namespace uri

1
2
3
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}

getNamespaceHandler

1
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  1. 首先要获取 NamespaceHandlerResolver,取到的是 DefaultNamespaceHandlerResolver,这是在前面 createReaderContext 的时候创建的
  2. 根据 namespaceUri 获取 NamespaceHandler
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
public NamespaceHandler resolve(String namespaceUri) {
// URI -> NamespaceHandler的实例或类名
Map<String, Object> handlerMappings = getHandlerMappings();

// 根据 namespaceUri 获取 handler
Object handlerOrClassName = handlerMappings.get(namespaceUri);

if (handlerOrClassName == null) {
return null;
}
// 取到的是实例
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
// 取到的是类名
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}

// 实例化
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);

// 初始化
namespaceHandler.init();

// 放到 map 中
// 下次获取到的就是实例,而不是 class name
handlerMappings.put(namespaceUri, namespaceHandler);

return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}

其中 HandlerMapping 是解析 META-INF/spring.handlers 的结果

使用 NamespaceHandler 解析元素

1
2
// parse
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

整体流程

下图是整体处理流程

image-20200411125807780