Spring XML Bean 定义的加载和注册

前言

本篇文章主要介绍 Spring IoC 容器怎么加载 bean 的定义元信息。

下图是一个大致的流程图:

Spring XML Bean 定义的加载和注册

第一次画图,画的有点烂。:joy:

正文

首先定义一个简单的 POJO,如下:

<code>public class User {

\tprivate Long id;
\tprivate String name;

\tpublic Long getId() {
\t\treturn id;
\t}

\tpublic void setId(Long id) {
\t\tthis.id = id;
\t}

\tpublic String getName() {
\t\treturn name;
\t}

\tpublic void setName(String name) {
\t\tthis.name = name;
\t}

\t@Override
\tpublic String toString() {
\t\treturn "User{" +
\t\t\t\t"id=" + id +
\t\t\t\t", name='" + name + '\\'' +
\t\t\t\t'}';
\t}
}/<code>

再编写一个 XML 文件。

<code>
<beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

\t<bean>
\t\t<property>

\t\t<property>
\t/<bean>

/<beans>/<code>

最后再来一个测试类。

<code>@Test
public void test(){
\tDefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
\tXmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
\treader.loadBeanDefinitions("META-INF/spring-bean.xml");
\tUser user = beanFactory.getBean("user", User.class);
\tSystem.out.println(user);
}/<code>

上面这段代码比较简单,无非就是声明 bean 工厂,然后通过指定的 XML 文件加载 bean 的定义元信息,最后通过 bean 工厂获取 bean 。

DefaultListableBeanFactory

首先我们来了解一下 DefaultListableBeanFactory ,下面是该类的类图及层次结构。

Spring XML Bean 定义的加载和注册

  • AliasRegistry: 定义对 alias 的简单增删改等操作。
  • SimpleAliasRegistry: 主要使用 map 作为 alias 的缓存,并对接口 AliasRegistry 进行实现。
  • SingletonBeanRegistry: 定义了对单例 bean 的注册及获取。
  • BeanFactory: 定义获取单个 bean 及 bean 的各种属性。
  • DefaultSingletonBeanRegistry: 对接口 SingletonBeanRegistry 各函数的实现。
  • HierarchicalBeanFactory: 继承 BeanFactory ,也就是在 BeanFactory 定义的功能的基础上增加了对 parentBeanFactory 的支持。
  • BeanDefinitionRegistry: 定义了对 BeanDefinition 的各种增删改操作。
  • FactoryBeanRegistrySupport: 在 DefaultSingletonBeanRegistry 基础上增加了对 FactoryBean 的特殊处理功能。
  • ConfigurableBeanFactory:
    提供配置 BeanFactory 的各种方法。
  • ListableBeanFactory: 继承 BeanFactory 提供了获取多个 bean 的各种方法。
  • AbstractBeanFactory: 综合 FactoryBeanRegistrySupport 和 ConfigurableBeanFactory 的功能。
  • AutowireCapableBeanFactory: 提供创建 bean 、自动注入、初始化以及应用 bean 的后处理器。
  • AbstractAutowireCapableBeanFactory: 综合 AbstractBeanFactory 并对接口 AutowireCapableBeanFactory 进行实现。
  • ConfigurableListableBeanFactory: BeanFactory 配置清单,指定忽略类型及接口等。
  • DefaultListableBeanFactory: 综合上面所有功能,主要是对 bean 注册后的处理。

可以看到上面的接口大多数是定义了一些功能或在父接口上扩展了一些功能, DefaultListableBeanFactory 实现了所有接口,大多数默认情况下我们所使用的 beanFactory 就是 DefaultListableBeanFactory 。

1.AbstractBeanDefinitionReader#loadBeanDefinitions 方法

<code>public int loadBeanDefinitions(String location, @Nullable Set<resource> actualResources) throws BeanDefinitionStoreException {
// 获取 resourceLoader,这边是 PathMatchingResourcePatternResolver

\tResourceLoader resourceLoader = getResourceLoader();
\tif (resourceLoader == null) {
\t\tthrow new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
\t}
\t// 判断 resourceLoader 是否是 ResourcePatternResolver,我们这边是符合的
\tif (resourceLoader instanceof ResourcePatternResolver) {
\t\t// Resource pattern matching available.
\t\ttry {
// 根据路径获取所欲符合的配置文件并封装成 Resource 对象
\t\t\tResource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 根据 Resource 加载 bean definition,并返回数量,见下面详解
\t\t\tint count = loadBeanDefinitions(resources);
\t\t\tif (actualResources != null) {
\t\t\t\tCollections.addAll(actualResources, resources);
\t\t\t}
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
\t\t\t}
\t\t\treturn count;
\t\t}
\t\tcatch (IOException ex) {
\t\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t\t"Could not resolve bean definition resource pattern [" + location + "]", ex);
\t\t}
\t}
\telse {
\t\t// Can only load single resources by absolute URL.
// 只能通过绝对路径加载单个资源
\t\tResource resource = resourceLoader.getResource(location);
// 根据 Resource 加载 bean definition,并返回数量,见下面详解
\t\tint count = loadBeanDefinitions(resource);
\t\tif (actualResources != null) {
\t\t\tactualResources.add(resource);
\t\t}
\t\tif (logger.isTraceEnabled()) {
\t\t\tlogger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
\t\t}
\t\treturn count;
\t}
}/<resource>/<code>

上面方法主要是将资源文件转换为 Resource 对象,然后调用 loadBeanDefinitions(Resource...) 加载 BeanDefinition 。

<code>public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
\tAssert.notNull(resources, "Resource array must not be null");
\tint count = 0;
\tfor (Resource resource : resources) {
// 加载 BeanDefinition,见下文详解
\t\tcount += loadBeanDefinitions(resource);
\t}
\treturn count;
}/<code>

该方法主要就是遍历 resources 然后调用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource) 。

2.XmlBeanDefinitionReader#loadBeanDefinitions

