【spring mvc】application context的生命週期

上一次講application context中bean的生命週期,後面貼了一部分代碼,但根本沒理解代碼意思,有幸在博客園看到一篇關於這部分的代碼解析,特別長,特此作了一些整理筆記,並附上連接:http://www.cnblogs.com/ITtangtang/p/3978349.htmlhtml

這部份內容從application context的建立開始講起,上次講bean的生命週期時,默認application context已經建立完了,但這部分是怎麼建立的也不是特別清楚,此次弄明白一下。git

下面主要分爲四部分:spring

一、ioc容器(application context)的建立;緩存

二、讀取配置文件,加載BeanDefinition(Bean定義資源)到ioc容器中;數據結構

三、實例化Bean多線程

四、設置屬性值,即執行setXxx()方法併發

下面依次解讀代碼:app

一、ioc容器的建立

ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);ide

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

分析FileSystemXmlApplicationContext的源代碼能夠知道,在建立FileSystemXmlApplicationContext容器時,構造方法作如下兩項重要工做:函數

  • 調用父類容器的構造方法(super(parent)方法)爲容器設置好Bean資源加載器
  • 再調用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設置Bean定義資源文件的定位路徑
  • refresh()方法的做用是:在建立IoC容器前,若是已經有容器存在,則須要把已有的容器銷燬和關閉,以保證在refresh以後使用的是新創建起來的IoC容器。refresh的做用相似於對IoC容器的重啓,在新創建好的容器中對容器進行初始化,對Bean定義資源進行載入

二、BeanDefinition的加載

Spring IoC容器對BeanDefinition(即Bean定義資源)的載入是從refresh()函數開始的。

FileSystemXmlApplicationContext經過調用其父類AbstractApplicationContext的refresh()函數啓動整個IoC容器對Bean定義的載入過程:

經過 ResourceLoader 來完成資源文件位置的定位,能夠從類路徑,文件系統, URL 等方式來定爲資源位置。若是是 XmlBeanFactory做爲 IOC 容器,容器經過使用XmlBeanDefinitionReader 來解析讀取bean的xml定義文件,而後加載bean的定義信息,並將XML的定義信息轉換爲Document對象

以後,按照Spring的Bean規則對Document對象進行解析。通過對Spring Bean定義資源文件轉換的Document對象中的元素層層解析,Spring IoC如今已經將XML形式定義的Bean定義資源文件轉換爲Spring IoC所識別的數據結構——BeanDefinition,它是Bean定義資源文件中配置的POJO對象在Spring IoC容器中的映射。因此在解析<Bean>元素過程當中沒有建立和實例化Bean對象,只是建立了Bean對象的定義類BeanDefinition,將<Bean>元素中的配置信息設置到BeanDefinition中做爲記錄,當依賴注入時才使用這些記錄信息建立和實例化具體的Bean對象。

容器解析獲得 BeanDefinition後,須要把它在 IOC 容器中註冊,這由 IOC 實現 BeanDefinitionRegistry 接口來實現。註冊過程就是在 IOC 容器內部維護的一個HashMap 來保存獲得的 BeanDefinition 的過程。這個 HashMap 是 IoC 容器持有 bean 信息的場所,之後對 bean 的操做都是圍繞這個HashMap 來實現的。

Bean定義資源文件中配置的Bean被解析成BeanDefinition,註冊到IoC容器中,被容器管理起來,真正完成了IoC容器初始化所作的所有工做。現 在IoC容器中已經創建了整個Bean的配置信息,這些BeanDefinition信息已經可使用,而且能夠被檢索,IoC容器的做用就是對這些註冊的Bean定義信息進行處理和維護。這些的註冊的Bean定義信息是IoC容器控制反轉的基礎,正是有了這些註冊的數據,容器才能夠進行依賴注入。

三、Bean的實例化

Spring IoC容器完成了Bean定義資源的定位、載入和解析註冊之後,IoC容器中已經管理類Bean定義的相關數據,可是此時IoC容器尚未對所管理的Bean進行依賴注入,依賴注入在如下兩種狀況發生:

(1).用戶第一次經過getBean方法向IoC容索要Bean時,IoC容器觸發依賴注入。

(2).當用戶在Bean定義資源中爲<Bean>元素配置了lazy-init屬性,即讓容器在解析註冊Bean定義時進行預實例化,觸發依賴注入。

 

