1. 容器简介
在Spring容器设计中,BeanFactory
实现容器的基本功能,ApplicationContext
作为容器高级形态存在,在简单容器的基础上,增加了许多面向框架的特性。
Spring通过BeanDefinition
来管理基于Spring应用中的各种对象以及它们之间的关系, 对IoC容器来说,BeanDefinition
就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕BeanDefinition
的处理来完成的。
在独立的程序中,一般使用ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
启动Spring容器,这2个是ApplicationContext
的具体实现类。
ApplicationContext
表示IoC容器,负责实例化、配置以及组装Bean,通过配置数据获取(configuration metadata), 配置数据包括XML, Java注解, Java代码, 利用Java的反射功能实例化Bean并建立Bean与Bean之间的依赖关系
BeanFactory和ApplicationContext的区别
BeanFactory
接口类是IoC设计的最基本的功能规范, ApplicationContext
是复杂功能的衍生扩展
ApplicatonContext
通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单容器的基础上添加了许多对高级容器的特性支持
所以我们一般是通过ApplicationContext启动IoC容器, ApplicationContext的getBean()
方法就是BeanFactory
接口方法
2. IoC容器的设计原理
2.1 接口
IoC容器是由一系列的接口组成的,以BeanFactory
和ApplicationContext
为核心,BeanFactory
是最基本的接口, 在ApplicationContext
的设计中,它集成了BeanFactory
接口体系中的ListableBeanFactory
、AutowireCapableBeanFactory
、HierarchicalBeanFactory
等BeanFactory接口,具备BeanFactory IoC容器的基本功能,并且,通过集成MessageSource
、ResourceLoader
、ApplicationEventPublisher
等接口赋予了IoC容器更高级的特性。
BeanFactory接口线
从接口BeanFactory
到HierarchicalBeanFactory
,再到ConfigurableBeanFactory
是一条主要的BeanFactory
设计路径,定义了IoC容器的基本规范。而HierarchicalBeanFactory
增加了getParentBeanFactory()
,使BeanFactory
具备了双亲IoC容器的管理功能,在接下来的ConfigurableBeanFactory
接口中,定义了一些对BeanFactory
的配置功能
ApplicationContext接口线
第二条接口设计主线,以ApplicationContext
接口上下文为设计核心,从 BeanFactory
到ListableBeanFactory
, 再到ApplicationContext
, 再到WebApplicationContext
或者ConfigurableApplicationContext
. 在这个体系中, ListableBeanFactory
接口和HierarchicalBeanFactory
两个接口, 连接BeanFactory
和ApplicationContext
。
在ListableBeanFactory
接口中,细化了BeanFactory
的接口功能,对于ApplicationContext
接口,通过继承MessageSource
、ResourceLoader
、ApplicationEventPublisher
等接口,在BeanFactory
的基础上添加了许多对高级容器的特性支持。
2.2 BeanFactory容器的设计原理
DefaultListableBeanFactory
是 IoC容器最常用的具体实现类,而XmlBeanFactory
继承了DefaultListableBeanFactory
, XmlBeanFactory
扩展了读取Xml文件的功能,也就是说XmlBeanFactory
是一个可以读取XML文件方式定义的BeanDefinition
的IoC容器
XMLBeanFactory
中,初始化了一个XmlBeanDefinitionReader
对象,有了Reader对象,那些以XML方式定义的BeanDefinition
就有了处理的地方, 实际上Xml信息的处理是由XmlBeanDefinitionReader
完成的。
在XMLBeanFactory
构造函数中传入Resource对象,Resource是Spring用来封装I/O操作的类。比如XML文件形式的Resource可以使用ClassPathResource
实现
所以,在整个BeanFactory
容器的实现过程中,DefaultListableBeanFactory
是一个很重要的IoC实现,在ApplicationContext
的实现中,原理和XMLBeanFactory
一样,也是通过持有或者扩展DefaultListableBeanFactory
来获得基本的IoC容器的功能。
编程实现
ClassPathResource res = new ClasspathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
- 创建IoC配置文件的抽象资源,包含了BeanDefinition的定义信息
- 创建一个BeanFactory,这里使用DefaultListableBeanFactory
- 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来加载XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory
- 加载定义好的资源,解析BeanDefinition,完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了
XmlBeanFactory在Spring 3.1之后被废弃,一般的,我们使用ApplicationContext启动IoC容器
2.3 ApplicationContext容器的设计原理
ApplicationContext
在BeanFactory
的基础上添加了附加的功能,使得IoC容器的功能更加丰富,所以一般建议在开发时使用ApplicationContext作为IoC容器的基本形式,以常见的FileSystemXmlApplicationContext
进行说明。
FileSystemXmlApplicationContext
作为具体的应用上下文,实现和它自身设计相关的两个功能。
一个是直接使用FileSystemXmlApplicationContext,支持实例化的这个应用上下文,同时启动IoC容器的refresh()
过程,这个refresh()
过程涉及一系列复杂的操作,将在IoC容器的初始化过程中说明
另一个是与FileSystemXmlApplicationContext设计具体相关的功能,与从文件系统中加载XML的Bean定位资源相关,相关代码:
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
3. IoC容器的初始化过程
ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");
我们一般通过上面代码启动Spring IoC容器, 下面我们根据这行代码,进行分析IoC容器的初始化过程, Spring IoC容器的启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。
3.1 Resource定位
Resource定位指的是BeanDefinition的定位过程,这里用的IoC容器是ListableBeanFactory,DefaultListableBeanFactory并不能直接使用Resource, 必须通过BeanDefinitionReader来处理,在ApplicationContext中,Spring为我们提供了一系列加载不同Resource的Reader实现,DefaultListableBeanFactory是纯粹的容器,我们需要为其配置特定的Reader才能完成这些功能
FileSystemXmlApplicationContext类的继承关系
AbstractXmlApplicationContext
专门针对XML文件进行处理,重写了loadBeanDefinitions()方法,实例化了XmlBeanDefinitionReader,赋予读取了Xml配置文件的能力
AbstractRefreshableConfigApplicationContext
为ClassPathXmlApplicationContext、FileSystemXmlApplicationContext以及XmlWebApplicationContext提供通用的配置路径处理能力
AbstractRefreshableApplicationContext
最最重要的ApplicationContext的实现类,创建DefaultListableBeanFactory容器,并且提供了loadBeanDefinitions方法, 赋予了其子类AbstractXmlApplicationContext 等实现载入BeanDefinition的能力
AbstractApplicationContext
ApplicationContext接口的抽象实现类,是ApplicationContext的超级大打手,使用了模板方法设计模式,自动注册了BeanPostProcessors、ApplicationListeners以及MessageSource等高级功能,其中的refresh()方法就是FileSystemXmlApplicationContext等具体ApplicationContext实现类构造函数的启动入口, 老大终于现真身了
DefaultResourceLoader
ResourceLoader接口的实现类,会根据不同的配置文件类型,提供不同的Resource,比如UrlResource和ClassPathResource等,该类的getResource()方法就是真正调用FileSystemXmlApplicationContext的getResourceByPath()的地方。
源码剖析
通过上面几个类层级关系,大致清楚了FileSystemXmlApplicationContext的实现关系,下面通过分析源代码,来剖析实现过程。
new FileSystemXmlApplicationContext(String configLocation)
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
refresh()
方法是整个容器的触发点
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
在这个方法中,阐明了用何种Resource进行定位,这里通过FileSystemResource方式载入资源.
getResourceByPath()是在DefaultResourceLoader中定义的,通过上述继承图可以发现,DefaultResourceLoader是AbstractApplicationContext的基类, 而FileSystemXmlApplicationContext继承AbstractApplicationContext, 所以FileSystemXmlApplicationContext本质是通过DefaultResourceLoader进行资源定位的
最终该方法会被XmlBeanDefinitionReader的loadBeanDefinitions方法触发 InputStream inputStream = encodedResource.getResource().getInputStream();
, 在DefaultResourceLoader的getResource()方法中被调用
在XmlWebApplicationContext和ClassPathXmlApplicationContext等其他ApplicationContext中,都会定义自己的Resource, 并且最终由基类DefaultResourceLoader的getResource()方法执行
AbstractApplicationContext.refresh()
refresh是一个很重要的方法,是IoC容器的入口,refresh详细的描述了整个ApplicationContext初始化的过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等等,这个执行过程为Bean的生命周期提供了条件。
refresh中的执行步骤如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
...
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
AbstractApplicationContext.obtainFreshBeanFactory()
这里执行AbstractRefreshableApplicationContext的refreshBeanFactory方法,准备刷新容器
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
AbstractRefreshableApplicationContext.refreshBeanFactory()
在创建容器前,如果容器已经存在,则需要先销毁和关闭,保证在refresh之后的容器的新的容器,所以refresh更像是重启的重启. 这里真正的创建了容器DefaultListableBeanFactory,然后当容器建立之后,就开始进行最重要的BeanDefinitions信息的载入
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
AbstractXmlApplicationContext.loadBeanDefinitions()
在AbstractRefreshableApplicationContext中调用的loadBeanDefinitions是抽象方法,真正执行的地方在这里,在AbstractXmlApplicationContext的loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,配置基本信息,使其具备了读取XML配置的能力
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
当XmlBeanDefinitionReader组装完毕后,就准备好大显身手了
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
AbstractBeanDefinitionReader.loadBeanDefinitions(String location)
AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类,这里为载入BeanDefinitions做好了准备
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
count += loadBeanDefinitions(location);
}
return count;
}
XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)
XMLBeanDefinitionReader是真正实现解析XML配置的地方,在loadBeanDefinitions方法中,拿到代表XML文件的Resource,真正打开了XML文件流,拿到Document对象,进行了BeanDefinitions的注册.
这里实际是通过DefaultResourceLoader调用了FileSystemApplicationContext的getResourceByPath方法拿到FileSystemResource进行XML资源的获取。
...
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
...
doLoadBeanDefinitions方法
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
registerBeanDefinitions方法
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()
DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的实现类,真正的Document文档语义解析是委托给BeanDefinitionParserDelegate进行解析,该类包含了对各种Spring Bean定义规则的处理, 其处理的结果由BeanDefinitionHolder持有,BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition使用相关的信息,比如Bean的名字,别名名称等
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这样经过逐层的解析,在XML文件中定义的BeanDefinition就被载入到了IoC容器中,并在容器中建立了数据映射。这些数据结构以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作,但重要的依赖注入此时还未发生,现在IoC容器中还只是存着静态的配置信息。
3.2 BeanDefinition在IoC容器中的注册
此时IoC容器还不能直接使用,需要在IoC容器中对这些BeanDefinition进行注册,具体是指在DefaultListableBeanFactory中的HashMap存储这些BeanDefinition信息
/** Map of bean definition objects, keyed by bean name. */
private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);
BeanDefinition注册的动作是在前文的DefaultBeanDefinitionDocumentReader.processBeanDefinition中执行,具体实现在BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry), 其调用了DefaultListableBeanFactory的registerBeanDefinition方法.
BeanDefinition注册完成后,就已经可以被IoC容器所使用了,他们都被存在DefaultListableBeanFactory的beanDefinitionMap中,可以被检索和使用。
3.3 IoC容器的依赖注入
DefaultListableBeanFactory.getBean()和AbstractBeanFactory.getBean()
依赖注入的入口,其具体实现是在DefaultListableBeanFactory的基类AbstractBeanFactory的getBean方法中具体的实现的,之后会调用createBean()
AbstractAutowireCapableBeanFactory.createBean()
createBean不但生成了需要的Bean,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等
AbstractAutowireCapableBeanFactory.createBeanInstance()
在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些都由相关的BeanDefinition来指定的。
如果未指定生成策略,默认有2种方式,一种是JVM的反射功能,一种是通过cglib来生成,SimpleInstantiationStrategy是生成对象的默认处理类
AbstractAutowireCapableBeanFactory.populateBean()
实例化后,会进行property的配置,通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中. 在BeanDefinitionValueResolver类进行具体的解析。 在这里会触发相关依赖Bean的递归实现,在resolveValueIfNecessary方法中进行解析处理。
依赖注入的发生是在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的。
一个递归是在上下文体系中查找需要的 Bean和创建Bean的递归调用, 另一个递归是在依赖注入时, 通过递归调用容器的getBean方法, 得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入 。在对Bean的属性进行依赖注入时 ,解析的过程也是一个递归的过程 。这样 ,根据依赖关系 ,一层一层地完成Bean的创建和注入 ,直到最后完成当前Bean的创建 。有了这个顶层Bean的创建和对它的属性依赖注入的完成 ,意味着和当前Bean相关的整个依赖链的注入也完成了 。