<code>public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 将 Resource 封装成 EncodedResource,也就是对资源指定编码和字符集
\treturn loadBeanDefinitions(new EncodedResource(resource));
}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
\tAssert.notNull(encodedResource, "EncodedResource must not be null");
\tif (logger.isTraceEnabled()) {
\t\tlogger.trace("Loading XML bean definitions from " + encodedResource);
\t}
\t// 当前正在加载的 EncodedResource
\tSet<encodedresource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
\tif (currentResources == null) {
\t\tcurrentResources = new HashSet<>(4);
\t\tthis.resourcesCurrentlyBeingLoaded.set(currentResources);
\t}
// 如果当前 EncodedResource 以及存在,代表出现了循环加载,抛出异常
\tif (!currentResources.add(encodedResource)) {
\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
\t}
\ttry {
// 获取 Resource 的输入流
\t\tInputStream inputStream = encodedResource.getResource().getInputStream();
\t\ttry {
// 将 inputStream 封装成 org.xml.sax.InputSource

\t\t\tInputSource inputSource = new InputSource(inputStream);
// 如果 encodedResource 的编码不为空,设置 inputSource 的编码
\t\t\tif (encodedResource.getEncoding() != null) {
\t\t\t\tinputSource.setEncoding(encodedResource.getEncoding());
\t\t\t}
// 加载 BeanDefinition (方法以 do 开头,真正处理的方法)
\t\t\treturn doLoadBeanDefinitions(inputSource, encodedResource.getResource());
\t\t}
\t\tfinally {
// 关闭流
\t\t\tinputStream.close();
\t\t}
\t}
\tcatch (IOException ex) {
\t\tthrow new BeanDefinitionStoreException(
\t\t\t\t"IOException parsing XML document from " + encodedResource.getResource(), ex);
\t}
\tfinally {
// 当前资源以及加载完毕,从 currentResources 中移除
\t\tcurrentResources.remove(encodedResource);
\t\tif (currentResources.isEmpty()) {
\t\t\tthis.resourcesCurrentlyBeingLoaded.remove();
\t\t}
\t}
}/<encodedresource>/<code>

上面主要将 Resource 封装成 EncodedResource ,也就是制定资源的编码和字符集。然后获取 Resource 的输入流 InputStream ,并封装成 InputSource 设置其编码,最终调用 doLoadBeanDefinitions 开始真正的加载流程。

3.XmlBeanDefinitionReader#doLoadBeanDefinitions

<code>protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
\t\tthrows BeanDefinitionStoreException {
\ttry {
// 根据 inputSource 和 resource 加载 XML 文件,并封装成 Document,见下文详解
\t\tDocument doc = doLoadDocument(inputSource, resource);
// 用 doc 去解析和注册 bean definition,见下文详解
\t\tint count = registerBeanDefinitions(doc, resource);

\t\tif (logger.isDebugEnabled()) {
\t\t\tlogger.debug("Loaded " + count + " bean definitions from " + resource);
\t\t}
\t\treturn count;
\t}
\tcatch (BeanDefinitionStoreException ex) {
\t\tthrow ex;
\t}
\tcatch (SAXParseException ex) {
\t\tthrow new XmlBeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
\t}
\tcatch (SAXException ex) {
\t\tthrow new XmlBeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"XML document from " + resource + " is invalid", ex);
\t}
\tcatch (ParserConfigurationException ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Parser configuration exception parsing XML from " + resource, ex);
\t}
\tcatch (IOException ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"IOException parsing XML document from " + resource, ex);
\t}
\tcatch (Throwable ex) {
\t\tthrow new BeanDefinitionStoreException(resource.getDescription(),
\t\t\t\t"Unexpected exception parsing XML document from " + resource, ex);
\t}
}/<code>

上面代码抛开异常处理,逻辑非常简单,就是用 inputSource 和 resource 加载 XML 文件,并封装成 Document 对象,然后去注册 BeanDefinition 。

4.XmlBeanDefinitionReader#doLoadDocument

<code>protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
\treturn this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
\t\t\tgetValidationModeForResource(resource), isNamespaceAware());
}

// 获取 XML 文件的验证模式
protected int getValidationModeForResource(Resource resource) {
// 如果手动指定了验证模式则使用指定的验证模式
\tint validationModeToUse = getValidationMode();

\tif (validationModeToUse != VALIDATION_AUTO) {
\t\treturn validationModeToUse;
\t}
// 如果未指定则使用自动检测,其实就是判断文件是否包含 DOCTYPE,如果
\tint detectedMode = detectValidationMode(resource);
\tif (detectedMode != VALIDATION_AUTO) {
\t\treturn detectedMode;
\t}
\t// Hmm, we didn't get a clear indication... Let's assume XSD,
\t// since apparently no DTD declaration has been found up until
\t// detection stopped (before finding the document's root tag).
// 如果没有找到验证,默认使用 XSD 模式,因为 DTD 已经不维护了
\treturn VALIDATION_XSD;
}

// DefaultDocumentLoader.java
// 这里就是使用 DocumentLoader 去加载 XML 文件。首先创建 DocumentBuilderFactory,再通过 DocumentBuilderFactory 创建 DocumentBuilder,进而解析 inputSource 来返回 Document 对象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
\t\t\tErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

\tDocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
\tif (logger.isTraceEnabled()) {
\t\tlogger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
\t}
\tDocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
\treturn builder.parse(inputSource);
}/<code>

detectValidationMode() 方法其实就是读取文件内容,判断是否包含 DOCTYPE ,如果包含就是 DTD 否则就是 XSD。

获取 XML 配置文件的验证模式。XML 文件的验证模式是用来保证 XML 文件的正确性,常见的验证模式有 DTD 和 XSD。

DTD XML 格式示例:

Spring XML Bean 定义的加载和注册

STD XML 格式示例:

Spring XML Bean 定义的加载和注册

5.XmlBeanDefinitionReader#registerBeanDefinitions

<code>public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 获取 DefaultBeanDefinitionDocumentReader
\tBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取注册中心,再靠注册中心获取注册之前以及注册过的 BeanDefinition 数量
\tint countBefore = getRegistry().getBeanDefinitionCount();
// 解析并注册 BeanDefinition,见下文详情
\tdocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 获取注册过后 BeanDefinition 数量减去注册之前的数量,得到的就是本次注册的数量
\treturn getRegistry().getBeanDefinitionCount() - countBefore;
}/<code>

这里的 getRegistry() 方法返回的就是 DefaultListableBeanFactory ,因为就只有它实现了 BeanDefinitionRegistry 接口。

DefaultListableBeanFactory 中定义了存放 BeanDefinition 的缓存,所以 getBeanDefinitionCount() 方法返回的就是 beanDefinitionMap 的数量。

<code>// 存放 BeanDefinition 的缓存,key 为 bean 的名称,value 就是其 BeanDefinition
private final Map<string> beanDefinitionMap = new ConcurrentHashMap<>(256);/<string>/<code>

6.DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions

<code>public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
\tthis.readerContext = readerContext;
// 提取 root,注册 BeanDefinition (理论上 Spring 的配置文件,root 都应该是 beans 标签)
\tdoRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
\t// Any nested <beans> elements will cause recursion in this method. In
\t// order to propagate and preserve <beans> default-* attributes correctly,
\t// keep track of the current (parent) delegate, which may be null. Create
\t// the new (child) delegate with a reference to the parent for fallback purposes,

\t// then ultimately reset this.delegate back to its original (parent) reference.
\t// this behavior emulates a stack of delegates without actually necessitating one.
\tBeanDefinitionParserDelegate parent = this.delegate;
// 专门处理解析
\tthis.delegate = createDelegate(getReaderContext(), root, parent);
\t// 校验 root 节点的命名空间是否为默认的命名空间 (默认命名空间http://www.springframework.org/schema/beans)
\tif (this.delegate.isDefaultNamespace(root)) {
// 处理 profile 属性
\t\tString profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
\t\tif (StringUtils.hasText(profileSpec)) {
\t\t\tString[] specifiedProfiles = StringUtils.tokenizeToStringArray(
\t\t\tprofileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
\t\t\t// We cannot use Profiles.of(...) since profile expressions are not supported
\t\t\t// in XML config. See SPR-12458 for details.
// 校验当前节点的 profile 是否符合当前环境定义的,如果不是则直接跳过,不解析该节点下的内容
\t\t\tif (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
\t\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\t\tlogger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
\t\t\t\t\t\t\t"] not matching: " + getReaderContext().getResource());
\t\t\t\t}
\t\t\t\treturn;
\t\t\t}
\t\t}
\t}
\t// 解析前处理,留给子类实现
\tpreProcessXml(root);
// 解析注册 BeanDefinition,见下文详解
\tparseBeanDefinitions(root, this.delegate);
// 解析后处理,留给子类实现
\tpostProcessXml(root);

\tthis.delegate = parent;
}/<beans>/<beans>/<code>

profile 主要是用于多环境开发,例如:

Spring XML Bean 定义的加载和注册

集成到 Web 环境时,在 web.xml 中加入以下代码:

<code><coontext-param>
\t<param-name>Spring.profiles.active/<param-name>
<param-value>dev/<param-value>
/<coontext-param>/<code>

preProcessXml() 和 postProcessXml() 采用的 模板方法模式 ,子类可以继承 DefaultBeanDefinitionDoucumentReader 来重写这两个方法,这也是解析前后的扩展点。

7.DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions

<code>protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 校验 root 节点的命名空间是否为默认的命名空间,这里为什么再次效验,因为调用解析前调用了preProcessXml() 方法,可能会对节点做修改
\tif (delegate.isDefaultNamespace(root)) {
\t\tNodeList nl = root.getChildNodes();
\t\tfor (int i = 0; i < nl.getLength(); i++) {
\t\t\tNode node = nl.item(i);
\t\t\tif (node instanceof Element) {
\t\t\t\tElement ele = (Element) node;
\t\t\t\tif (delegate.isDefaultNamespace(ele)) {
// 默认命名空间节点的处理,例如 <bean>
\t\t\t\t\tparseDefaultElement(ele, delegate);
\t\t\t\t}
\t\t\t\telse {
// 自定义命名空间节点的处理,例如 <compoent-scan>、<aspectj-autoproxy>
\t\t\t\t\tdelegate.parseCustomElement(ele);
\t\t\t\t}
\t\t\t}
\t\t}
\t}
\telse {
// 自定义命名空间节点的处理

\t\tdelegate.parseCustomElement(root);
\t}
}


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 对 import 标签的处理
\tif (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
\t\timportBeanDefinitionResource(ele);
\t}
// 对 alias 标签的处理
\telse if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
\t\tprocessAliasRegistration(ele);
\t}
// 对 bean 标签的处理
\telse if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
\t\tprocessBeanDefinition(ele, delegate);
\t}
// 对 beans 标签的处理
\telse if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
\t\tdoRegisterBeanDefinitions(ele);
\t}
}/<aspectj-autoproxy>/<code>

上面 parseDefaultElement 方法中对 bean 标签的处理方法 processBeanDefinition 最为重要,下面来着重分析一下。

7-1.DefaultBeanDefinitionDocumentReader#processBeanDefinition

<code>protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 将 ele 解析成 BeanDefinitionHolder,见下面详解
\tBeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
\tif (bdHolder != null) {
// 若存在默认标签下的子节点下不再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入分析)
\t\tbdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
\t\ttry {
\t\t\t// Register the final decorated instance.
// 注册最终的 BeanDefinition
\t\t\tBeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
\t\t}

\t\tcatch (BeanDefinitionStoreException ex) {
\t\t\tgetReaderContext().error("Failed to register bean definition with name '" +
\t\t\t\t\t\tbdHolder.getBeanName() + "'", ele, ex);
\t\t}
\t\t// Send registration event.
// 发出响应事件,通知相关监听器,这个 bean 已经注册完
\t\tgetReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
\t}
}/<code>

上面代码主要步骤如下:

  1. 将 Element 解析成 BeanDefinitionHolder 。
  2. 若存在默认标签下的子节点下有自定义属性,需要再次对自定义标签再进行解析。
  3. 注册 BeanDefinition 。
  4. 发出响应事件,通知相关监听器,这个 bean 已经注册完,具体详情可以查看 ReaderEventListener#componentRegistered() 方法。可以通过以下方式去注册这个监听器:
Spring XML Bean 定义的加载和注册

7-1-1.BeanDefinitionParseDelegate#parseBeanDefinitionElement

