1. 程式人生 > >spring ioc容器結構初始化步驟以及bean生命週期和三種建立方式

spring ioc容器結構初始化步驟以及bean生命週期和三種建立方式

建立物件的三種方式:     

       1、採用預設的建構函式建立

       2、採用靜態工廠方法

          1、寫一個靜態工廠方法類

               public class HelloWorldFactory {

                  public static HelloWorld getInstance(){

                     return new HelloWorld();

                  }

               }

          2、在spring的配置檔案中進行宣告

                <bean id="helloWorld2" class="com.itheima05.spring.bean.HelloWorldFactory"

                           factory-method="getInstance"></bean>

                告訴spring容器利用HelloWorldFactory類中的getInsatance靜態方法產生物件

                但是具體的物件的建立的過程是由程式設計師來完成的

       3、例項工廠方法

          1、寫一個例項工廠類

              public class HelloWorldFactory {

                  public HelloWorld getInstance(){

                     return new HelloWorld();

                  }

               }

          2、在spring的配置檔案中

            1、<bean id="helloWorld3" class="com.itheima05.spring.bean.HelloWorldFactory2"></bean>

                   spring容器為HelloWorldFactory2建立物件

            2、<bean id="helloWorldFactory" factory-bean="helloWorld3" factory-method="getInstance"></bean>

                  告訴spring容器,利用helloWorld3物件呼叫getInstance方法

Bean的生命週期

1.Spring對Bean進行例項化(相當於程式中的new Xx())
2.Spring將值和Bean的引用注入進Bean對應的屬性中
3.如果Bean實現了BeanNameAware介面,Spring將Bean的ID傳遞給setBeanName()方法(實現BeanNameAware主要是為了通過Bean的引用來獲得Bean的ID,一般業務中是很少有用到Bean的ID的)
4.如果Bean實現了BeanFactoryAware介面,Spring將呼叫setBeanFactory(BeanFactory bf)方法並把BeanFactory容器例項作為引數傳入。(實現BeanFactoryAware 主要目的是為了獲取Spring容器,如Bean通過Spring容器釋出事件等)
5.如果Bean實現了ApplicationContextAwaer介面,Spring容器將呼叫setApplicationContext(ApplicationContext ctx)方法,把y應用上下文作為引數傳入.(作用與BeanFactory類似都是為了獲取Spring容器,不同的是Spring容器在呼叫setApplicationContext方法時會把它自己作為setApplicationContext 的引數傳入,而Spring容器在呼叫setBeanDactory前需要程式設計師自己指定(注入)setBeanFactory裡的引數BeanFactory )
6.如果Bean實現了BeanPostProcess介面,Spring將呼叫它們的postProcessBeforeInitialization(預初始化)方法(作用是在Bean例項建立成功後進行增強處理,如對Bean進行修改,增加某個功能)
7.如果Bean實現了InitializingBean介面,Spring將呼叫它們的afterPropertiesSet方法,作用與在配置檔案中對Bean使用init-method宣告初始化的作用一樣,都是在Bean的全部屬性設定成功後執行的初始化方法。
8.如果Bean實現了BeanPostProcess介面,Spring將呼叫它們的postProcessAfterInitialization(後初始化)方法(作用與6的一樣,只不過6是在Bean初始化前執行的,而這個是在Bean初始化後執行的,時機不同 )
9.經過以上的工作後,Bean將一直駐留在應用上下文中給應用使用,直到應用上下文被銷燬
10.如果Bean實現了DispostbleBean介面,Spring將呼叫它的destory方法,作用與在配置檔案中對Bean使用destory-method屬性的作用一樣,都是在Bean例項銷燬前執行的方法。

1. Bean的例項化

  首先容器啟動後,會對scope為singleton且非懶載入的bean進行例項化。

  容器在內部實現的時候,採用“策略模式”來決定採用何種方式初始化bean例項。通常,可以通過反射或者CGLIB動態位元組碼生成來初始化相應的bean例項或者動態生成其子類。預設情況下,容器內部採用CglibSubclassingInstantiationStartegy。容器只要根據相應bean定義的BeanDefinition取得例項化資訊,結合CglibSubclassingInstantiationStartegy以及不同的bean定義型別,就可以返回例項化完成的物件例項。但不是直接返回構造完成的物件例項,而是以BeanWrapper對構造完成的物件例項進行包裹,返回相應的BeanWrapper例項。這個BeanWrapper的實現類BeanWrapperImpl是對某個bean進行包裹,然後對包裹後的bean進行操作,比如設定或獲取bean的相應屬性值。

