Spring Ioc容器

Spring演進

  • Spring框架所倡導的基於POJO(Plain Old Java Object,簡單Java對象)的輕量級開發理念,就是從實際出發,立足於最基礎的POJO(就好像我們的地球)。爲了能夠讓這些基礎的POJO構建出健壯而強大的應用,Spring框架就好像那包裹地球的大氣層一樣,爲構築應用的POJO提供了各種服務,進而創造了一套適宜用POJO進行輕量級開發的環境。
  • 整個Spring框架構建在Core核心模塊之上,它是整個框架的基礎。AOP模塊提供了一個輕便但功能強大的 AOP框架,讓我們可以以AOP的形式增強各POJO的能力,進而補足OOP/OOSD之缺憾。
  • Spring框架在Core核心模塊和AOP模塊的基礎上,爲我們提供了完備的數據訪問和事務管理的抽象和集成服務。Spring框架中的事務管理抽象層是Spring AOP的最佳實踐,它直接構建在Spring AOP的基礎之上,爲我們提供了編程式事務管理和聲明式事務管理的完備支持。

不要只將Spring看作是一個IoC容器,也不要只將Spring與AOP掛鉤Spring提供的遠比這些東西要多得多。

Spring不僅僅是一個簡化Java EE開發的輕量級框架,它更應該是一個簡化任何Java應用的開發框架。

Spring IoC

  • Spring核心容器就是一個超級大工廠,所有的對象都會被當成Spring核心容器管理的對象,Spring把容器中的對象統稱爲Bean.
  • Inversion of Control(控制反轉),別名依賴注入(Dependency Injection)。IoC的理念就是, 讓別人爲你服務!

avatar

通常,被注入對象會直接依賴於被依賴對象。但是,在IoC的場景中,二者之間通過IoC Service Provider來打交道,所有的被注入對象和依賴對象現在由IoC Service Provider統一管理。被注入對象需要 什麼,直接跟IoC Service Provider招呼一聲,後者就會把相應的被依賴對象注入到被注入對象中,從而 達到IoC Service Provider爲被注入對象服務的目的。IoC Service Provider在這裏就是通常的IoC容器所充 當的角色。

  • 理解依賴注入
    1. spring使用一種依賴注入的方式來管理Bean之間的依賴關係,也就是當某個Java實例需要其他Java實例時,系統自動提供所需要的實例,無須程序顯示的獲取
    2. 依賴注入是一種優秀的解耦方式。依賴注入讓Spring的Bean以配置文件組織在一起,而不是以硬編碼的方式耦合在一起

    Ioc依賴注入的方式:

  • 三種依賴注入的方式,即構造方法注入(constructor injection)、setter方法注入(setter injection)以及接口注入(interface injection)。
  • 構造方法注入:被注入對象可以通過在其構造方法中聲明依賴對象的參數列表,讓外部(通常是IoC容器)知道它需要哪些依賴對象。IoC 會檢查被注入對象的構造方法,取得它所需要的依賴對象列表,進而爲其注入相應的對象。同一個對象是不可能被構造兩次的,因此,被注入對象的構造乃至其整個生命週期,應該是由IoC容器來管理的。
  • setter方法注入:對於JavaBean對象來說,通常會通過 setXXX() 和 getXXX() 方法來訪問對應屬性。這些 setXXX() 方法統稱爲setter方法, getXXX() 當然就稱爲getter方法。通過setter方法,可以更改相應的對象屬性,通過getter方法,可以獲得相應屬性的狀態。setter方法注入雖不像構造方法注入那樣,讓對象構造完成後即可使用,但相對來說更寬鬆一些,可以在對象構造完成後再注入。
  • 構造方法注入和設值注入方法比較:
    1. 對於複雜的依賴關係,使用構造注入需要同時實例化其依賴的全部實例,因而會導致性能的下降
    2. 構造注入可以在構造器中決定依賴關係的注入順序,優先依賴的優先注入
    3. 依賴關係只能在構造器中設定,則只有組件的創建者才能改變組件的依賴關係,對組件的調用者而言,組件內部的依賴關係完全透明,更符合高內聚的原則

    IoC Service Provider之IOC容器

  • 職責:業務對象的構建管理和業務對象間的依賴綁定
  • 如何管理對象間的依賴關係:IoC Service Provider同樣需要知道自己所管理和掌握的被注入 對象和依賴對象之間的對應關係。
  • 如何記錄記錄諸多對象之間的對應關係:1. 配置文件 2. 註解(屬性,構造器) avatar