BeanFactory接口中定義了幾個getBean方法,就是用戶向IoC容器索取管理的Bean的方法。當調用者經過 getBean( name )向容器尋找Bean時,就能夠開始看Bean的生命週期了。詳見:http://www.cnblogs.com/hantalk/p/6644701.html

若是Bean定義的單態模式(Singleton),則容器在建立以前先從緩存中查找,以確保整個容器中只存在一個實例對象。若是Bean定義的是原型模式(Prototype),則容器每次都會建立一個新的實例對象。

Ioc容器從BeanDefinitionRegistry中取出BeanDefinition對象,調用InstantiationStrategy,採用反射機制進行Bean實例化的工做。InstantiationStrategy僅負責實例化Bean的操做,至關於執行Java語言中new的功能,不會參與Bean屬性的設置工做。

1    //使用初始化策略實例化Bean對象  
2    public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {  
3        //若是Bean定義中沒有方法覆蓋,則就不須要CGLIB父類類的方法  
4        if (beanDefinition.getMethodOverrides().isEmpty()) {  
5            Constructor<?> constructorToUse;  
6            synchronized (beanDefinition.constructorArgumentLock) {  
7                //獲取對象的構造方法或工廠方法  
8                constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;  
9                //若是沒有構造方法且沒有工廠方法  
10                if (constructorToUse == null) {  
11                    //使用JDK的反射機制,判斷要實例化的Bean是不是接口  
12                    final Class clazz = beanDefinition.getBeanClass();  
13                    if (clazz.isInterface()) {  
14                        throw new BeanInstantiationException(clazz, "Specified class is an interface");  
15                    }  
16                    try {  
17                        if (System.getSecurityManager() != null) {  
18                        //這裏是一個匿名內置類,使用反射機制獲取Bean的構造方法  
19                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {  
20                                public Constructor run() throws Exception {  
21                                    return clazz.getDeclaredConstructor((Class[]) null);  
22                                }  
23                            });  
24                        }  
25                        else {  
26                            constructorToUse =  clazz.getDeclaredConstructor((Class[]) null);  
27                        }  
28                        beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;  
29                    }  
30                    catch (Exception ex) {  
31                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);  
32                    }  
33                }  
34            }  
35            //使用BeanUtils實例化,經過反射機制調用」構造方法.newInstance(arg)」來進行實例化  
36            return BeanUtils.instantiateClass(constructorToUse);  
37        }  
38        else {  
39            //使用CGLIB來實例化對象  
40            return instantiateWithMethodInjection(beanDefinition, beanName, owner);  
41        }  
    } 

 四、Bean的屬性注入

Spring IoC容器是如何將屬性的值注入到Bean實例對象中去的,經過BeanWrapper將Bean包裝起來,從Bean對應的BeanDefinition中獲取Bean屬性的配置信息PropertyValue,而後進行類型轉換解析:

(1).對於集合類型list,array,map的屬性,將其屬性值解析爲目標類型的集合後直接賦值給屬性。

(2).對於非集合類型的屬性,大量使用了JDK的反射和內省機制,經過屬性的getter方法(reader method)獲取指定屬性注入之前的值,同時調用屬性的setter方法(writer method)爲屬性設置注入後的值。看到這裏相信不少人都明白了Spring的setter注入原理。

五、關於Bean懶加載的實例化,可參考這篇文章:http://blog.csdn.net/chjttony/article/details/6278627

簡單就是說:在上面「二、BeanDefinition的加載」中執行refresh()函數時,在此進行實例化。

ApplicationContext實現的默認行爲就是在啓動時將全部singleton bean提早進行實例化。也就是說,默認狀況下lazy-init=false(不延遲加載),大部分的bean默認在refresh()的時候進行bean的預實例化,提早注入。而lazy-init=true時,纔不會提早實例化,等到beandefinition加載完了再實例化。

 

 //容器初始化的過程,讀入Bean定義資源,並解析註冊  
