本文主要介绍我们定义的xml配置文件是怎样被Spring加载封装到bean工厂的。
我们写代码使用Spring的IOC通常是这样的:
Resource resource=new FileSystemResource("benas-config.xml");
BeanFactory factory=new XmlBeanFactory(resource);
HelloBean hello=(HelloBean)factory.getBean("helloBean");
System.out.println(hello.getHelloWord());
配置文件通常是这样的
<bean id="helloBean" class="spring2.HelloBean">
<property name="helloWord">
<value>hello</value>
</property>
</bean></beans>
那么我们就直接看底层代码是怎么实现的,转到XmlBeanFactory中,在这个类构造的时候就会去读取我们传入的xml文件,像这样:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
那么我们自然转到XmlBeanDefinitionReader类中的实现代码:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
try {
//转换为输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//构造InputSource
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//把xml中的bean的定义解析出来
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
转到doLoadBeanDefinitions(InputSource inputSource, Resource resource)中,主要代码如下:
………………
try {
int validationMode = getValidationModeForResource(resource);
//调用[com.sun.org.apache.xerces.internal
//.jaxp.DocumentBuilderImpl.parse(inputSource)]解析为Document对象
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
………………
在转到registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
//注册当前解析到的bean到工厂,注意这里会初始化“读环境”,初始化换进过的时候会将当前的beanFactory的对象引用传入,这样以后这个环境里就维持了一个到工厂的引用
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//在这里报错
return getRegistry().getBeanDefinitionCount() - countBefore;//DefaultBeanDefinitionDocumentReader
}
注意这里的BeanDefinitionDocumentReader对象实际为DefaultBeanDefinitionDocumentReader(作用完成bean的定义读取和封装到bean工厂),然后看这个类:
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void doRegisterBeanDefinitions(Element root) {
preProcessXml(root);
//实际的解析过程
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
}
}
然后我们在把代码定位到parseBeanDefinitions方法,因为要贴的代码有点多,这里就截图了:
图上注释说的很清楚,这个解析的过程是用Spring定义的一套标签的解析类来完成的,每一个标签都对应自己的解析类,这里要注意几个特殊的标签,是没有解析类的,它们是alias、name、import和bean,对于这四个标签,Spring直接调用在DefaultBeanDefinitionDocumentReader中的方法解析。
具体解析过程我们以标签为例说明(最终的解析封装过程在BeanDefinitionParserDelegate类中)。
我们看到一些列的解析,最终构造出了AbstractBeanDefinition类,就是说bean工厂在读取配置文件之后是转换为了一系列的AbstractBeanDefinition类,然后在工厂会判断一个bean定义的是不是单例的,如果是单例的那么就会实例化,如果不是单例的,是发生在用户第一次请求的时候实例化。