<code>public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
// 解析元素,封装成 BeanDefinitionHolder
\treturn parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取 id 属性
\tString id = ele.getAttribute(ID_ATTRIBUTE);
// 获取 name 属性
\tString nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
\tList<string> aliases = new ArrayList<>();
// 将 name 属性所有的名称按照逗号或者分号(,;)分割成数组放入别名集合 aliases
\tif (StringUtils.hasLength(nameAttr)) {
\t\tString[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
\t\taliases.addAll(Arrays.asList(nameArr));
\t}
\t// beanName 默认使用 id
\tString beanName = id;
// 没有指定 id 属性 && 指定了 name 属性
\tif (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果没有指定id,beanName 等于第一个别名,剩下的依然作为别名使用
\t\tbeanName = aliases.remove(0);
\t\tif (logger.isTraceEnabled()) {
\t\t\tlogger.trace("No XML 'id' specified - using '" + beanName +
\t\t\t\t\t"' as bean name and " + aliases + " as aliases");
\t\t}
\t}

\tif (containingBean == null) {
// 验证 beanName 和 aliases 是否在同一个 <beans> 下已经存在
\t\tcheckNameUniqueness(beanName, aliases, ele);
\t}
\t// 将元素解析成 GenericBeanDefinition
\tAbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
\tif (beanDefinition != null) {
// 如果不存在 beanName 会根据 Spring 的命名规则生成一个

\t\tif (!StringUtils.hasText(beanName)) {
\t\t\ttry {
\t\t\t\tif (containingBean != null) {
\t\t\t\t\tbeanName = BeanDefinitionReaderUtils.generateBeanName(
\t\t\t\t\t\t\tbeanDefinition, this.readerContext.getRegistry(), true);
\t\t\t\t}
\t\t\t\telse {
\t\t\t\t\tbeanName = this.readerContext.generateBeanName(beanDefinition);
\t\t\t\t\t// Register an alias for the plain bean class name, if still possible,
\t\t\t\t\t// if the generator returned the class name plus a suffix.
\t\t\t\t\t// This is expected for Spring 1.2/2.0 backwards compatibility.
\t\t\t\t\tString beanClassName = beanDefinition.getBeanClassName();
\t\t\t\t\tif (beanClassName != null &&
\t\t\t\t\t\t\tbeanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
\t\t\t\t\t!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
\t\t\t\t\t\t\taliases.add(beanClassName);
\t\t\t\t\t}
\t\t\t\t}
\t\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\t\tlogger.trace("Neither XML 'id' nor 'name' specified - " +
\t\t\t\t\t\t\t"using generated bean name [" + beanName + "]");
\t\t\t\t}
\t\t\t}
\t\t\tcatch (Exception ex) {
\t\t\t\terror(ex.getMessage(), ele);
\t\t\t\treturn null;
\t\t\t}
\t\t}
\t\tString[] aliasesArray = StringUtils.toStringArray(aliases);
// 用 beanDefinition 和 beanName 以及 aliasesArray 构建 BeanDefinitionHolder
\t\treturn new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
\t}
\treturn null;
}


public AbstractBeanDefinition parseBeanDefinitionElement(
\t\t\tElement ele, String beanName, @Nullable BeanDefinition containingBean) {

\tthis.parseState.push(new BeanEntry(beanName));

\tString className = null;
// 获取 class 属性
\tif (ele.hasAttribute(CLASS_ATTRIBUTE)) {
\t\tclassName = ele.getAttribute(CLASS_ATTRIBUTE).trim();
\t}
\tString parent = null;
// 获取 parent 属性
\tif (ele.hasAttribute(PARENT_ATTRIBUTE)) {

\t\tparent = ele.getAttribute(PARENT_ATTRIBUTE);
\t}

\ttry {
// 创建用于承载属性的 AbstractBeanDefinition 类型的 GenericBeanDefinition
\t\tAbstractBeanDefinition bd = createBeanDefinition(className, parent);
\t\t// 解析默认 bean 的各种属性,见下方 parseBeanDefinitionAttributes 方法详解
\t\tparseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
\t\tbd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
\t\t// 解析元数据,见下方 parseMetaElements 方法详解
\t\tparseMetaElements(ele, bd);
// 解析 lookup-method 属性,很少使用,不具体介绍
\t\tparseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method 属性,很少使用,不具体介绍
\t\tparseReplacedMethodSubElements(ele, bd.getMethodOverrides());
\t\t// 解析 constructot-arg 属性,见下方 parseConstructorArgElements 方法详解
\t\tparseConstructorArgElements(ele, bd);
// 解析 property 属性,见下方 parsePropertyElements 方法详解
\t\tparsePropertyElements(ele, bd);
// 解析 qualifier 属性,见下方 parseQualifierElements 方法详解
\t\tparseQualifierElements(ele, bd);
\t\t
\t\tbd.setResource(this.readerContext.getResource());
\t\tbd.setSource(extractSource(ele));

\t\treturn bd;
\t}
\tcatch (ClassNotFoundException ex) {
\t\terror("Bean class [" + className + "] not found", ele, ex);
\t}
\tcatch (NoClassDefFoundError err) {
\t\terror("Class that bean class [" + className + "] depends on not found", ele, err);
\t}
\tcatch (Throwable ex) {
\t\terror("Unexpected failure during bean definition parsing", ele, ex);
\t}
\tfinally {
\t\tthis.parseState.pop();

\t}

\treturn null;
}/<beans>/<string>/<code>

上面代码主要将 bean 标签,解析为 BeanDefinitionHolder 返回,主要步骤如下:

  1. 解析 id 、 name 属性,将 name 按照 , 或者 ; 分割作为别名 ( alias )。
  2. 解析剩下的属性,并封装成 GenericBeanDefinition 。调用 parseBeanDefinitionAttributes 方法解析 bean 标签的所有属性。调用 parseMetaElements 方法解析元数据信息。调用 parseLookupOverrideSubElements 方法解析 lookup-method 子标签。调用 parseReplacedMethodSubElements 方法解析 replaced-method 子标签。调用 parseConstructorArgElements 方法解析 constructor-arg 子标签。调用 parsePropertyElements 方法解析 property 子标签。调用 parseQualifierElements 方法解析 qualifier 子标签。
  3. 判断 beanName 是否存在,不存在会根据 Spring 的命名规则生成一个。
  4. 使用 beanDefinition 、 beanName 、 aliasesArray 构建 BeanDefinitionHolder 返回。

我们这边可以简单看一下 BeanDefinitionHolder 的属性,如下:

<code>public class BeanDefinitionHolder implements BeanMetadataElement {
\t
// bean 的定义元信息
\tprivate final BeanDefinition beanDefinition;
\t// bean 的名称
\tprivate final String beanName;
\t// bean 的别名数组
\t@Nullable
\tprivate final String[] aliases;

...省略其它代码

}/<code>

BeanDefinitionHolder 其实就是对 BeanDefinition 的包装。

parseBeanDefinitionAttributes