2    public void refresh() throws BeansException, IllegalStateException {  
3        synchronized (this.startupShutdownMonitor) {  
4             //調用容器準備刷新的方法,獲取容器的當時時間,同時給容器設置同步標識  
5             prepareRefresh();  
6             //告訴子類啓動refreshBeanFactory()方法,Bean定義資源文件的載入從  
7             //子類的refreshBeanFactory()方法啓動  
8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
9             //爲BeanFactory配置容器特性,例如類加載器、事件處理器等  
10            prepareBeanFactory(beanFactory);  
11            try {  
12                //爲容器的某些子類指定特殊的BeanPost事件處理器  
13                postProcessBeanFactory(beanFactory);  
14                //調用全部註冊的BeanFactoryPostProcessor的Bean  
15                invokeBeanFactoryPostProcessors(beanFactory);  
16                //爲BeanFactory註冊BeanPost事件處理器.  
17                //BeanPostProcessor是Bean後置處理器,用於監聽容器觸發的事件  
18                registerBeanPostProcessors(beanFactory);  
19                //初始化信息源,和國際化相關.  
20                initMessageSource();  
21                //初始化容器事件傳播器.  
22                initApplicationEventMulticaster();  
23                //調用子類的某些特殊Bean初始化方法  
24                onRefresh();  
25                //爲事件傳播器註冊事件監聽器.  
26                registerListeners();  
27                //這裏是對容器lazy-init屬性進行處理的入口方法  
28                finishBeanFactoryInitialization(beanFactory);  
29                //初始化容器的生命週期事件處理器,併發布容器的生命週期事件  
30                finishRefresh();  
31            }  
32            catch (BeansException ex) {  
33                //銷燬以建立的單態Bean  
34                destroyBeans();  
35                //取消refresh操做,重置容器的同步標識.  
36                cancelRefresh(ex);  
37                throw ex;  
38            }  
39        }  
    } 
複製代碼

 AbstractApplicationContext類中的finishBeanFactoryInitialization方法對配置了預實例化屬性的Bean進行預初始化過程,

 //對配置了lazy-init屬性的Bean進行預實例化處理  
2    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {  
3        //這是Spring3之後新加的代碼,爲容器指定一個轉換服務(ConversionService)  
4        //在對某些Bean屬性進行轉換時使用  
5        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&  
6                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {  
7            beanFactory.setConversionService(  
8                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));  
9        }  
10        //爲了類型匹配,中止使用臨時的類加載器  
11        beanFactory.setTempClassLoader(null);  
12        //緩存容器中全部註冊的BeanDefinition元數據,以防被修改  
13        beanFactory.freezeConfiguration();  
14        //對配置了lazy-init屬性的單態模式Bean進行預實例化處理  
15        beanFactory.preInstantiateSingletons();  
    } 
複製代碼

 ConfigurableListableBeanFactory是一個接口,其preInstantiateSingletons方法由其子類DefaultListableBeanFactory提供。  

1//對配置lazy-init屬性單態Bean的預實例化  
2public void preInstantiateSingletons() throws BeansException {  
3        if (this.logger.isInfoEnabled()) {  
4            this.logger.info("Pre-instantiating singletons in " + this);  
5        }  
6        //在對配置lazy-init屬性單態Bean的預實例化過程當中,必須多線程同步,以確保數據一致性  
7        synchronized (this.beanDefinitionMap) {  
8            for (String beanName : this.beanDefinitionNames) {  
9                //獲取指定名稱的Bean定義  
10                RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);  
11                //Bean不是抽象的,是單態模式的,且lazy-init屬性配置爲false  
12                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {  
13                    //若是指定名稱的bean是建立容器的Bean  
14                    if (isFactoryBean(beanName)) {  
15                    //FACTORY_BEAN_PREFIX=」&」,當Bean名稱前面加」&」符號  
16                   //時,獲取的是產生容器對象自己,而不是容器產生的Bean.  
17                   //調用getBean方法,觸發容器對Bean實例化和依賴注入過程  
18                        final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);  
19                        //標識是否須要預實例化  
20                        boolean isEagerInit;  
21                        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {  
22                            //一個匿名內部類  
23                            isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {  
24                                public Boolean run() {  
25                                    return ((SmartFactoryBean) factory).isEagerInit();  
26                                }  
27                            }, getAccessControlContext());  
28                        }  
29                        else {  
30                            isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit();   
31                        }  
32                        if (isEagerInit) {  
33                           //調用getBean方法,觸發容器對Bean實例化和依賴注入過程  
34                            getBean(beanName);  
35                        }  
36                    }  
37                    else {  
38                         //調用getBean方法,觸發容器對Bean實例化和依賴注入過程  
39                        getBean(beanName);  
40                    }  
41                }  
42            }  
43        }  
    } 

推薦參考連接:

spring源碼閱讀

Spring---IOC原理淺析

Spring:源碼解讀Spring IOC原理

相關文章
相關標籤/搜索