Spring通过Xml方式注册Bean的几处关键实现点

Posted 沉迷Spring

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring通过Xml方式注册Bean的几处关键实现点相关的知识,希望对你有一定的参考价值。

1.前言

我们用SpringMVC的时候一般会用到Xml配置文件,那么我们这篇文章就来谈下Spring读取Xml配置文件的一些关键实现点。

2.AbstractRefreshableApplicationContext

这个抽象类的名称告诉我们这是一个具备可刷新能力的应用上下文,并且里面定义了一个关键接口loadBeanDefinitions

/**
* Load bean definitions into the given bean factory, typically through
* delegating to one or more bean definition readers.
*
@param beanFactory the bean factory to load bean definitions into
*
@throws BeansException if parsing of the bean definitions failed
*
@throws IOException if loading of bean definition files failed
*
@see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
*
@see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*/

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

我们从这个接口名称就可以看出是用来加载BeanDefinition的,并且参数传入了一个很重要的东西BeanFactory,那么这个接口由谁来实现了呢?

2.AbstractXmlApplicationContext

    /**
* Loads the bean definitions via an XmlBeanDefinitionReader.
*
@see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*
@see #initBeanDefinitionReader
*
@see #loadBeanDefinitions
*/

//todo 重载了 AbstractRefreshableApplicationContext
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.

//todo 传入bean工厂 使得beanDefinitionReader有了 Environment 2020-09-11
//todo 使得beanDefinitionReader有了 BeanRegistry 2020-09-11
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's
// resource loading environment.
//todo beanDefinitionReader 又设置了 Environment 把当前Context的环境 给了他 2020-09-11
beanDefinitionReader.setEnvironment(this.getEnvironment());

//todo important 设置为ClassPathXmlApplicationContext 又因为AbstractApplicationContext继承自 DefaultResourceLoader 为自己 2020-08-31
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//初始化beanReader
initBeanDefinitionReader(beanDefinitionReader);
//加载bean定义
loadBeanDefinitions(beanDefinitionReader);
}

这个loadBeanDefinitions接口的实现做了几件事情,我们来分析下:1.每次调用都会实例化一个XmlBeanDefinitionReader对象,并且把接口的参数beanFactory都放入了它的构造函数。我们看XmlBeanDefinitionReader类的构造函数实际上是接收一个BeanDefinitionRegistry接口的,也就是说DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这个实现就为之后解析Bean配置并且用通过这个registry注册BeanDefinition提供了可能。至于BeanDefinitionRegistry接口我们可以之后单独写文来分析,在这里就不展开讨论。2.把当前应用上下文的Envrionment环境给了XmlBeanDefinitionReader,这点也很重要,我们还记得可以在Xml配置文件中定义bean的profile配置来开发环境来筛选吗?我们来截张图:看红圈圈出来的地方,beanDefinitionReader.setEnvironment就让这个功能实现得到了基础保障,因为我们肯定首先要有应用上下文的环境嘛,具体这个profile小配置的解析之后我们用个简短的小文章来揭秘下。3.设置了beanDefinitionReader的ResourceLoader为当前应用上下文。4.设置了entityResolver,先不管。5.初始化了beanDefinitionReader,默认设置了下验证模式,先不用管。6.开始加载beanDefinitions,很重要。

最后一步最终调用到了beanDefinitionReader的registerBeanDefinitions这个方法。

3.XmlBeanDefinitionReader

/**
* Register the bean definitions contained in the given DOM document.
* Called by {
@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {
@code registerBeanDefinitions} on it.
*
@param doc the DOM document
*
@param resource the resource descriptor (for context information)
*
@return the number of bean definitions found
*
@throws BeanDefinitionStoreException in case of parsing errors
*
@see #loadBeanDefinitions
*
@see #setDocumentReaderClass
*
@see BeanDefinitionDocumentReader#registerBeanDefinitions
*/

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

//默认是DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//https://www.cnblogs.com/whx7762/p/7777775.html
//todo 里面最终调用了registry.registerBeanDefinition 2020-08-31
//todo createReaderContext new 一个XmlReaderContext 构造函数会传入 当前 XmlBeanDefinitionReader(继承自 AbstractBeanDefinitionReader 实现了 EnvironmentCapable 接口) 所以有了 Environment 2020-10-18
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
  1. 每次调用都会创建一个BeanDefinitionDocumentReader对象,默认就是在XmlBeanDefinitionReader中定义好的DefaultBeanDefinitionDocumentReader类。

  2. 获取registry中的BeanDefinition数量。

  3. 来来来!最重要一步就是通过创建的documentReader对象来注册beanDefinition了,传入了要解析的文档, 创建带有resource资源的readerContext给到我们的documentReader对象。

  4. 返回注册过后当前的beanDefinitionCount。


以上是关于Spring通过Xml方式注册Bean的几处关键实现点的主要内容,如果未能解决你的问题,请参考以下文章

spring初识--bean的几种注册方式

SpringBoot 获取上下文,获取bean的几种中方式

Spring在代码中获取bean的几种方式

spring Bean装配的几种方式简单介绍

Spring在代码中获取bean的几种方式(转)

@Configuration和@bean给容器中注册组件