<code>public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
\t\t\t@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
\t// 解析 singleton 属性
\tif (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton 属性已经不支持了,使用了会直接抛出异常,请使用 scope 属性替代
\t\terror("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
\t}
// 解析 scope 属性
\telse if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
\t\tbd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
\t}
\telse if (containingBean != null) {
\t\t// Take default from containing bean in case of an inner bean definition.
\t\tbd.setScope(containingBean.getScope());
\t}
\t// 解析 abstract 属性
\tif (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
\t\tbd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
\t}
\t// 解析 lazy 属性
\tString lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
// 若没有设置或者设置成其他字符都会被设置为默认值 false
\tif (isDefaultValue(lazyInit)) {
\t\tlazyInit = this.defaults.getLazyInit();
\t}
\tbd.setLazyInit(TRUE_VALUE.equals(lazyInit));
\t// 解析 autowire 属性
\tString autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
\tbd.setAutowireMode(getAutowireMode(autowire));
\t// 解析 depends-on 属性
\tif (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
\t\tString dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
\t\tbd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
\t}
\t// 解析 autowire-candidate 属性
\tString autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);

\tif (isDefaultValue(autowireCandidate)) {
\t\tString candidatePattern = this.defaults.getAutowireCandidates();
\t\tif (candidatePattern != null) {
\t\t\tString[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
\t\t\tbd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
\t\t}
\t}
\telse {
\t\tbd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
\t}
\t// 解析 primary 属性
\tif (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
\t\tbd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
\t}
\t// 解析 init-mehtod 属性
\tif (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
\t\tString initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
\t\tbd.setInitMethodName(initMethodName);
\t}
// 如果 beans 标签指定了 default-init-method 属性,则会给所有此标签下的 bean 都指定该 init-method
\telse if (this.defaults.getInitMethod() != null) {
\t\tbd.setInitMethodName(this.defaults.getInitMethod());
\t\tbd.setEnforceInitMethod(false);
\t}
\t// 解析 destory-method 属性
\tif (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
\t\tString destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
\t\tbd.setDestroyMethodName(destroyMethodName);
\t}
// 如果 beans 标签指定了 default-destory-method 属性,则会给所有此标签下的 bean 都指定该 destory-method
\telse if (this.defaults.getDestroyMethod() != null) {
\t\tbd.setDestroyMethodName(this.defaults.getDestroyMethod());
\t\tbd.setEnforceDestroyMethod(false);
\t}
\t// 解析 factory-method 属性
\tif (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
\t\tbd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
\t}
// 解析 factory-bean 属性
\tif (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
\t\tbd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
\t}
\t
\treturn bd;

}/<code>

上面方法完成了对所有 bean 标签属性的解析。值得注意的地方是如果同时指定了 bean 标签的 init-method 和 beans 标签的 default-init-method 属性,那么优先使用前者, destory-mehtod 标签也是一样。

大家可以去看一下 AbstractBeanDefinition 中定义的属性就一目了然了,这里限于篇幅原因就不展示了。

parseMetaElements

这里先回顾一下元数据 meta 属性的使用。

Spring XML Bean 定义的加载和注册

这个属性并不会体现在 user 的属性当中,而是一个额外的声明,当需要使用里面的信息时可以通过 BeanDefinition#getAttribute(key) 来获取。

<code>public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
// 获取所有子节点
\tNodeList nl = ele.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 提取 meta
\t\tif (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
\t\t\tElement metaElement = (Element) node;
\t\t\tString key = metaElement.getAttribute(KEY_ATTRIBUTE);
\t\t\tString value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 使用 key、value 构造 BeanMetadataAttribute
\t\t\tBeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
\t\t\tattribute.setSource(extractSource(metaElement));
// 记录信息
\t\t\tattributeAccessor.addMetadataAttribute(attribute);
\t\t}
\t}
}/<code>

parseConstructorArgElements

<code>public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 获取所有子节点
\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 提取 constructor-arg
\t\tif (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
\t\t\tparseConstructorArgElement((Element) node, bd);
\t\t}
\t}
}
// <constructor-arg>
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取 index 属性
\tString indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取 type 属性
\tString typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取 name 属性

