Spring IOC

Spring IOC體系結(jié)構(gòu)

(1) BeanFactory

Spring Bean的創(chuàng)建是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發(fā)者管理對象間的依賴關(guān)系提供了很多便利和基礎(chǔ)服務(wù),在Spring中有許多的IOC容器的實(shí)現(xiàn)供用戶選擇和使用,其相互關(guān)系如下:



其中BeanFactory作為最頂層的一個(gè)接口類,它定義了IOC容器的基本功能規(guī)范,BeanFactory 有三個(gè)子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是從上圖中我們可以發(fā)現(xiàn)最終的默認(rèn)實(shí)現(xiàn)類是 DefaultListableBeanFactory,他實(shí)現(xiàn)了所有的接口。那為何要定義這么多層次的接口呢?查閱這些接口的源碼和說明發(fā)現(xiàn),每個(gè)接口都有他使用的場合,它主要是為了區(qū)分在 Spring 內(nèi)部在操作過程中對象的傳遞和轉(zhuǎn)化過程中,對對象的數(shù)據(jù)訪問所做的限制。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關(guān)系的,也就是每個(gè)Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規(guī)則。這四個(gè)接口共同定義了 Bean 的集合、Bean 之間的關(guān)系、以及 Bean 行為

最基本的IOC容器接口BeanFactory:

1      public interface BeanFactory {    
2      
3      //對FactoryBean的轉(zhuǎn)義定義,因?yàn)槿绻褂胋ean的名字檢索FactoryBean得到的對象是工廠生成的對象,    
4      //如果需要得到工廠本身,需要轉(zhuǎn)義           
5      String FACTORY_BEAN_PREFIX = "&"; 
6         
7      //根據(jù)bean的名字,獲取在IOC容器中得到bean實(shí)例    
8      Object getBean(String name) throws BeansException;    
9    
10     //根據(jù)bean的名字和Class類型來得到bean實(shí)例,增加了類型安全驗(yàn)證機(jī)制。    
11      Object getBean(String name, Class requiredType) throws BeansException;    
12     
13     //提供對bean的檢索,看看是否在IOC容器有這個(gè)名字的bean    
14      boolean containsBean(String name);    
15     
16     //根據(jù)bean名字得到bean實(shí)例,并同時(shí)判斷這個(gè)bean是不是單例    
17     boolean isSingleton(String name) throws NoSuchBeanDefinitionException;    
18     
19     //得到bean實(shí)例的Class類型    
20     Class getType(String name) throws NoSuchBeanDefinitionException;    
21     
22     //得到bean的別名,如果根據(jù)別名檢索,那么其原名也會被檢索出來    
23    String[] getAliases(String name);  

在BeanFactory里只對IOC容器的基本行為作了定義,根本不關(guān)心你的bean是如何定義怎樣加載的。正如我們只關(guān)心工廠里得到什么的產(chǎn)品對象,至于工廠是怎么生產(chǎn)這些對象的,這個(gè)基本的接口不關(guān)心。 而要知道工廠是如何產(chǎn)生對象的,我們需要看具體的IOC容器實(shí)現(xiàn),spring提供了許多IOC容器的實(shí)現(xiàn)。
比如:XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是針對最基本的ioc容器的實(shí)現(xiàn),這個(gè)IOC容器可以讀取XML文件定義的BeanDefinition(XML文件中對bean的描述),如果說XmlBeanFactory是容器中的屌絲,ApplicationContext應(yīng)該算容器中的高帥富。ApplicationContext是Spring提供的一個(gè)高級的IoC容器,它除了能夠提供IoC容器的基本功能外,還為用戶提供了以下的附加服務(wù)。

從ApplicationContext接口的實(shí)現(xiàn),我們看出其特點(diǎn):

     1.支持信息源,可以實(shí)現(xiàn)國際化(實(shí)現(xiàn)MessageSource接口)

     2.訪問資源 (實(shí)現(xiàn)ResourcePatternResolver接口,這個(gè)后面要講)

     3.支持應(yīng)用事件 (實(shí)現(xiàn)ApplicationEventPublisher接口)
(2) BeanDefinition

SpringIOC容器管理了我們定義的各種Bean對象及其相互的關(guān)系,Bean對象在Spring實(shí)現(xiàn)中是以BeanDefinition來描述的,其繼承體系如下:



Bean 的解析過程非常復(fù)雜,功能被分的很細(xì),因?yàn)檫@里需要被擴(kuò)展的地方很多,必須保證有足夠的靈活性,以應(yīng)對可能的變化。Bean 的解析主要就是對 Spring 配置文件的解析。這個(gè)解析過程主要通過下圖中的類完成:


