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的构造函数:
一般情况下,我们可以通过编码的方式来启动容器,例如,如下:
2.2容器的初始化
2.2.1Refresh方法分析
我们接下来分析fresh方法,该方法由FileSystemXmlApplicationContext的父类AbstractApplicationContext实现,从图1可以看到AbstractApplicationContext是ApplicationContext接口的最顶层实现。注意:方法中的子方法调用将在后面的章节中分别介绍。
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与getBeanFactory都被定义为抽象方法,这是典型的模板方法模式。其中子类AbstractRefreshableApplicationContext与GenericApplicationContext都实现这些方法,接下来以AbstractRefreshableApplicationContext的实现作进一步说明。
2.2.2.1 refreshBeanFactory方法分析
容器可以有双亲(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的相关缓存实现,其代码如下:
//当调用外部容器的getBean方法时,外部容器将调用转交给DefaultListalbeBeanFactory,它会依次检查singletonObjects 、earlySingletonObjects 、singletonFactories 是否存在。