\tString nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// index 不为空
\tif (StringUtils.hasLength(indexAttr)) {
\t\ttry {
// 转换为 int 类型
\t\t\tint index = Integer.parseInt(indexAttr);
\t\t\tif (index < 0) {
\t\t\t\terror("'index' cannot be lower than 0", ele);
\t\t\t}
\t\t\telse {
\t\t\t\ttry {
\t\t\t\t\tthis.parseState.push(new ConstructorArgumentEntry(index));
// 解析属性值,见下面详解
\t\t\t\t\tObject value = parsePropertyValue(ele, bd, null);
// 使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素
\t\t\t\t\tConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 如果 type 不为空,设置 ValueHolder 的 type
\t\t\t\t\tif (StringUtils.hasLength(typeAttr)) {
\t\t\t\t\t\tvalueHolder.setType(typeAttr);
\t\t\t\t\t}
// 如果 name 不为空,设置 ValueHolder 的 name
\t\t\t\t\tif (StringUtils.hasLength(nameAttr)) {
\t\t\t\t\t\tvalueHolder.setName(nameAttr);
\t\t\t\t\t}
\t\t\t\t\tvalueHolder.setSource(extractSource(ele));
// 判断 index 是否重复,重复则抛出异常
\t\t\t\t\tif (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {\t\t\t\t\t\t\terror("Ambiguous constructor-arg entries for index " + index, ele);
\t\t\t\t\t}
// 将 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 当中
\t\t\t\t\telse {
\t\t\t\t\t\tbd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
\t\t\t\t\t}
\t\t\t\t}
\t\t\t\tfinally {
\t\t\t\t\tthis.parseState.pop();
\t\t\t\t}
\t\t\t}
\t\t}
\t\tcatch (NumberFormatException ex) {
\t\t\terror("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
\t\t}
\t}
\telse {

\t\ttry {
// 这里就是 constructor-arg 标签中没有指定 index 属性
\t\t\tthis.parseState.push(new ConstructorArgumentEntry());
\t\t\tObject value = parsePropertyValue(ele, bd, null);
\t\t\tConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
\t\t\tif (StringUtils.hasLength(typeAttr)) {
\t\t\t\tvalueHolder.setType(typeAttr);
\t\t\t}
\t\t\tif (StringUtils.hasLength(nameAttr)) {
\t\t\t\tvalueHolder.setName(nameAttr);
\t\t\t}
\t\t\tvalueHolder.setSource(extractSource(ele));
// 将 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
// 这里和上面的 IndexedArgumentValue 类似,只不过上面是 Map,这里是 List
\t\t\tbd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
\t\t}
\t\tfinally {
\t\t\tthis.parseState.pop();
\t\t}
\t}
}/<code>

上面代码首先提取 constructor-arg 标签中必要的属性 ( index 、 type 、 name )。

  • 如果指定了 index 属性:解析 constructor-arg 的子元素。使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。将 type 、 name 和 index 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinition 的 ConstructorArgumentValues 中的 LinkedHashMap 类型的属性 indexedArgumentValues 中。
  • 如果有指定 index 属性:解析 constructor-arg 的子元素。使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。将 type 、 name 和 index 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinition 的 ConstructorArgumentValues 中的 ArrayList 类型的属性 genericArgumentValues 中。

parsePropertyValue

<code>public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { 

\tString elementName = (propertyName != null ?
\t\t\t"<property> element for property '" + propertyName + "'" :
\t\t\t"<constructor-arg> element");

\t// Should only have one child element: ref, value, list, etc.
// 获取所有子节点,例如 list、map等
\tNodeList nl = ele.getChildNodes();
\tElement subElement = null;
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
// 跳过 description 或者 meta 不处理
\t\tif (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
\t\t\t\t!nodeNameEquals(node, META_ELEMENT)) {
\t\t\t// Child element is what we're looking for.
\t\t\tif (subElement != null) {
\t\t\t\terror(elementName + " must not contain more than one sub-element", ele);
\t\t\t}
\t\t\telse {
\t\t\t\tsubElement = (Element) node;
\t\t\t}
\t\t}
\t}
\t// 提取 ref 属性
\tboolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 提取 value 属性
\tboolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 如果同时有 ref 和 value 属性 || 有 ref 或 value 属性同时又有子元素,抛出异常
\tif ((hasRefAttribute && hasValueAttribute) ||
\t\t\t((hasRefAttribute || hasValueAttribute) && subElement != null)) {
\t\terror(elementName +
\t\t\t\t" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
\t}
\t// 只有 ref 属性,使用 RuntimeBeanReference 封装对应的 ref 名称 (该 ref 值指向另一个 bean 的 beanName)
// RuntimeBeanReference 起到占位符的作用, ref 指向的 beanName 将在运行时被解析成真正的 bean 实例引用
\tif (hasRefAttribute) {
\t\tString refName = ele.getAttribute(REF_ATTRIBUTE);
\t\tif (!StringUtils.hasText(refName)) {
\t\t\terror(elementName + " contains empty 'ref' attribute", ele);
\t\t}
\t\tRuntimeBeanReference ref = new RuntimeBeanReference(refName);

\t\tref.setSource(extractSource(ele));
\t\treturn ref;
\t}
// 只有 value 属性,使用 TypedStringValue 封装
\telse if (hasValueAttribute) {
\t\tTypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
\t\tvalueHolder.setSource(extractSource(ele));
\t\treturn valueHolder;
\t}
\telse if (subElement != null) {
// 解析子元素
\t\treturn parsePropertySubElement(subElement, bd);
\t}
\telse {
\t\t// Neither child element nor "ref" or "value" attribute found.
// 没有子元素,也没有 ref 和 value,直接抛出异常
\t\terror(elementName + " must specify a ref or value", ele);
\t\treturn null;
\t}
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
\treturn parsePropertySubElement(ele, bd, null);
}

public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 校验是否为默认的命名空间,如果不是则走解析自定义节点代码
\tif (!isDefaultNamespace(ele)) {
\t\treturn parseNestedCustomElement(ele, bd);
\t}
// 解析 bean 节点
\telse if (nodeNameEquals(ele, BEAN_ELEMENT)) {
\t\tBeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
\t\tif (nestedBd != null) {
\t\t\tnestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
\t\t}
\t\treturn nestedBd;
\t}
// 解析 ref 节点
\telse if (nodeNameEquals(ele, REF_ELEMENT)) {
\t\t// A generic reference to any name of any bean.
\t\tString refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
\t\tboolean toParent = false;
\t\tif (!StringUtils.hasLength(refName)) {

\t\t\t// A reference to the id of another bean in a parent context.
\t\t\trefName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
\t\t\ttoParent = true;
\t\t\tif (!StringUtils.hasLength(refName)) {
\t\t\t\terror("'bean' or 'parent' is required for element", ele);
\t\t\t\treturn null;
\t\t\t}
\t\t}
\t\tif (!StringUtils.hasText(refName)) {
\t\t\terror(" element contains empty target attribute", ele);
\t\t\treturn null;
\t\t}
\t\tRuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
\t\tref.setSource(extractSource(ele));
\t\treturn ref;
\t}
// 解析 idref 节点
\telse if (nodeNameEquals(ele, IDREF_ELEMENT)) {
\t\treturn parseIdRefElement(ele);
\t}
// 解析 value 节点
\telse if (nodeNameEquals(ele, VALUE_ELEMENT)) {
\t\treturn parseValueElement(ele, defaultValueType);
\t}
// 解析 null 节点
\telse if (nodeNameEquals(ele, NULL_ELEMENT)) {
\t\t// It's a distinguished null value. Let's wrap it in a TypedStringValue
\t\t// object in order to preserve the source location.
\t\tTypedStringValue nullHolder = new TypedStringValue(null);
\t\tnullHolder.setSource(extractSource(ele));
\t\treturn nullHolder;
\t}
// 解析 array 节点
\telse if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
\t\treturn parseArrayElement(ele, bd);
\t}
// 解析 list 节点
\telse if (nodeNameEquals(ele, LIST_ELEMENT)) {
\t\treturn parseListElement(ele, bd);
\t}
// 解析 set 节点
\telse if (nodeNameEquals(ele, SET_ELEMENT)) {
\t\treturn parseSetElement(ele, bd);
\t}
// 解析 map 节点
\telse if (nodeNameEquals(ele, MAP_ELEMENT)) {

\t\treturn parseMapElement(ele, bd);
\t}
// 解析 props 节点
\telse if (nodeNameEquals(ele, PROPS_ELEMENT)) {
\t\treturn parsePropsElement(ele);
\t}
// 未知属性,抛出异常
\telse {
\t\terror("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
\t\treturn null;
\t}
}
/<constructor-arg>/<property>/<code>

从上面的代码来看,对构造函数中属性元素的解析,步骤如下:

  1. 略过 description 或者 meta 。
  2. 提取 constructor-arg 上的 ref 和 value 属性,以便于根据规则验证正确性。其规则为在 constructor-arg 上不存在一下情况:同时存在 ref 和 value 属性。存在 ref 或者 value 属性,并且又有子元素。

parsePropertyElements