IoC容器的初始化

IoC容器的初始化包括BeanDefinition的Resource定位、載入和注冊這三個(gè)基本的過程。我們以ApplicationContext為例講解,ApplicationContext系列容器也許是我們最熟悉的,因?yàn)閣eb項(xiàng)目中使用的XmlWebApplicationContext就屬于這個(gè)繼承體系,還有ClasspathXmlApplicationContext等,其繼承體系如下圖所示:



ApplicationContext允許上下文嵌套,通過保持父上下文可以維持一個(gè)上下文體系。對于bean的查找可以在這個(gè)上下文體系中發(fā)生,首先檢查當(dāng)前上下文,其次是父上下文,逐級向上,這樣為不同的Spring應(yīng)用提供了一個(gè)共享的bean定義環(huán)境。
下面我們分別簡單地演示一下兩種ioc容器的創(chuàng)建過程

1、XmlBeanFactory(屌絲IOC)的整個(gè)流程

通過XmlBeanFactory的源碼,我們可以發(fā)現(xiàn):

public class XmlBeanFactory extends DefaultListableBeanFactory{

     private final XmlBeanDefinitionReader reader; 
 

     public XmlBeanFactory(Resource resource)throws BeansException{
         this(resource, null);
     }
     

     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
          throws BeansException{
         super(parentBeanFactory);
         this.reader = new XmlBeanDefinitionReader(this);
         this.reader.loadBeanDefinitions(resource);
    }
 }

//根據(jù)Xml配置文件創(chuàng)建Resource資源對象,該對象中包含了BeanDefinition的信息
 ClassPathResource resource =new ClassPathResource("application-context.xml");
//創(chuàng)建DefaultListableBeanFactory
 DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
//創(chuàng)建XmlBeanDefinitionReader讀取器,用于載入BeanDefinition。之所以需要BeanFactory作為參數(shù),是因?yàn)闀⒆x取的信息回調(diào)配置給factory
 XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader執(zhí)行載入BeanDefinition的方法,最后會完成Bean的載入和注冊。完成后Bean就成功的放置到IOC容器當(dāng)中,以后我們就可以從中取得Bean來使用
 reader.loadBeanDefinitions(resource);

this.reader = new XmlBeanDefinitionReader(this); 中其中this 傳的是factory對象

2、FileSystemXmlApplicationContext 的IOC容器流程

1.ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);

先看其構(gòu)造函數(shù):

/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of file paths
* @throws BeansException if context creation failed
 */public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

實(shí)際調(diào)用

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)  
            throws BeansException {    
        super(parent);  
        setConfigLocations(configLocations);  
        if (refresh) {  
            refresh();  
        }  
    } 

2、設(shè)置資源加載器和資源定位
通過分析FileSystemXmlApplicationContext的源代碼可以知道,在創(chuàng)建FileSystemXmlApplicationContext容器時(shí),構(gòu)造方法做以下兩項(xiàng)重要工作:

首先,調(diào)用父類容器的構(gòu)造方法(super(parent)方法)為容器設(shè)置好Bean資源加載器。

然后,再調(diào)用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設(shè)置Bean定義資源文件的定位路徑。