2. 設定物件屬性

  BeanWrapper繼承了PropertyAccessor介面,可以以同一的方式對物件屬性進行訪問,同時又繼承了PropertyEditorRegistry和TypeConverter介面,然後BeanWrapper就可以很方便地對bean注入屬性了。

3. 如果Bean實現了BeanNameAware介面,會回撥該介面的setBeanName()方法,傳入該bean的id,此時該Bean就獲得了自己在配置檔案中的id。

4. 如果Bean實現了BeanFactoryAware介面,會回撥該介面的setBeanFactory()方法,傳入該Bean的BeanFactory,這樣該Bean就獲得了自己所在的BeanFactory.

5. 如果Bean實現了ApplicationContextAware介面,會回撥該介面的setApplicationContext()方法,傳入該Bean的ApplicationContext, 這樣該Bean就獲得了自己所在的ApplicationContext.

6. 如果有一個Bean實現了BeanPostProcessor介面,並將該介面配置到配置檔案中,則會呼叫該介面的postProcessBeforeInitialization()方法。

7.如果Bean實現了InitializingBean介面,則會回撥該介面的afterPropertiesSet()方法。

8. 如果Bean配置了init-method方法,則會執行init-method配置的方法。

9. 如果有一個Bean實現了BeanPostProcessor介面,並將該介面配置到配置檔案中,則會呼叫該介面的postProcessAfterInitialization方法。

10.經過9之後,就可以正式使用該Bean了,對於scope為singleton的Bean, Spring IoC容器會快取一份該Bean的例項,而對於scope為prototype的Bean, 每次被呼叫都回new一個物件,而且生命週期也交給呼叫方管理了,不再是Spring容器進行管理了。

11. 容器關閉後,如果Bean實現了DisposableBean介面,則會呼叫該介面的destroy()方法。

12. 如果Bean配置了destroy-method方法,則會執行destroy-method配置的方法,至此,整個Bean生命週期結束。

Spring容器,指的就是IoC容器,其初始化步驟為: 
1.建立容器物件,設定父容器、配置檔案; 
2.容器預處理,主要是設定容器同步標識(標識當前容器是否啟用); 
3.關閉舊容器,讀取配置檔案,將其普通bean的元資料BeanDefinition註冊到beanFactory中;(bean這個時候並沒有例項化) 
4.針對子類的或使用者自定義的,執行postBeanPostProcessor方法,對BeanDefinition做後處理工作; 
5.註冊BeanPostProcessor; 
6.初始化MessageResource; 
7.初始化事件傳播器; 
8.子類自有容器初始化工作; 
9.註冊監聽器bean; 
10.例項化單例非延遲bean; 
11.釋出容器生命週期事件。

Spring IoC容器結構

IoC容器初始化過程主要經過以下幾個階段:
  1.解析階段:Spring會解析Bean的XML配置檔案,將XML元素進行抽象,抽象成Resource物件。
  2.轉換階段:通過Resource物件將配置檔案進行抽象後轉換成Spring能夠理解的BeanDefinition結構。
  3.註冊階段:Spring IoC容器的實現,從根源上是beanfactory,但真正可以作為一個可以獨立使用的ioc容器還是DefaultListableBeanFactory。

Spring IoC容器

IoC容器主要作用就是建立並管理Bean物件以及Bean屬性注入。如何建立Bean物件?是通過讀取Bean配置檔案生成相應物件。這些配置檔案格式可能多種多樣,xml、properties等格式,所以需要將其轉換(ResourceLoader/Resolver)成統一資源物件(Resource),儲存的位置也不一樣,可能是ClassPath,可能是FileSystem,也可能是URL路徑,路徑的不一樣,說明的是應用環境可能不一樣。獲得Resource物件,還要將其轉換成(BeanDefinitionReader)Spring內部對Bean的描述物件(BeanDefinition),然後,將其註冊(BeanRegister)到容器中(BeanFactory),供以後轉換成Bean物件使用。

所以,IoC容器包括了很多東西,但主要有以下六個元件:

1.資源元件:Resource,對資原始檔的描述,不同資原始檔如xml、properties檔案等,格式不同,最終都將被ResourceLoader載入獲得相應的Resource物件;

2.資源載入元件:ResourceLoader:載入xml、properties等各類格式檔案,解析檔案,並生成Resource物件。

3.Bean容器元件:BeanFactory體系:IoC容器的核心,其他元件都是為它工作的(但不是僅僅為其服務).

4.Bean註冊元件:SingletonBeanRegister/AliasRegister:將BeanDefinition物件註冊到BeanFactory(BeanDefinition Map)中去。

5.Bean描述元件:BeanDefinition體系,Spring內部對Bean描述的基本資料結構

