本文对 Bean
容器的加载过程进行分析。首先我们看一个简单的例子。
// MyTestBean.java
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
// BeanFactoryTest.java
public class BeanFactoryTest {
@Test
public void testSimpleLoad() {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
MyTestBean bean = (MyTestBean) factory.getBean("myTestBean");
System.out.println(bean.getTestStr());
assertEquals("testStr", bean.getTestStr());
}
}
<!-- beanFactoryTest.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-3.2.xsd">
<bean id="myTestBean" class="org.springframework.mytest.MyTestBean"/>
</beans>
这是一个简单的例子,里面的具体内容可以分为以下几个部分说明。
1、对 XmlBeanFactory 的初始化
通过 ClassPathResource
提供 xml 资源,在初始化 XmlBeanFactory
的同时解析 xml 文件中的内容。
2、准备资源文件并注册Bean
- 首先封装资源文件,将参数 Resource 通过 EncodedResource 进行封装。
- 再从 Resource 中获取 InputStream 并转换为 InputSource
- 最后执行注册,其中核心有三点:获取XML文件的验证格式;加载XML文件获取Document;根据Document注册Bean。
获取XML文件的验证格式
XML 文件有两种类型:DTD 和 XSD 两种。Spring 通过判断 XML 文件是否有 DOCTYPE,如果有那就是 DTD,否则是 XSD。
获取Document
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
该函数完成了 Document 的加载。通过通用套路 SAX 解析 XML 文档。首先创建 DocumentBuilderFactory,再通过 DocumentBuilderFactory 创建 DocumentBuilder,进而解析 inputSource 来返回 Document 对象。
这里需要解释一下
EntityResolver
的作用。
通常来说,都是通过 XML 中定义的 DTD 或者 XSD 声明来下载对应的文件来处理 XML。但是这样会有可能导致下载不到从而解析失败。EntityResover 就是提供一个如何寻找声明的方法,可以允许程序在本地寻找声明文件。
解析及注册 BeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 实例化 DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 记录统计前 BeanDefinition 加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载及注册 Bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
registerBeanDefinitions
函数完成了实际的注册工作。底层主要完成了对 beans
标签内所有 bean 的解析。