通過追蹤FileSystemXmlApplicationContext的繼承體系,發(fā)現(xiàn)其父類的父類AbstractApplicationContext中初始化IoC容器所做的主要源碼如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader  
        implements ConfigurableApplicationContext, DisposableBean {  
    //靜態(tài)初始化塊,在整個(gè)容器創(chuàng)建過程中只執(zhí)行一次  
    static {  
        //為了避免應(yīng)用程序在Weblogic8.1關(guān)閉時(shí)出現(xiàn)類加載異常加載問題,加載IoC容  
       //器關(guān)閉事件(ContextClosedEvent)類  
        ContextClosedEvent.class.getName();  
    }  
    //FileSystemXmlApplicationContext調(diào)用父類構(gòu)造方法調(diào)用的就是該方法  
    public AbstractApplicationContext(ApplicationContext parent) {  
        this.parent = parent;  
        this.resourcePatternResolver = getResourcePatternResolver();  
    }  
    //獲取一個(gè)Spring Source的加載器用于讀入Spring Bean定義資源文件  
    protected ResourcePatternResolver getResourcePatternResolver() {  
        // AbstractApplicationContext繼承DefaultResourceLoader,也是一個(gè)S  
        //Spring資源加載器,其getResource(String location)方法用于載入資源  
        return new PathMatchingResourcePatternResolver(this);  
    }   
……  
} 

AbstractApplicationContext構(gòu)造方法中調(diào)用PathMatchingResourcePatternResolver的構(gòu)造方法創(chuàng)建Spring資源加載器:

public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {  
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");  
        //設(shè)置Spring的資源加載器  
        this.resourceLoader = resourceLoader;  
} 

在設(shè)置容器的資源加載器之后,接下來FileSystemXmlApplicationContet執(zhí)行setConfigLocations方法通過調(diào)用其父類AbstractRefreshableConfigApplicationContext的方法進(jìn)行對Bean定義資源文件的定位,該方法的源碼如下:

//處理單個(gè)資源文件路徑為一個(gè)字符串的情況  
    public void setConfigLocation(String location) {  
       //String CONFIG_LOCATION_DELIMITERS = ",; /t/n";  
       //即多個(gè)資源文件路徑之間用” ,; /t/n”分隔,解析成數(shù)組形式  
        setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));  
    }  

    //解析Bean定義資源文件的路徑,處理多個(gè)資源文件字符串?dāng)?shù)組  
     public void setConfigLocations(String[] locations) {  
        if (locations != null) {  
            Assert.noNullElements(locations, "Config locations must not be null");  
            this.configLocations = new String[locations.length];  
            for (int i = 0; i < locations.length; i++) {  
                // resolvePath為同一個(gè)類中將字符串解析為路徑的方法  
                this.configLocations[i] = resolvePath(locations[i]).trim();  
            }  
        }  
        else {  
            this.configLocations = null;  
        }  
    } 

通過這兩個(gè)方法的源碼我們可以看出,我們既可以使用一個(gè)字符串來配置多個(gè)Spring Bean定義資源文件,也可以使用字符串?dāng)?shù)組,即下面兩種方式都是可以的:

a. ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);

多個(gè)資源文件路徑之間可以是用” ,; /t/n”等分隔。

b. ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});

至此,Spring IoC容器在初始化時(shí)將配置的Bean定義資源文件定位為Spring封裝的Resource。

3、AbstractApplicationContext的refresh函數(shù)載入Bean定義過程

Spring IoC容器對Bean定義資源的載入是從refresh()函數(shù)開始的,refresh()是一個(gè)模板方法,refresh()方法的作用是:在創(chuàng)建IoC容器前,如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關(guān)閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進(jìn)行初始化,對Bean定義資源進(jìn)行載入

FileSystemXmlApplicationContext通過調(diào)用其父類AbstractApplicationContext的refresh()函數(shù)啟動整個(gè)IoC容器對Bean定義的載入過程:

1      public void refresh() throws BeansException, IllegalStateException {  
2        synchronized (this.startupShutdownMonitor) {  
3            //調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時(shí)時(shí)間,同時(shí)給容器設(shè)置同步標(biāo)識  
4            prepareRefresh();  
5            //告訴子類啟動refreshBeanFactory()方法,Bean定義資源文件的載入從  
6           //子類的refreshBeanFactory()方法啟動  
7            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
8            //為BeanFactory配置容器特性,例如類加載器、事件處理器等  
9            prepareBeanFactory(beanFactory);  
10            try {  
11                //為容器的某些子類指定特殊的BeanPost事件處理器  
12                postProcessBeanFactory(beanFactory);  
13                //調(diào)用所有注冊的BeanFactoryPostProcessor的Bean  
14                invokeBeanFactoryPostProcessors(beanFactory);  
15                //為BeanFactory注冊BeanPost事件處理器.  
16                //BeanPostProcessor是Bean后置處理器,用于監(jiān)聽容器觸發(fā)的事件  
17                registerBeanPostProcessors(beanFactory);  
18                //初始化信息源,和國際化相關(guān).  
19                initMessageSource();  
20                //初始化容器事件傳播器.  
21                initApplicationEventMulticaster();  
22                //調(diào)用子類的某些特殊Bean初始化方法  
23                onRefresh();  
24                //為事件傳播器注冊事件監(jiān)聽器.  
25                registerListeners();  
26                //初始化所有剩余的單態(tài)Bean.  
27                finishBeanFactoryInitialization(beanFactory);  
28                //初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件  
29                finishRefresh();  
30            }  
31            catch (BeansException ex) {  
32                //銷毀以創(chuàng)建的單態(tài)Bean  
33                destroyBeans();  
34                //取消refresh操作,重置容器的同步標(biāo)識.  
35                cancelRefresh(ex);  
36                throw ex;  
37            }  
38        }  
39    }

refresh()方法主要為IoC容器Bean的生命周期管理提供條件,Spring IoC容器載入Bean定義資源文件從其子類容器的refreshBeanFactory()方法啟動,所以整個(gè)refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”這句以后代碼的都是注冊容器的信息源和生命周期事件,載入過程就是從這句代碼啟動。

refresh()方法的作用是:在創(chuàng)建IoC容器前,如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關(guān)閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進(jìn)行初始化,對Bean定義資源進(jìn)行載入

AbstractApplicationContext的obtainFreshBeanFactory()方法調(diào)用子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資源文件的過程,代碼如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {  
        //這里使用了委派設(shè)計(jì)模式,父類定義了抽象的refreshBeanFactory()方法,具體實(shí)現(xiàn)調(diào)用子類容器的refreshBeanFactory()方法
         refreshBeanFactory();  
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();  
        if (logger.isDebugEnabled()) {  
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);  
        }  
        return beanFactory;  
    } 

AbstractApplicationContext子類的refreshBeanFactory()方法:

AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正調(diào)用的是其子類AbstractRefreshableApplicationContext實(shí)現(xiàn)的 refreshBeanFactory()方法,方法的源碼如下:

1    protected final void refreshBeanFactory() throws BeansException {  
2        if (hasBeanFactory()) {//如果已經(jīng)有容器,銷毀容器中的bean,關(guān)閉容器  
3            destroyBeans();  
4            closeBeanFactory();  
5        }  
6        try {  
7             //創(chuàng)建IoC容器  
8             DefaultListableBeanFactory beanFactory = createBeanFactory();  
9             beanFactory.setSerializationId(getId());  
10            //對IoC容器進(jìn)行定制化,如設(shè)置啟動參數(shù),開啟注解的自動裝配等  
11            customizeBeanFactory(beanFactory);  
12            //調(diào)用載入Bean定義的方法,主要這里又使用了一個(gè)委派模式,在當(dāng)前類中只定義了抽象的loadBeanDefinitions方法,具體的實(shí)現(xiàn)調(diào)用子類容器  
13            loadBeanDefinitions(beanFactory);  
14            synchronized (this.beanFactoryMonitor) {  
15                this.beanFactory = beanFactory;  
16            }  
17        }  
18        catch (IOException ex) {  
19            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
20        }  
21    }

