Spring源码笔记之SpringIOC--(1)从XML文件到Bean的描述对象BeanDefinition
从XML文件到Bean的描述对象BeanDefinition
最开始学习spring的入门实践是,编写一个xml文件,然后利用spring读取xml文件中配置的bean。
编写一个xml配置文件default.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/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">
<bean name="myBean" class="org.numb.springframework.ioc.bean.MyBean"/>
</beans>
编写代码加载这个bean,并获取bean的实例
public void test() {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("default.xml"));
MyBean myBean = (MyBean) beanFactory.getBean("myBean");
}
至此就实现了springIOC加载一个bean的功能。其实在Spring代码中提供了现有的测试用例,可以从测试用例入手了解源码。注意XmlBeanFactory
自spring 3.x后逐步废弃,使用XmlBeanDefinitionReader
替代。查看其测试类XmlBeanDefinitionReaderTests
的
withClassPathResource()
测试方法
class XmlBeanDefinitionReaderTests {
private final SimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
@Test
void withClassPathResource() {
// 读取test.xml的资源文件
Resource resource = new ClassPathResource("test.xml", getClass());
// 加载资源
reader.loadBeanDefinitions(resource);
// 断言检查
assertThat(registry.getBeanDefinition("rod").getBeanClassName()).isEqualTo(TestBean.class.getName());
}
}
上述测试用例中,spring使用ClassPathResource
读取bean定义test.xml
文件,然后使用XmlBeanDefinitionReader
加载资源,加载过程中会解析test.xml
,单步调试一下loadBeanDefinitions
方法的调用过程
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 为了适配字符集与编码,统一交由EncodedResource来读取资源
return loadBeanDefinitions(new EncodedResource(resource));
}
首先将Resource->EncodedResource来加载,EncodedResource内部主要适配了编码和字符集,这步不用太关心。后面使用EncodedResource加载,这里借助NamedThreadLocal
获取当前待加载的资源currentResources
,然后判断资源是否能add到currentResources
的set中,如果能add说明Resource未被加载,等加载完成后再remove,避免重复加载相同的Resource。
重点在于doLoadBeanDefinitions()
方法
BeanDefinitionDocumentReader
接口根据document
读取Bean的定义,并注册进XmlReaderContext
中,XmlReaderContext
内包含了xml加载的Resource
、XmlBeanDefinitionReader
等必要对象的封装。具体bean的加载实现在DefaultBeanDefinitionDocumentReader
中的doRegisterBeanDefinitions()
。此处doRegisterBeanDefinitions()
会被递归调用,BeanDefinitionParserDelegate是类内的对象,所以为了保证递归调用时类内能够使用正确的Delegate,此处createDelegate()
会模拟堆栈调用过程,将parent delegate传入生成当前的delegate。
parseBeanDefinitions()
方法是具体将xml定义解析的实现
到此为止,spring的XmlBeanDefinitionReader
将xml文件使用Resource
读取,并解析为Bean的定义为BeanDefinition
,然后注册入XmlReaderContext.BeanDefinitionRegistry
中,中间类的调用关系如下。
去除各个子类实现类,以及xxxUtil
、xxxHolder
、xxxConetxt
等工具类,简单罗列一下顶层接口的交互过程
spring定义了几个bean操作的顶层接口BeanDefinitionRegistry
、BeanDefinitionReader
、BeanDefinitionReader
和Resource
,上述过程完成了XML资源文件到Bean的描述对象BeanDefinition
的注册过程,但是实际使用IOC过程中,并不是直接使用BeanDefinition
,而是直接获取bean实例,可以查看测试用例XmlBeanDefinitionReaderTests
的doTestValidation()
方法
private void doTestValidation(String resourceName) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
Resource resource = new ClassPathResource(resourceName, getClass());
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(resource);
assertThat((TestBean) factory.getBean("testBean")).isNotNull();
}
对比此处与上面的不同之处在于,此处XmlBeanDefinitionReader
的入参由SimpleBeanDefinitionRegistry
变为DefaultListableBeanFactory
。DefaultListableBeanFactory
中提供方法factory.getBean("testBean")
直接获取名为testBean
的bean对象,而不是获取BeanDefinition
。
- 点赞
- 收藏
- 关注作者
评论(0)