IOC容器之BeanFactory:

  • BeanFactory BeanFactory。基礎類型IoC容器,提供完整的IoC服務支持。如果沒有特殊指定,默認採用延 遲初始化策略(lazy-load)。只有當客戶端對象需要訪問容器中的某個受管對象的時候,纔對 該受管對象進行初始化以及依賴注入操作。所以,相對來說,容器啓動初期速度較快,所需 要的資源有限。對於資源有限,並且功能要求不是很嚴格的場景, BeanFactory是比較合適的 IoC容器選擇。
  • ApplicationContext 在 BeanFactory 的基礎上構建,是相對比較高級的容器實現, ApplicationContext 所管理的對象,在該類型容器啓動之後,默認全部初始化並綁定完成。在那些系統資源充足,並且要求更多功能的場景中, ApplicationContext 類型的容器是比較合適的選擇。
  • BeanFactory 接口只定義如何訪問容器內管理的Bean的方法,各個 BeanFactory 的具體實現類負責具體Bean的註冊以及管理工作。BeanDefinitionRegistry 接口定義抽象了Bean的註冊邏輯。如下: avatar

    1. BeanDefinition 的實例(instance)與對象相對應,該BeanDefinition 的實例負責保存對象的所有必要信息,當客戶端向 BeanFactory 請求相應對象的時候, BeanFactory 會通過這些信息爲客戶端返回一個完備可用的對象實例。
    2. BeanFactory接口只定義如何訪問容器內管理的bean方法,而BeanDefinitionRegistry接口才是在BeanFactory的實現中充當Bean註冊管理的角色。通常情況下,具體的BeanFactory實現類會實現這個接口來管理Bean的註冊

    外部配置文件方式

    支持Properties文件格式和XML文件格式。

    通常情況下,需要根據不同的外部配置文件格式,給出相應的 BeanDefinitionReader 實現類,由 BeanDefinitionReader 的相應實現類負責將相應的配置文件內容讀取並映射到 BeanDefinition ,然後將映射後的 BeanDefinition 註冊到一個 BeanDefinitionRegistry ,之後, BeanDefinitionRegistry 即完成Bean的註冊和加載。

    BeanDefinitionRegistry beanRegistry = <某個BeanDefinitionRegistry 實現類,通常爲
    DefaultListableBeanFactory>;
    BeanDefinitionReader beanDefinitionReader = new BeanDefinitionReaderImpl(beanRegistry);
    beanDefinitionReader.loadBeanDefinitions("配置文件路徑");
    // 現在我們就取得了一個可用的BeanDefinitionRegistry實例
    
  • XML配置文件的加載:首先給出給出相應的BeanDefinitionReader實現類,由BeanDefinitionReader的相應實現類負責將相應的配置文件內容讀取並映射到BeanDefinition,然後將映射後的BeanDefinition註冊到一個BeanDefinitionRegistry,之後, BeanDefinitionRegistry即完成Bean的註冊和加載。

    public static void main(String[] args)
    {
        DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
        BeanFactory container = (BeanFactory)bindViaXMLFile(beanRegistry);
        FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
        newsProvider.getAndPersistNews();
    }
    public static BeanFactory bindViaXMLFile(BeanDefinitionRegistry registry)
    {
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry); 
        reader.loadBeanDefinitions("classpath:../news-config.xml");
        return (BeanFactory)registry;
        // 或者直接
        //return new XmlBeanFactory(new ClassPathResource("../news-config.xml"));
    }
    

bean的scope

>scope用來聲明容器中的對象所應該處的限定場景或者說該對象的存活時間
  • 使用

    DTD:
    <bean id="mockObject1" class="...MockBusinessObject" singleton="false"/>
    XSD:
    <bean id="mockObject2" class="...MockBusinessObject" scope="prototype"/>
    
  • singleton

標記爲擁有singleton scope的對象定義,在Spring的IoC容器中只存在一個實例,所有對該對象的引 用將共享這個實例。該實例從容器啓動,並因爲第一次被請求而初始化之後,將一直存活到容器退出, 也就是說,它與IoC容器“幾乎”擁有相同的“壽命”。

  • protorye 原型, 容器在接到該類型對象的請求的時候,會每次都重新生成一個新的對象實例給請求方。

