Spring 源码阅读(IOC容器)-容器启动1
我们知道Spring框架提供基于依赖注入的IOC容器,完成对象的构造、依赖注入、对象声明周期维护等功能,下面将以FileSystemXmlApplicationContext为例来分析Spring IOC容器的实现。
1.容器类图
从整体上看Spring容器可以分为两大部分:外部容器和内部容器。我们经常使用的FileSystemXmlApplicationContext、ClassPathXmlApplicationContext表现为外部容器,他们的父类都继承了ResourceLoader(资源加载),因此他们主要侧重于对外部资源的注入、解释方面的处理,而对于对象的构造、注入等声明周期维护则委托给内部对象实现,通过区分外部容器和内部容器,可以有效保护内部容器管理的bean,防止外部错误的调用破坏对象状态。
图-1 外部容器类图
图2 内部容器类图
从类图分析可以看到Spring的IOC容器可以分成两部分:外部容器和内部容器。外部容器(FileSystemXmlApplicationContext)主要负责与外部的调用进行沟通,包括接收外部的配置信息、为外部提供受容器管理的Bean、外部配置信息的解释、BeanDefinition的缓存等。内部容器(DefaultListableBeanFactory--深绿色)主要负责Bean的声明周期管理,包括Bean的注册、Bean的缓存、Bean的销毁等。
1.1 接口和类说明
类图中列出了多种类和接口,下面列出重点的进行说明。注意官方说明,翻译自API。
接口ListableBeanFactory
官方说明
BeanFactory接口的扩展,由那些能够列举它们所管理的所有bean实例的bean工厂所实现。而不是试图通过来自于客户端的by name方式的bean查找。预载所有bean definitions的BeanFactory实现可能会实现该接口。
个人理解:提供bean factory所管理bean的枚举的接口
接口HierarchicalBeanFactory
官方说明
由bean factory实现的子接口,实现了该接口的bean factory能成为一个层次结构的一部分。
个人理解:用以组织bean factory层次的一个接口。
接口ApplicationContext
官方说明:
中心接口,以提供应用程序的配置。当应用程序正在运行,这是只读的,但如果实现支持,它是允许重新加载的。
一个ApplicationContext提供如下功能:
用来访问应用程序组件的bean factory方法,其继承自ListableBeanFactory。
能以通用的方式加载文件资源,其继承自ResourceLoader。
能够向注册的监听器发布事件,其继承自ApplicationEventPublisher。
能够解析消息,支持国际化,继承自MessageSource。
继承自父上下文,后代上下文中的Definition将总能获得优先级,这意味着,例如,一个单亲上下文能够被整个应用程序使用,而每个servlet有它自己的孩子上下文,它独立于其他的servlet。
接口AutowireCapableBeanFactory
官方说明
BeanFactory接口的扩展,可以被那些有能力自动装配的bean factory实现,用以达到为存在的bean暴露此项功能的目的。
该接口不应在常见的应用程序中使用,黏合BeanFactory或者ListableBeanFactory是其典型的应用案例。
应该注意的是,该接口不应被ApplicationContext直接实现,因为应用程序代码几乎不曾被应用程序代码使用。也就是说,它也可以通过访问ApplicationContext的getAutowireCapableBeanFactory()方法从应用程序上下文中获得,即获得一个AutowireCapableBeanFactory实例。
个人理解
从接口定义的方法中可以看出,该接口主要用于自动装配bean,包括创建bean、装配bean的属性、装配bean的依赖、注册bean等。该接口对于外部的应用程序而言,几乎不需要使用,其只应用于Spring容器的内部管理,实际上只有内部bean factory实现此功能。关于内部bean factory后面会有说明。
接口ConfigurableApplicationContext
官方说明
SPI(单个程序启动)接口将会被大多数而不是全部的应用程序上下文实现,除了在ApplicationContext中的应用程序上下文客户端方法,其还提供设施来配置上下文。
配置和生命周期方法被封装在这里,以避免使他们暴漏给ApplicationContext的客户端代码。这些方法应该只用于启动和关闭代码中。
个人理解:
抽象类AbstractApplicationContext
官方说明:
ApplicationContext接口的抽象实现。其不要求配置使用的存储类型,只是简单的实现了常见上下文功能。它采用了模板方法模式,因此要求具体子类实现抽象方法。
与普通的BeanFactory相比,ApplicationContext支持检测定义在其内部的bean工厂中的特殊的bean。因此,该类能自动注册BeanFactoryPostProcessor、BeanPostProcessor、ApplicationListener,它们在上下文中都被定义为beans。
通过扩展DefaultResourceLoader实现资源加载,因此,视非URL资源路径为类路径资源(支持完整类路径资源命名,包括包路径,例如mypackage/myresource.dat),否则getResourceByPath方法需要在子类中覆写。
个人理解:
该类提供了BeanFactory的后置processor的注册、应用上下文的双亲维护、资源加载的功能。
抽象类AbstractRefreshableApplicationContext
ApplicationContext的基类的实现,其支持多次refreshs,每次创建一个内部bean工厂实例。通常(但不一定),一个上下文将会由一系列的配置定位来驱动,加载bean definations。
子类唯一需要实现的方法是loadBeanDefinitions,该方法主要是获取每次刷新调用。具体的实现应该是加载bean定义到给定的org.springframework.beans.factory.support.DefaultListableBeanFactory,通常委托给一个或多个特定的bean definition readers。
抽象类AbstractRefreshableConfigApplicationContext
官方说明
AbstractRefreshableApplicationContext的子类,增加了针对 指定的配置位置(configLocations)的常见的处理。可以作为基于XML应用程序上下文实现的基类,例如ClassPathXmlApplicationContex与FileSystemXmlApplicationContext,也可以是XmlWebApplicationContext与XmlPortletApplicationContext。
个人理解:
主要为配置文件位置的设置提供入口,即实现了setConfigLocation方法。这就提供了不依赖于Spring的且更灵活、通用的配置注入方式。
抽象类AbstractXmlApplicationContext说明:
ApplicationContext的便利基类的实现。用于提取包含bean definition信息的XML文档,该文档由XmlBeanDefinitionReader负责解释。
子类要实getConfigResources和/或getConfigLocations,此外,他们可能会覆盖
getResourceByPath钩子来解释相对路径。
类FileSystemXmlApplicationContext说明
独立的XML应用程序上下文,它从文件系统或者URLs获得上下文定义,解释普通路径为相对文件系统位置(如“MYDIR/ myfile.txt”),同时它也是有用的测试工具与独立环境。
普通路径总是会被解释为相对当前VM的工作目录,即使他们以斜线开头。(这与Servlet容器的语义是一致的。)使用一个明确的“file:”前缀,以执行一个绝对文件路径。
配置文件位置默认值可以通过getConfigLocations覆盖,配置位置可以表示为“/ MYFILES/ context.xml中”之类的具体文件,也可以像“/ MYFILES/ *- context.xml”似的Ant风格模式(org.springframework.util.AntPathMatcher)。
注意:在多个配置位置的情况下,以后的bean定义覆盖先前加载的文件中定义的。这可以通过一个额外的XML文件,来故意覆盖某些bean定义。
这是一个简单的,一站式的便利的ApplicationContext。考虑结合使用GenericApplicationContext和XmlBeanDefinitionReader提供更灵活的上下文设置。
接口ResourceLoader
加载资源(例如,类路径和文件路径)的策略接口,ApplicationContext需要提供此功能,加上扩展的ResourcePatternResolver支持。DefaultResourceLoader是该接口的独立实现,它可用于ApplicationContext以外,也可以被ResourceEditor使用。
接口ResourcePatternResolver
解释位置模式的策略接口,其扩展自ResourcePatternResolver接口。
类PathMatchingResourcePatternResolver
ResourcePatternResolver接口的实现,能够把指定的资源位置路径转化为一个或者多个匹配资源。源路径可能是一个简单的路径,它一对一映射到目标(Resource),或者可能选择性地包含特定的“classpath*:”前缀和/或者Ant-style正则表达式(由org.springframework.util.AntPathMatcher匹配)
在一般的情况,如果指定的位置路径没有以“classpath*:”前缀开始,并且未包含PathMatcher模式,解释器将通过调用ResourceLoader的getResource方法返回单一的资源。例子可包括真实的 URLs,例如file:C:/context.xml;伪URLs,例如classpath:/context.xml;简单的无前缀的路径,例如,/WEB-INF/context.xml。
2容器启动分析
2.1容器的构建
我们以FileSystemXmlApplicationContext为例来分析容器的启动过程,首先看看FileSystemXmlApplicationContext的构造函数:
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
//refresh方法是容器启动的核心,包括内部容器的维护,资源定位处理,资源的解析、注册等。
refresh();
}
}
关于super的调用,以下代码是个重点:
以下代码来自于AbstractApplicationContext,这里要注意的是容器在构造时自动构造了资源模式解释器,关于资源模式解释器后面会有介绍.
// 资源模式解释器,用于解释处理ant-style配置信息,也可以把它理解为批量资源解释器,并把解释的一组资源定位解释成Resource.该实例在容器创建时会自动创建
private ResourcePatternResolver resourcePatternResolver;
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
一般情况下,我们可以通过编码的方式来启动容器,例如,如下:
new String[] {"classpath:applicationContext*.xml"} , true , null );
BeanFactory factory = (BeanFactory) context;
2.2容器的初始化
2.2.1Refresh方法分析
我们接下来分析fresh方法,该方法由FileSystemXmlApplicationContext的父类AbstractApplicationContext实现,从图1可以看到AbstractApplicationContext是ApplicationContext接口的最顶层实现。注意:方法中的子方法调用将在后面的章节中分别介绍。
//获得启动关闭监视器,防止此刻容器被其他线程操作。
synchronized (this.startupShutdownMonitor) {
prepareRefresh();//激活容器的活动状态,采用锁进行同步
/** *//** *//** *//**这里是容器初始化最为关键的一步,主要完成:
1.内部容器的初始化,包括对已经存在的内部容器管理的资源的销毁、内部容器的销毁、创建新的内部容器等;
2.资源定位解释,即把configLocations解释成一个或者多个Resource;
3.BeanDefinition加载与注册,把Resource加载解释成xml Document,然后对Document进行解释,形成BeanDefinition,并对Definition进行注册;*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//内部工厂的预处理,主要是注册一些Spring组件
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
//调用BeanFactory的后置处理,其处理的流程是:
/** *//** *//** *//**
(1)先对在prepareBeanFactory阶段注册的后置处理组件先进行调用;
(2)然后从容器加载BeanFactoryPostProcessor类型的bean,进行调用处理,这类例子我们经常用到,例如PropertyPlaceholderConfigurer在分离数据库配置中的使用;*/
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) {
beanFactory.destroySingletons();
cancelRefresh(ex);
throw ex;
}
}
}
2.2.2 obtainFreshBeanFactory方法分析
该方法是在refresh方法中调用的,它主要完成了以下任务:
1.内部容器的初始化,包括对已经存在的内部容器管理的资源(缓存的singleton bean以及bean name)的销毁、内部容器的销毁、创建新的内部容器、容器的定制等;
2.资源定位解释,即把configLocations解释成一个或者多个Resource;
3.BeanDefinition加载与注册,把Resource加载解释成xml Document,然后对Document进行解释,形成BeanDefinition,并对Definition进行注册;BeanDefinition就是spring对bean的定义,在spring构造bean以及维护bean的引用关系时用到。
obtainFreshBeanFactory方法由抽象类AbstractApplicationContext定义,代码如下:
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
……
return beanFactory;
}
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
从代码中可以看到方法refreshBeanFactory与getBeanFactory都被定义为抽象方法,这是典型的模板方法模式。其中子类AbstractRefreshableApplicationContext与GenericApplicationContext都实现这些方法,接下来以AbstractRefreshableApplicationContext的实现作进一步说明。
2.2.2.1 refreshBeanFactory方法分析
// 上下文中的beanFactory,这个factory就是所的内部容器,可以在图2中看到它的继承关系
private DefaultListableBeanFactory beanFactory;
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//检查beanFactory是否为空,如果不为空,则销毁singletonBeans缓存、singletonFactories工厂缓存、beanFactory等;
destroyBeans();
closeBeanFactory();
}
try {
// 创建internal bean factory,即属性beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory(); customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);// 加载BeanDefinition,下面重点讨论的
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException(
"I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
}
}
容器可以有双亲(AbstractApplicationContext类的parent),每个容器下都有一个beanFactory(AbstractRefreshableApplicationContext下),该beanFactory的类型为DefaultListableBeanFactory,我们称之为内部容器。它才是真正意义上bean的管理容器,因为缓存相关bean的信息的载体是由其父类DefaultSingletonBeanRegistry负责的,这一点在下面的destroyBeans方法分析中将能看到。
refreshBeanFactory()方法详细说明:
<1>检查内部的beanFactory是否存在,如果存在则要销毁beanFactory内存在的相关资
源(关于销毁的资源参看destroyBeans方法的说明)以及关闭beanFactory;
<2>构造新的内部bean factory,并定制该工厂,定制的选项主要包括:allowBeanDefinitionOverriding(是否允许bean definition覆盖)
allowCircularReferences(是否允许bean之间循环引用)
<3>加载bean definition;
destroyBeans()方法解析
在解析之前我们先明确一件事情,spring把bean分为单实例(singleton)与原型(prototype)两种类型。对于singleton类型的bean,容器只保留一个实例,而原型类型的bean是容器每次接受请求通过defination创建的一个新实例。因此,我们都能够推断出容器是要缓存singleton bean的,事实上spring也确实这样做的。
destroyBeans主要完成了以下任务,销毁singleton beans、early singleton beans、singleton factory beans、disposable beans以及disposable beans的依赖beans。destroyBeans方法由DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry实现的,包括对singleton beans的相关缓存实现,其代码如下:
getBeanFactory().destroySingletons();//destroySingletons()的实现在下面
//这里的getBeanFactory()获得的是上面提到的内部容器beanFactory
}
/** */ /** */ /** */ /*************以下代码摘自DefaultSingletonBeanRegistry**********/
// 缓存singleton对象集合,映射关系为bean name--> bean instance
/** */ /** */ /** */ /**ConcurrentMap的创建:jdk5以及以上使用juc包的ConcurrentMap;否则,存在edu的ConcurrentMap则使用edu的ConcurrentMap,否则使用Collection.synchronizedMap(HashMap);*/
private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible( 16 );
/** */ /** */ /** */ /** Cache of singleton factories: bean name --> ObjectFactory */
/** */ /** */ /** */ /**缓存singleton factory bean集合,这是不是说明一个singleton的beanName需要对应一个真实的singleton bean以及一个singleton factory bean呢?答案是否定的,FactoryBean存在的意义在于:在开发中可能存在需要类似于工厂模式创建类的需求,即一个对象是由工厂代理创建的,尤其是在第三方库结合的时候,可以用FactoryBean来创建合适的bean.*/
private final Map singletonFactories = new HashMap();
/** */ /** */ /** */ /** Cache of early singleton objects: bean name --> bean instance */
// 缓存早期的singlton bean,那么早期是如何定义的呢?spring又是如何管理的呢?实际上,earlySingletonObjects 存储的是FactoryBean的getObject方法获得的bean,singletonFactories 存储FactoryBean(详见:getSingleton()方法)
//当调用外部容器的getBean方法时,外部容器将调用转交给DefaultListalbeBeanFactory,它会依次检查singletonObjects 、earlySingletonObjects 、singletonFactories 是否存在。
private final Map earlySingletonObjects = new HashMap();
/** */ /** */ /** */ /** Set of registered singletons, containing the bean names in registration order */
// 注册的singleton bean集合,主要包含以注册顺序存储的bean names
private final Set registeredSingletons = new LinkedHashSet( 16 );
/** */ /** */ /** */ /** Names of beans that are currently in creation */
// 当前创建的singleton bean的name
private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet( new HashSet());
/** */ /** */ /** */ /** List of suppressed Exceptions, available for associating related causes */
private Set suppressedExceptions;
/** */ /** */ /** */ /** Flag that indicates whether we're currently within destroySingletons */
// 表示当前是否处于销毁singleton的状态
private boolean singletonsCurrentlyInDestruction = false ;
/** */ /** */ /** */ /** Disposable bean instances: bean name --> disposable instance */
// 处置bean,主要在bean销毁时需要调用该bean的destroy方法,即实现了disposable接口
private final Map disposableBeans = new LinkedHashMap( 16 );
/** */ /** */ /** */ /** Map between dependent bean names: bean name --> Set of dependent bean names */
// 用于保存bean的依赖关系,即一个bean依赖于哪几个bean
private final Map dependentBeanMap = CollectionFactory.createConcurrentMapIfPossible( 16 );
// 上一个的反向存储,可以理解为从属于
/** */ /** */ /** */ /** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
private final Map dependenciesForBeanMap = CollectionFactory.createConcurrentMapIfPossible( 16 );
public void destroySingletons() {//销毁单例
synchronized (this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}
synchronized (this.disposableBeans) {
String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
}
synchronized (this.singletonObjects) {
this.singletonObjects.clear(); //清除singleton对象
this.singletonFactories.clear();//清除single factory bean
this.earlySingletonObjects.clear();//清除早期的single对象
this.registeredSingletons.clear();
this.singletonsCurrentlyInDestruction = false;
}
}
下一节 《Spring 源码阅读(IOC容器)-容器启动2》