在這個(gè)方法中,先判斷BeanFactory是否存在,如果存在則先銷毀beans并關(guān)閉beanFactory,接著創(chuàng)建DefaultListableBeanFactory,并調(diào)用loadBeanDefinitions(beanFactory)裝載bean定義。

5、AbstractRefreshableApplicationContext子類的loadBeanDefinitions方法:
AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調(diào)用的是其子類AbstractXmlApplicationContext對該方法的實(shí)現(xiàn),AbstractXmlApplicationContext的主要源碼如下:
loadBeanDefinitions方法同樣是抽象方法,是由其子類實(shí)現(xiàn)的,也即在AbstractXmlApplicationContext中。

1  public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {  
2     ……  
3     //實(shí)現(xiàn)父類抽象的載入Bean定義方法  
4     @Override  
5     protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
6         //創(chuàng)建XmlBeanDefinitionReader,即創(chuàng)建Bean讀取器,并通過回調(diào)設(shè)置到容器中去,容  器使用該讀取器讀取Bean定義資源  
7         XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
8         //為Bean讀取器設(shè)置Spring資源加載器,AbstractXmlApplicationContext的  
9         //祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個(gè)資源加載器  
10        beanDefinitionReader.setResourceLoader(this);  
11        //為Bean讀取器設(shè)置SAX xml解析器  
12        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
13        //當(dāng)Bean讀取器讀取Bean定義的Xml資源文件時(shí),啟用Xml的校驗(yàn)機(jī)制  
14        initBeanDefinitionReader(beanDefinitionReader);  
15        //Bean讀取器真正實(shí)現(xiàn)加載的方法  
16        loadBeanDefinitions(beanDefinitionReader);  
17    }  
18    //Xml Bean讀取器加載Bean定義資源  
19    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
20        //獲取Bean定義資源的定位  
21        Resource[] configResources = getConfigResources();  
22        if (configResources != null) {  
23            //Xml Bean讀取器調(diào)用其父類AbstractBeanDefinitionReader讀取定位  
24            //的Bean定義資源  
25            reader.loadBeanDefinitions(configResources);  
26        }  
27        //如果子類中獲取的Bean定義資源定位為空,則獲取FileSystemXmlApplicationContext構(gòu)造方法中setConfigLocations方法設(shè)置的資源  
28        String[] configLocations = getConfigLocations();  
29        if (configLocations != null) {  
30            //Xml Bean讀取器調(diào)用其父類AbstractBeanDefinitionReader讀取定位  
31            //的Bean定義資源  
32            reader.loadBeanDefinitions(configLocations);  
33        }  
34    }  
35    //這里又使用了一個(gè)委托模式,調(diào)用子類的獲取Bean定義資源定位的方法  
36    //該方法在ClassPathXmlApplicationContext中進(jìn)行實(shí)現(xiàn),對于我們  
37    //舉例分析源碼的FileSystemXmlApplicationContext沒有使用該方法  
38    protected Resource[] getConfigResources() {  
39        return null;  
40    }   ……  
41}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring容器高層視圖 Spring 啟動時(shí)讀取應(yīng)用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,917評論 1 24
  • 1- IOC的概念 IOC:也即控制反轉(zhuǎn),DI即依賴注入,控制反轉(zhuǎn)IOC和依賴注入DI其實(shí)就是同個(gè)概念的兩個(gè)不同...
    zhanglbjames閱讀 3,136評論 1 3
  • 1 spring核心IoC spring源碼版本 version 4.0.5 1.1 BeanFactory 和 ...
    我不是李小龍閱讀 478評論 0 1
  • 長夜不覺長,綿綿思悠悠。 萬般皆寂寞,念念語不休。 昔人相媚好,今飲穿腸酒。 歌罷琴音澀,曲終杯莫愁。
    阿賴耶識矣閱讀 679評論 0 2
  • 不知道大家有沒有這樣的感覺,真正相愛的人,是沒辦法做朋友的。 兩個(gè)相愛的人,她們曾經(jīng)那么熟悉彼此,從興趣愛好到各...
    小太陽girl閱讀 1,040評論 0 1

友情鏈接更多精彩內(nèi)容