6.Bean構造元件:BeanDefinitionReader體系,讀取Resource並將其資料轉換成一個個BeanDefinition物件。

這裡元件的定義是相對於IoC容器而言,一般這裡的元件設計包括一組介面、一組抽象類、一組實現類、異常類、工具類組成,至於為什麼要這麼設計,可以參考下設計模式的七大原則。

這些元件並不是互相獨立的,而是相互聯絡形成一個極其複雜的類關係網,下圖摘取的元件類關係圖只是摘取比較純粹(比較高層)的那一部分(比如,就ApplicationContext這種比較中層的物件而言,既是ResourceLoader元件,也是BeanFactory元件一部分,還有其他的,越底層其成分越複雜,功能對應的也越具體,其實質就是一個抽象到具體的過程,但這種抽象不是純粹的抽象到具體,更摻雜有多種抽象的結合。另外,摘取的圖來源Spring 3.2.7)。

下面依次對各個元件做詳細說明:

1.資源元件,Resource體系,資訊的載體,如各型別的檔案,二進位制流資料都是資源,是Spring內部對資源的一種統一描述,整個體系類圖網如下:(圖中名稱前面 I 代表是介面,C表示實體類,C中左上角有四分之一扇形綠色的表示是抽象類,虛線表示實現,實線表示繼承,下同)

每一個實體類代表一種資源型別,比如ClassPathResource表示類載入路徑上的資源,FileSystemResource表示檔案系統上的資源,UrlResource則是表示網路資源。這是Spring對資源訪問策略的一種實現,更加詳細內容可以參考:Spring資原始檔剖析和策略模式應用(李剛)

總的來說,Java有自己的對資源訪問策略的實現,比如Path,File,URI/URL類等,但有缺點或有不足,Spring就自己弄了一套,可以粗略的認為Spring在Java基礎上封裝了一層(就像UrlResource,其原始碼內部就是通過URI/URL實現的),增加了一些功能,更加方便點。

2.資源載入元件,ResourceLoader/Resolver體系,負責資源的載入,這裡的資源指的是xml、properties等檔案資源,返回一個對應型別的Resource物件。

同Resource對應,不同的實現類載入不同的Resource,返回對應的Resource物件,針對這一部分,現在有興趣的可以參考:深入Spring IoC原始碼之ResourceLoader(上善若水)

3.Bean描述元件,BeanDefinition體系,是對bean物件描述的基本資料結構。

4.Bean構造元件,BeanDefinitionReader體系,將Resource物件,轉換成BeanDefinition物件,就是將內部資源資料轉換成Spring Bean描述資料。其實這種讀取資料轉換成內部物件的,不僅僅是Spring專有的,比如:Dom4j解析器

SAXReader reader = new SAXReader(); Document doc = reader.read(ur.getFile());//ur是一個URLResource物件

嚴格來說,都是Reader體系吧,就是將統一資源資料物件讀取轉換成相應內部物件。

5.Bean註冊元件,將BeanDefinition物件註冊到BeanFactory中去。

6.Bean容器元件,整個IoC容器核心,所謂Bean容器,就是這裡裝著Bean物件以及所需要的各種資料。其中BeanFactory是純粹的Bean容器,用來儲存描述Bean,無關其他環境,而像ApplicationContext,也是Bean容器,但它和應用環境息息相關,所以被稱為應用上下文(環境)更恰當,從圖中也能看出來,ApplicationContext不僅有著BeanFactory“血統”,同時也繼承了EnvironmentCapable、MessageSource、ApplicationEventPublisher,即擴充套件了其許多額外功能,而其實現類則是和具體應用相關了,比如ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、WebApplicationContext,從字面上就不難理解其作用。BeanFactory和ApplicationContext的詳細區別,可以自行查詢。

這裡下面這句程式碼為例子,描述整個工作流程。

ApplicationContext context = new ClassPathXmlApplicationContext(path);

0.新建ApplicationContext容器物件(並沒有初始化);

1.ResourceLoader載入並解析資原始檔 ———> 獲得Resource物件;

2.BeanDefinitionReader讀取Resource物件,將其讀到的bean元素資料封裝到BeanDefinition元件中;

3.BeanDefinitionRegister將所有的BeanDefinition註冊到BeanFactory中(BeanDefinition是容器內部Bean的基本資料結構,BeanFactory維持著一個BeanDefinition Map);

4.容器初始化開始,容器初始化工作由AbstractApplicationContext提供的refresh()方法完成,具體初始化步驟可以參考下一節:Spring IoC容器初始化。

整個流程大體概括如下:

至此,context作為應用環境上下文功能完成,程式此後就可以通過context的geBean(String beanName)方法獲得path檔案宣告定義的bean物件。