<code>public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取所有子节点
\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
\t\tif (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
// 解析 property 节点
\t\t\tparsePropertyElement((Element) node, bd);
\t\t}
\t}
}

// 这里是解析 property 标签,<property>

public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取 name 属性
\tString propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// name 为空,抛出异常
\tif (!StringUtils.hasLength(propertyName)) {
\t\terror("Tag 'property' must have a 'name' attribute", ele);
\t\treturn;
\t}
\tthis.parseState.push(new PropertyEntry(propertyName));
\ttry {
\t// 出现两个 name 相同的抛出异常
\t\tif (bd.getPropertyValues().contains(propertyName)) {
\t\t\terror("Multiple 'property' definitions for property '" + propertyName + "'", ele);
\t\t\treturn;
\t\t}
// 解析属性值,跟构造器解析一样,查看上方代码
\t\tObject val = parsePropertyValue(ele, bd, propertyName);
// 用 name 和 val 封装成 PropertyValue
\t\tPropertyValue pv = new PropertyValue(propertyName, val);
// 解析元数据,跟 beans 标签内的 meta 一样
\t\tparseMetaElements(ele, pv);
\t\tpv.setSource(extractSource(ele));
// 添加到 BeanDefiniton 的 PropertyValues 属性中
\t\tbd.getPropertyValues().addPropertyValue(pv);
\t}
\tfinally {
\t\tthis.parseState.pop();
\t}
}/<code>

上面方法主要是遍历 property 节点,然后解析属性值封装成 PropertyValue 添加到 BeanDefinition 的 PropertyValues 中。

注意: property 节点类似于 POJO 中的 set 方法, bean 中的属性必需有 set 方法否则会抛出异常。

parseQualifierElements

<code>public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
// 获取子节点

\tNodeList nl = beanEle.getChildNodes();
\tfor (int i = 0; i < nl.getLength(); i++) {
\t\tNode node = nl.item(i);
\t\tif (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
// 解析 qualifier 节点
\t\t\tparseQualifierElement((Element) node, bd);
\t\t}
\t}
}

public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 提取 type
\tString typeName = ele.getAttribute(TYPE_ATTRIBUTE);
// type 为空抛出异常
\tif (!StringUtils.hasLength(typeName)) {
\t\terror("Tag 'qualifier' must have a 'type' attribute", ele);
\t\treturn;
\t}
\tthis.parseState.push(new QualifierEntry(typeName));
\ttry {
\t\tAutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
\t\tqualifier.setSource(extractSource(ele));
// 提取 value
\t\tString value = ele.getAttribute(VALUE_ATTRIBUTE);
// value 不为空,设置到 AutowireCandidateQualifier 的 attribute 中
\t\tif (StringUtils.hasLength(value)) {
\t\t\tqualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
\t\t}
// 获取子节点
\t\tNodeList nl = ele.getChildNodes();
\t\tfor (int i = 0; i < nl.getLength(); i++) {
\t\t\tNode node = nl.item(i);
// 如果是有 attribute 节点,进行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
\t\t\tif (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
\t\t\t\tElement attributeEle = (Element) node;
\t\t\t\tString attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
\t\t\t\tString attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
\t\t\t\tif (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
\t\t\t\t\tBeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
\t\t\t\t\tattribute.setSource(extractSource(attributeEle));
\t\t\t\t\tqualifier.addMetadataAttribute(attribute);
\t\t\t\t}
\t\t\t\telse {
\t\t\t\t\terror("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
\t\t\t\t\treturn;
\t\t\t\t}

\t\t\t}
\t\t}
// 设置 BeanDefinition 的 qualifier
\t\tbd.addQualifier(qualifier);
\t}
\tfinally {
\t\tthis.parseState.pop();
\t}
}/<code>

对于 qualifier 元素的获取,我们大多数接触的更多是注解的形式,在使用 Spring 框架中进行自动注入时,Spring 容器中匹配的候选 Bean 必需有且只有一个。如果存在多个类型相同的 Bean ,且按照类型注入时,Spirng 允许通过 Qualifier 指定注入 Bean 的名称,这样歧义就消除了。

7-1-2.BeanDefinitionReaderUtils#registerBeanDefinition

<code>public static void registerBeanDefinition(
\t\t\tBeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
\t\t\tthrows BeanDefinitionStoreException {

\t// Register bean definition under primary name.
// 获取 beanName
\tString beanName = definitionHolder.getBeanName();
// 以 key-value 的形式注册,key 为 beanName,value 为 BeanDefinition。见下文详解
\tregistry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

\t// Register aliases for bean name, if any.
// 注册 bean 的别名
\tString[] aliases = definitionHolder.getAliases();
\tif (aliases != null) {
\t\tfor (String alias : aliases) {
// 以 key-value 的形式注册 bean 的别名,key 为别名,value 为 beanName。见下文详解
\t\t\tregistry.registerAlias(beanName, alias);
\t\t}
\t}
}

// DefaultListableBeanFactory.java

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
\t\t\tthrows BeanDefinitionStoreException {

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

\tif (beanDefinition instanceof AbstractBeanDefinition) {
\t\ttry {
\t\t\t// 验证 Bean 的格式是否正确
\t\t\t((AbstractBeanDefinition) beanDefinition).validate();
\t\t}
\t\tcatch (BeanDefinitionValidationException ex) {
\t\t\tthrow new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
\t\t\t\t\t"Validation of bean definition failed", ex);
\t\t}
\t}

\tBeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
\t// 这里判断 BeanDefinition 是否存在
\tif (existingDefinition != null) {
\t\t// 这里就是如果 Bean 定义以及存在,判断是否可以覆盖,默认是可以的
\t\t// Spring Boot 2.1开始这里会手动设置 allowBeanDefinitionOverriding 的值为 false
\t\tif (!isAllowBeanDefinitionOverriding()) {
\t\t\tthrow new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
\t\t}
\t\telse if (existingDefinition.getRole() < beanDefinition.getRole()) {
\t\t\t// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
\t\t\tif (logger.isInfoEnabled()) {
\t\t\t\tlogger.info("Overriding user-defined bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with a framework-generated bean definition: replacing [" +
\t\t\t\t\t\texistingDefinition + "] with [" + beanDefinition + "]");
\t\t\t}
\t\t}
\t\telse if (!beanDefinition.equals(existingDefinition)) {
\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\tlogger.debug("Overriding bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with a different definition: replacing [" + existingDefinition +
\t\t\t\t\t\t"] with [" + beanDefinition + "]");
\t\t\t}
\t\t}
\t\telse {
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Overriding bean definition for bean '" + beanName +
\t\t\t\t\t\t"' with an equivalent definition: replacing [" + existingDefinition +
\t\t\t\t\t\t"] with [" + beanDefinition + "]");

\t\t\t}
\t\t}
// 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t}
\telse {
\t\t// bean 是否已经开始创建
\t\tif (hasBeanCreationStarted()) {
\t\t\t// Cannot modify startup-time collection elements anymore (for stable iteration)
\t\t\tsynchronized (this.beanDefinitionMap) {
// 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
\t\t\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t\t\t\t// 这里将 beanDefinitionNames 写时复制一份,类似于 CopyOnWriteArrayList
\t\t\t\tList<string> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
\t\t\t\tupdatedDefinitions.addAll(this.beanDefinitionNames);
\t\t\t\tupdatedDefinitions.add(beanName);
\t\t\t\tthis.beanDefinitionNames = updatedDefinitions;
\t\t\t\t// 从单例 Bean 注册名称列表中删除当前 beanName
\t\t\t\tremoveManualSingletonName(beanName);
\t\t\t}
\t\t}
// bean 不在创建状态中
\t\telse {
\t\t\t// Still in startup registration phase
\t\t\tthis.beanDefinitionMap.put(beanName, beanDefinition);
\t\t\t// 因为 ConcurrentHashMap 是无序的,这里将 beanName 放入 ArrayList,记录注册顺序
\t\t\tthis.beanDefinitionNames.add(beanName);
\t\t\t// 从单例 Bean 注册名称列表中删除当前 beanName
\t\t\tremoveManualSingletonName(beanName);
\t\t}
\t\tthis.frozenBeanDefinitionNames = null;
\t}
\t// 如果存在相同的 beanName 的 BeanDefinition,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象