但是,只要對象實例返回給請求方時, 容器就不再擁有當前返回對象的引用,請求方需要自己負責當前返回對象的後繼生命週期的管理工作,包括該對象的銷燬。

對於那些請求方不能共享使用的對象類型,應該將其bean定義的scope設置爲prototype,通常,聲明 爲prototype的scope的bean定義類型,都是一些有狀態的,比如保存每個顧客信息的對象。

  • 其他用在網絡中的,request、session和global session。

方法注入和方法替換

  • 方法注入

    1. 擁有prototype類型scope的bean,在請求方每次向容器請求該類型對象的時候,容器都 會返回一個全新的該對象實例。
    2. 但實際上在調用getBean()方法返回了依賴對象的實例,每次返回的都是容器第一次注入的實例
    3. 解決問題的關鍵在於getBean()方法每次從容器中取得新的Bean實例,而不是每次都返回其持有的單一實例。

      <bean id="newsBean" class="..domain.FXNewsBean" singleton="false">
      </bean>
      <bean id="mockPersister" class="..impl.MockNewsPersister">
      <lookup-method name="getNewsBean" bean="newsBean"/>
      </bean>
      
  • 通過<lookup-method>的name屬性指定需要注入的方法名, bean屬性指定需要注入的對象,當 getNewsBean方法被調用的時候,容器可以每次返回一個新的FXNewsBean類型的實例。

  • 方法替換

與方法注入只是通過相應方法爲主體對象注入依賴對象不同,方法替換更多體現在方法的實現層 面上,它可以靈活替換或者說以新的方法實現覆蓋掉原來某個方法的實現邏輯。基本上可以認爲,方 法替換可以幫助我們實現簡單的方法攔截功能。

        <bean id="djNewsProvider" class="..FXNewsProvider">
        <constructor-arg index="0">
        <ref bean="djNewsListener"/>
        </constructor-arg>
        <constructor-arg index="1">
        <ref bean="djNewsPersister"/>
        </constructor-arg>
        <replaced-method name="getAndPersistNews" replacer="providerReplacer">
        </replaced-method>
        </bean>
        <bean id="providerReplacer" class="..FXNewsProviderMethodReplacer">
        </bean>

容器背後的祕密

  • Spring的IoC容器基本上可以按照類似的流程劃分爲兩個階段,即容器啓動階段和Bean實例化階段
  • 容器啓動階段

容器需要依賴某些工具類(BeanDefinitionReader)對加載的Configuration MetaData,進行解析和分析,並將分析後的信息編組爲相應的BeanDefinition,最後把這些保存了bean定義必要信息的BeanDefinition,註冊到相應的BeanDefinitionRegistry,這樣容器啓動工作就完成了。

  • Bean實例化階段

當請求方通過容器的getBean方法,明確的請求某個對象時 1. 檢查對象是否初始化; 2. 如果已經初始化,則直接返回給請求方使用 3. 如果沒有,則根據BeanDefinition提供的信息實例化被請求對象,併爲其注入依賴

Bean的一生

並不會馬上就實例化相應的bean定義。我們知道,容器現在僅僅擁有所有對象的 BeanDefinition來保存實例化階段將要用的必要信息。只有當請求方通過BeanFactory的getBean() 方法來請求某個對象實例的時候,纔有可能觸發Bean實例化階段的活動。

  • BeanFactory,對象默認採用延遲初始化

當對象A被請求而需要第一次實例化的時候,如果它所依賴的對象B之前同樣沒有被實例化,那麼容器會先實例化對象A所依賴的對象。這時容器內部就會首先實例化對象B,以及對象 A依賴的其他還沒有實例化的對象。這種情況是容器內部調用getBean(),對於本次請求的請求方是隱式的。

  • ApplicationContext,啓動之後會實例化所以的bean定義

但ApplicationContext在實現的過程中依然遵循Spring容器實現流程的兩個階段,只不過它 會在啓動階段的活動完成之後,緊接着調用註冊到該容器的所有bean定義的實例化方法 getBean()。這就是爲什麼當你得到ApplicationContext類型的容器引用時,容器內所有對 象已經被全部實例化完成。

getBean()方法第一次被調用時,不管是顯式的還是隱式的, Bean實例化階段的活動纔會被觸發,第二次被調用則會直接返回容器緩存的第一次實例化完的對象實例(prototype類型bean除外)。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章