Spring 源码阅读(IOC容器)-容器启动1

Spring 源码阅读(IOC容器)-容器启动1

    我们知道Spring框架提供基于依赖注入的IOC容器,完成对象的构造、依赖注入、对象声明周期维护等功能,下面将以FileSystemXmlApplicationContext为例来分析Spring IOC容器的实现。

1.容器类图

    从整体上看Spring容器可以分为两大部分:外部容器和内部容器。我们经常使用的FileSystemXmlApplicationContext、ClassPathXmlApplicationContext表现为外部容器,他们的父类都继承了ResourceLoader(资源加载),因此他们主要侧重于对外部资源的注入、解释方面的处理,而对于对象的构造、注入等声明周期维护则委托给内部对象实现,通过区分外部容器和内部容器,可以有效保护内部容器管理的bean,防止外部错误的调用破坏对象状态。
                                                                        图-1 外部容器类图
  
                                                                           图2 内部容器类图
    从类图分析可以看到SpringIOC容器可以分成两部分:外部容器和内部容器。外部容器(FileSystemXmlApplicationContext)主要负责与外部的调用进行沟通,包括接收外部的配置信息、为外部提供受容器管理的Bean、外部配置信息的解释、BeanDefinition的缓存等。内部容器(DefaultListableBeanFactory--深绿色)主要负责Bean的声明周期管理,包括Bean的注册、Bean的缓存、Bean的销毁等。
    

1.1 接口和类说明

类图中列出了多种类和接口,下面列出重点的进行说明。注意官方说明,翻译自API。

接口
ListableBeanFactory

官方说明

BeanFactory接口的扩展,由那些能够列举它们所管理的所有bean实例的bean工厂所实现。而不是试图通过来自于客户端的by name方式的bean查找。预载所有bean definitionsBeanFactory实现可能会实现该接口。

个人理解:提供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直接实现,因为应用程序代码几乎不曾被应用程序代码使用。也就是说,它也可以通过访问ApplicationContextgetAutowireCapableBeanFactory()方法从应用程序上下文中获得,即获得一个AutowireCapableBeanFactory实例。
个人理解

    从接口定义的方法中可以看出,该接口主要用于自动装配bean,包括创建bean、装配bean的属性、装配bean的依赖、注册bean等。该接口对于外部的应用程序而言,几乎不需要使用,其只应用于Spring容器的内部管理,实际上只有内部bean factory实现此功能。关于内部bean factory后面会有说明。


接口
ConfigurableApplicationContext

官方说明

SPI(单个程序启动)接口将会被大多数而不是全部的应用程序上下文实现,除了在ApplicationContext中的应用程序上下文客户端方法,其还提供设施来配置上下文。

         配置和生命周期方法被封装在这里,以避免使他们暴漏给ApplicationContext的客户端代码。这些方法应该只用于启动和关闭代码中。

个人理解:


抽象类
AbstractApplicationContext

官方说明:

ApplicationContext接口的抽象实现。其不要求配置使用的存储类型,只是简单的实现了常见上下文功能。它采用了模板方法模式,因此要求具体子类实现抽象方法。

与普通的BeanFactory相比,ApplicationContext支持检测定义在其内部的bean工厂中的特殊的bean。因此,该类能自动注册BeanFactoryPostProcessorBeanPostProcessorApplicationListener,它们在上下文中都被定义为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应用程序上下文实现的基类,例如ClassPathXmlApplicationContexFileSystemXmlApplicationContext也可以是XmlWebApplicationContextXmlPortletApplicationContext
个人理解:

 主要为配置文件位置的设置提供入口,即实现了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。考虑结合使用GenericApplicationContextXmlBeanDefinitionReader提供更灵活的上下文设置。


接口
ResourceLoader

加载资源(例如,类路径和文件路径)的策略接口,ApplicationContext需要提供此功能,加上扩展的ResourcePatternResolver支持。DefaultResourceLoader是该接口的独立实现,它可用于ApplicationContext以外,也可以被ResourceEditor使用。


接口
ResourcePatternResolver

    解释位置模式的策略接口,其扩展自ResourcePatternResolver接口。


PathMatchingResourcePatternResolver

ResourcePatternResolver接口的实现,能够把指定的资源位置路径转化为一个或者多个匹配资源。源路径可能是一个简单的路径,它一对一映射到目标(Resource),或者可能选择性地包含特定的“classpath*:”前缀和/或者Ant-style正则表达式(由org.springframework.util.AntPathMatcher匹配)

在一般的情况,如果指定的位置路径没有以“classpath*:”前缀开始,并且未包含PathMatcher模式,解释器将通过调用ResourceLoadergetResource方法返回单一的资源。例子可包括真实的 URLs,例如file:C:/context.xml;伪URLs,例如classpath:/context.xml;简单的无前缀的路径,例如,/WEB-INF/context.xml

2容器启动分析

2.1容器的构建
 我们以FileSystemXmlApplicationContext为例来分析容器的启动过程,首先看看FileSystemXmlApplicationContext的构造函数:

public  FileSystemXmlApplicationContext(String[] configLocations,  boolean  refresh, ApplicationContext parent)
                                  
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);
}

一般情况下,我们可以通过编码的方式来启动容器,例如,如下:

ApplicationContext context  =   new  FileSystemXmlApplicationContext (
        
new  String[]  {"classpath:applicationContext*.xml"} , true , null );
BeanFactory factory 
=  (BeanFactory) context;

2.2容器的初始化
2.2.1Refresh方法分析
    我们接下来分析fresh方法,该方法由FileSystemXmlApplicationContext的父类AbstractApplicationContext实现,从图1可以看到AbstractApplicationContext是ApplicationContext接口的最顶层实现。注意:方法中的子方法调用将在后面的章节中分别介绍。

public   void  refresh()  throws  BeansException, IllegalStateException  {
    
//获得启动关闭监视器,防止此刻容器被其他线程操作。
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定义,代码如下:

protected  ConfigurableListableBeanFactory obtainFreshBeanFactory()  {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory 
= getBeanFactory();
         …… 
        
return beanFactory;
}


protected   abstract   void  refreshBeanFactory()  throws  BeansException, IllegalStateException;

public   abstract  ConfigurableListableBeanFactory getBeanFactory()  throws  IllegalStateException;

    从代码中可以看到方法refreshBeanFactorygetBeanFactory都被定义为抽象方法,这是典型的模板方法模式。其中子类AbstractRefreshableApplicationContextGenericApplicationContext都实现这些方法,接下来以AbstractRefreshableApplicationContext的实现作进一步说明。
2.2.2.1 refreshBeanFactory方法分析

/** */ /** Bean factory for this context */  
// 上下文中的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()方法解析

    在解析之前我们先明确一件事情,springbean分为单实例(singleton)与原型(prototype)两种类型。对于singleton类型的bean,容器只保留一个实例,而原型类型的bean是容器每次接受请求通过defination创建的一个新实例。因此,我们都能够推断出容器是要缓存singleton bean的,事实上spring也确实这样做的。

   destroyBeans主要完成了以下任务,销毁singleton beansearly singleton beanssingleton factory beansdisposable beans以及disposable beans的依赖beansdestroyBeans方法由DefaultListableBeanFactory父类DefaultSingletonBeanRegistry实现的,包括对singleton beans的相关缓存实现,其代码如下:

 

protected   void  destroyBeans()  {
        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》

你可能感兴趣的:(Spring 源码阅读(IOC容器)-容器启动1)