\tif (existingDefinition != null || containsSingleton(beanName)) {
\t\tresetBeanDefinition(beanName);
\t}
}

// SimpleAliasRegistry.java
public void registerAlias(String name, String alias) {
\tAssert.hasText(name, "'name' must not be empty");
\tAssert.hasText(alias, "'alias' must not be empty");
\tsynchronized (this.aliasMap) {
// 如果别名和 beanName 相同,从缓存中移除
\t\tif (alias.equals(name)) {
\t\t\tthis.aliasMap.remove(alias);
\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\tlogger.debug("Alias definition '" + alias + "' ignored since it points to same name");
\t\t\t}
\t\t}
\t\telse {
\t\t\tString registeredName = this.aliasMap.get(alias);
// 如果别名以及注册过,直接返回
\t\t\tif (registeredName != null) {
\t\t\t\tif (registeredName.equals(name)) {
\t\t\t\t\t// An existing alias - no need to re-register
\t\t\t\t\treturn;
\t\t\t\t}
// 如果不允许覆盖,抛出异常
\t\t\t\tif (!allowAliasOverriding()) {
\t\t\t\t\tthrow new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
\t\t\t\t\t\t\tname + "': It is already registered for name '" + registeredName + "'.");
\t\t\t\t}
\t\t\t\tif (logger.isDebugEnabled()) {
\t\t\t\t\tlogger.debug("Overriding alias '" + alias + "' definition for registered name '" +
\t\t\t\t\t\t\tregisteredName + "' with new target name '" + name + "'");
\t\t\t\t}
\t\t\t}
// 检查 name 和 alias 是否存在循环引用。例如 A 的别名为 B,B的别名为A
\t\t\tcheckForAliasCircle(name, alias);
// 将 alias 和 name 以 key-value 对放入到 aliasMap 中,进行缓存
\t\t\tthis.aliasMap.put(alias, name);
\t\t\tif (logger.isTraceEnabled()) {
\t\t\t\tlogger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
\t\t\t}
\t\t}

\t}
}/<string>/<code>

上面代码有两个变量比较重要 beanDefinitionMap 和 beanDefinitionNames ,下面代码是这两个属性在 DefaultListableBeanFactory 中的定义:

<code>/** Map of bean definition objects, keyed by bean name. */
// 缓存 BeanDefinition 的 Map,key 为 beanName,value 为 BeanDefinition
private final Map<string> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存 BeanDefinition 的注册顺序,保存的是 beanName
/** List of bean definition names, in registration order. */
private volatile List<string> beanDefinitionNames = new ArrayList<>(256);/<string>/<string>/<code>
  1. 如果 BeanDefinition 是 AbstractBeanDefinition 类型,验证 Bean 的格式是否正确。这次效验主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 的校验,校验 methodOverrides 是否与 工厂方法 并存或者 methodOverrides 中的方法根本不存在。
  2. 判断该 beanName 的 BeanDefinition 是否已经注册过;如果存在判断是否允许覆盖,允许的话直接替换,不允许直接抛出异常。默认的情况下是允许的,但是在 Spring Boot 2.1 开始这里会手动的设置为不允许。
  3. beanName 对应的 BeanDefinition 以前没有注册过,判断 bean 是否已经开始创建;如果在创建中对 beanDefinitionMap 进行加锁 (这里虽然 beanDefinitionMap 是线程安全的 ConcurrentHashMap ,单个操作是线程安全的但多个操作不是,所以这里手动加锁),然后将 beanName 和 BeanDefinition 以 key-value 形式放入 beanDefinitionMap 缓存中,然后写时复制一份 beanDefinitionNames ,将 beaName 缓存进去,记录 bean 的注册顺序;如果不在创建直接将 BeanDefinition 和 beanName 分别放入 beanDefinitionMap 和 beanDefinitionNames 中。
  4. 最后判断如果 BeanDefinition 已经注册过,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象。

总结

本文主要介绍了通过 XML 文件的方式注册 Bean ,我们可以重新梳理一下思路:

  1. 解析 XML 文件,构建成 AbstractBeanDefinition (GenericBeanDefinition) 对象来存放所有解析出来的属性。
  2. 将 AbstractBeanDefinition 、 beanName 、 aliasesArray 构建成 BeanDefinitionHolder 对象。
  3. 最后通过 BeanDefinitionHolder 将 beanName 和 BeanDefinition 注册到 DefaultListableBeanFactory 中,也就是保存起来。上文提到的两个比较重要的属性 beanDefinitionNames 和 beanDefinitionMap ,在后面都会多次用到,可以重点关注一下。


原文 http://www.cnblogs.com/yijinqincai/p/12701046.html


分享到:


相關文章: