失宠的 XmlBeanFactory
上篇文章中提到
Spring
IoC 容器中的两条设计路径,但是这两条路径都只是体现了接口之间的关系,今天就从具体的实现来看看 Spring
IoC 不同功能的IoC容器。
在Spirng
中IoC容器的实现有很多种,XmlBeanFactory
是Spring
中最早提供的实现类,虽然现在已经废弃不用了,但是依旧可以从中学习到很多的东西。面对过去,知其所以然;面向未来,才能更好的运用。
XmlBeanFactory的使用
由于XmlBeanFactory
已经弃用,这里简单的使用一下,代码展示如下:
public class XmlBeanFactoryTest {
public static void main(String[] args) {
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-bean.xml"));
XmlBeanFactoryService xmlServiceTest = (XmlBeanFactoryService)bf.getBean("xmlServiceTest");
xmlServiceTest.say();
}
}
public class XmlBeanFactoryService {
public void say(){
System.out.println("hello");
}
}
XmlBeanFactory
的使用是通过通过在xml文件中配置相应的Bean
,然后通过加载xml文件,根据id
来获取相应的Bean
,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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="classPathAppContextService" class="com.fchen.service.ClassPathAppContextService">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="xmlServiceTest" class="com.fchen.service.XmlBeanFactoryService">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
XmlBeanFactory的设计路径
在Spring
IoC总览中,提到了两种IoC容器的设计路径,其中XmlBeanFactory
隶属于: BeanFactory
到 HierarchicalBeanFactory
,再到 ConfigurableBeanFactory
。这一条设计路线,通过下面类设计的关系图可知:
从上图中可知 XmlBeanFactory
在 DefaultListableBeanFactory
的基础上做了扩展,而 DefaultListableBeanFactory
则实现了 ConfigurableBeanFactory
。这里这个DefaultListableBeanFactory
是在Spring
IoC 容器设计中极其重要的实现类,其中包含了IoC容器所具有的功能。后续的文章中会做详细的介绍。
XmlBeanFactory的源码分析
public class XmlBeanFactory extends DefaultListableBeanFactory {
/**
* 初始化 XmlBeanDefinitionReader 对象
* 处理以 xml 方式定义的 BeanDefinition
*/
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
/** 调用父类的构造方法*/
super(parentBeanFactory);
/** 使用 XmlBeanDefinitionReader,调用loadBeanDefinitions*/
this.reader.loadBeanDefinitions(resource);
}
}
从上述源码来看,XmlBeanFactory
只提供了最基本的IoC容器的功能,他用来读取以 XML
的方式描述的Spring
中Bean
的定义(BeanDefinition)。通过下图 XmlBeanFactory
的类继承关系图,来分析上述源码:
XmlBeanFactory
继承自 DefaultListableBeanFactory
,可知 XmlBeanFactory
会拥有父类的功能。在 XmlBeanFactory
中定义并初始化了 XmlBeanDefinitionReader
对象,并通过它来完成 Bean
描述文件的读取。
XmlBeanDefinitionReader对象
- 1.通过继承AbstractBeanDefinitionReader 中的方法,来使用 ResourceLoader 将资源文件路径转换 为对应的Resource 文件。
- 2.通过DocumentLoader 对 Resource 文件进行转换,将Resource文件转换为 Document 文件。
- 3.通过实现BeanDefinitionDocumentReader 接口的 DefaultBeanDefinitionDocumentReader类, 对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。
XmlBeanFactory 的初始化
在自己写的测试方法中,从下面这一行代码开始
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring-bean.xml"));
构造XmlBeanFactory这个IoC容器时,需要指定的BeanDefinition的信息来源,而这个信息 来源需要封装成Spring的Resource类来给出。Resource是Spring用来封装I/O操作的类。然后将Resource作为 构造参数传递给XmlBeanFactory构造函数。这样,IoC容器就可以方便定位到需要的BeanDefinition信息来对Bean 完成容器的初始化和依赖注入过程。
对XMLBeanDefinitionReader对象的初始化,以及使用这个对象来完成loadBeanDefinitions的 调用,就是这个调用启动从Resource中加载BeanDefinition的过程,loadBeanDefinitions()同时也是IoC容器 初始化的重要组成部分。
注: 这里请记住 loadBeanDefinitions() 这个方法,在以后的有关IoC的其他实现类中,Spring不只一次的用到这个方法。
XmlBeanFactory中的两行代码
对于XmlBeaFactory的调用最后都是通过super(parentBeanFactory);
与 this.reader.loadBeanDefinitions(resource);
来完成上述的功能。 下面,就来看一下这两行代码都做了些什么事情。
1.super(parentBeanFactory)
代码的调用顺序,结合XmlBeanFactory的类关系的继承图,可以看到其调用顺序如下:
①: XmlBeanFactory#super(parentBeanFactory) 其中parentBeanFactory 为null;
②: DefaultListableBeanFactory#super(null)
③: AbstractAutowireCapableBeanFactory#this()
④: AbstractAutowireCapableBeanFactory#super() 这里会调用 ignoreDependencyInterface()相关的方法
⑤: AbstractBeanFactory() 调用AbstractBeanFactory 的无惨构造方法
这里创建的XmlBeanFactory在继承了DefaultListableBeanFactory容器的功能的同时,增加了新的功能。
2.this.reader.loadBeanDefinitions(resource)
下面重点看一下loadBeanDefinitions()的调用顺序,因为涉及到的代码太多,这里只是简单标注调用逻辑,以及会画出相关的时序图。代码的讲解后面的文章会分析到,或在github上下载自行查看。
①: XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)
②: XmlBeanDefinitionReader#loadBeanDefinitions(new EncodedResource(resource))
a: EncodedResource()#this(resource, null, null)
b: EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset)
c: Object()
③: XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource encodedResource)
a: encodedResource.getResource().getInputStream()
④: XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)
⑤: XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource)
⑥: XmlBeanDefinitionReader#createBeanDefinitionDocumentReader()
⑦: DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
⑧: DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element root)
⑨: DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
⑩: BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)
⑪: DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
⑫: DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
⑬: BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
⑭: DefaultListableBeanFactory#registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
⑮: DefaultListableBeanFactory#beanDefinitionMap.put(beanName, beanDefinition)
3.loadBeanDefinitions(resource)时序图
总结
XmlBeanFactory
是IoC容器最底层的实现,从上面的分析过程可以看到,IoC容器的启动包括以下的三个过程,具体来说,这个启动包括BeanDefinition的Resource定位、载入和注册三个过程。
第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由 ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式BeanDefinition的使用都提供统 一的接口。
第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IoC容器内部的 数据结构,而这个容器内部的数据结构就是BeanDefinition。
第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程调用BeanDefinitionRegistry 接口的实现来完成。这个注册过程把载入过程中的解析得到的BeanDefinition向IoC容器进行注册。就是在IoC容器内部 将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来保存BeanDefinition的数据。
个人微信公众号:
个人github: github.com/FunCheney