學習Spring源碼(三)Bean的裝載與管理

1.BeanFactory接口

Spring 的容器最基本的接口就是:BeanFactory。BeanFactory 負責配置、創建及管理bean。調用者只需使用getBean 方法即可獲得指定bean的引用,無須關心bean 的實例化過程。即bean 實例的創建過程完全透明。 

Spring框架中,一旦把一個Bean納入到Spring IoC容器之中,這個Bean的生命週期就會交由容器進行管理,一般擔當管理者角色的是BeanFactory或ApplicationContex

將Bean元數據收集裝載

BeanDefinition:用於管理各種對象以及它們之間相互依賴關係的核心數據結構。

Resource:用來封裝IO操作的類。

收集過程:通過refresh方法啓動,包括BeanDefinition和Resource的定位、載入、註冊三個基本過程。

定位:即尋找數據的過程,指對BeanDefinition的資源定位,由ResourceLoader通過統一的Resource接口完成。

載入:把用戶定義好的Bean表示成IOC容器的內部數據結構,即BeanDefinition(POJO對象在IOC容器中的抽象)

註冊:將BeanDefinition注入到HashMap中,IOC容器就是通過這個HahMap來管理數據。

2.ApplicationContext接口

對大部分J2EE 應用而言,獲取Bean推薦使用ApplicationContext,因爲其是BeanFactory 的子接口,提供了更多面嚮應用的功能,其常用的實現類是org.springframework.context.support.FileSystemXmlApplicationContext。
 

3.BeanFactory與ApplicationContext區別

  1. BeanFacotry延遲加載,即只有在使用到某個Bean時(調用getBean()),纔對該Bean進行加載實例化,如果Bean的某一個屬性沒有注入,BeanFacotry加載後,直至第一次使用調用getBean方法纔會拋出異常;而ApplicationContext則在初始化自身是檢驗,這樣有利於檢查所依賴屬性是否注入;所以通常情況下我們選擇使用 ApplicationContext。ApplicationContext則會在上下文啓動後預載入所有的單實例Bean。通過預載入單實例bean ,確保當你需要的時候,你就不用等待,因爲它們已經創建好了。
  2. 一旦單例被實例化就會被放入緩存Bean池,下次獲取直接返回,而多例則每一次都創建一個新對象返回,並將控制權移交給用戶,由用戶管理Bean的生命週期,使用完後需要顯示設置爲null,幫助回收。BeanFactory只能管理單例Bean,而ApplicationContext也可以管理多例Bean。
  3. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動註冊,而ApplicationContext則是自動註冊。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 BeanFactory的許多功能需要通過編程實現而 Applicationcontext 可以通過配置實現。比如後處理 bean , Applicationcontext 直接配置在配置文件即可而 BeanFactory這要在代碼中顯示的寫出來纔可以被容器識別)。
  4. BeanFactory主要是面對與 Spring 框架的基礎設施,面對 spring 自己。而Applicationcontext主要面對與 Spring 使用的開發者。基本都會使用 Applicationcontext並非 BeanFactory。
     

 4.Bean的生命週期,以及作用域

  

1.實例化一個Bean(由BeanFactory讀取Bean定義文件,並生成各個實例)--也就是我們常說的new;


2.按照Spring上下文對實例化的Bean的屬性值進行設置--也就是IOC注入;


3.如果這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的id值


4.如果這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(可以用這個方式來獲取其它Bean,只需在Spring配置文件中配置一個普通的Bean就可以);


5.如果這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文(同樣這個方式也可以實現步驟4的內容,但比4更好,因爲ApplicationContext是BeanFactory的子接口,有更多的實現方法);說明如果Bean是由BeanFactory管理,則沒有第五步,同樣如果由ApplicationContext管理,則沒有第四步。


6.如果這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,初始化之前構造注入依賴之後執行;


7.如果Bean類實現了org.springframework.beans.factory.InitializingBean接口,則執行其afterPropertiesSet()方法。


一般是用來在實例構造完成後,實例的應該注入的依賴屬性已經完成注入,需要對不需要注入的實例屬性進行自定義初始化配置;當然也可指定一個init-method方法完成初始化,兩者的作用一樣,都是完成用戶自定義初始化。對於單例,如果bean實現了InitializingBean接口則afterPropertiesSet方法只會被調用一次;否則每次創建bean時afterPropertiesSet方法都會被重新調用。


8.如果Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法。或者使用@PostConstruct後構造註解來將任意方法名稱的方法標明爲一個init-method方法


9.如果這個Bean關聯了BeanPostProcessor接口,將會調用postProcessAfterInitialization(Object obj, String s)方法;


注:以上工作完成以後就可以應用這個Bean了,如果這個Bean是一個Singleton的,則將這準備就緒的Bean放入Spring緩存池中,已被下次使用,所以一般情況下我們調用同一個id的Bean會是在內容地址相同的實例,當然在Spring配置文件中也可以配置非Singleton,此時不會將Bean放入緩存池中,而是直接將控制權交給Bean的使用者。


10.當Bean不再需要時,會經過清理階段,如果Bean實現了DisposableBean這個接口,會調用那個其實現的destroy()方法;


11.最後,如果這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。可以使用@PreDestroy前銷燬註解,標明一個方法爲destroy-method

5.Bean的作用域Scope

通過@Scope註解來標註Bean的作用域

Scope的類型:

主要分爲單例(singleton)和多例(除了singleton外的Bean)
1.singleton:單例模式(如果不指定Scope,默認是singleton)
2.Prototype:原型模式
3.request:對於 每次http 請求,bean都會產生一個新的實例,只有在web 應用中才會生效
4.session 對於 每次HttpSession時會產生一個新的實例,只有在web 應用中才會生效
5.global session ,只有在portlet context中有效

比較常用到的是singleton和prototype,java在創建實例,要進行內存申請,銷燬實例時,要垃圾回收,這些都會導致系統開銷的增加,因此prototype作用域的bean在創建、銷燬代價會比較大,而singleton的bean實例,一旦創建,就可以重複使用

單例Bean的線程安全問題

在spring中,singleton屬性默認是true,只有設定爲false,對單線程的程序說並不會有什麼問題,但對於多線程的程序,就必須注意安全(Thread-safe)的議題,防止多個線程同時存取共享資源所引發的數據不同步問題。

如果多線程都會併發的修改Bean的實例字段時,一定要進行同步,如果只是只讀共享域則不用關心。注意處理共享字段

Prototype的Bean的使用

多例Bean通過BeanFactory和ApplicationContext 的getBean方法獲取,每次返回的都是新創建的Bean;當然單例也可以使用上面方法獲得,但是每次返回都是同一個Bean。

bean的注入方式(DI)

1.XML配置方式

2.註解方式

       採用註解的方式,比起xml配置方式更加簡潔,不然也要注意耦合方面
除了@Component外,Spring提供了3個功能基本和@Component等效的註解,分別對應於用於對DAO,Service,和Controller進行註解。

1:@Repository 用於對DAO實現類進行註解。
2:@Service 用於對業務層註解,但是目前該功能與 @Component 相同。
3:@Constroller用於對控制層註解,但是目前該功能與 @Component 相同。


3.Aotowired自動裝配方式
實現自動轉配需要兩個步驟:

  • 組件掃描(component scanning):Spring會自動發現應用上下文中所創建的bean
  • 自動裝配(autowiring):Spring自動滿足bean之間的依賴
	<!-- 掃描包 -->
	<context:component-scan base-package="com.test" />
	<!-- 註解映射支持 -->
	<mvc:annotation-drivern/>

 

@Autowired,該註解的作用是:可以對成員變量、方法和構造函數進行註解,來完成自動裝配的工作,通俗來說就是會根據類型從容器中自動查到到一個Bean給bookDAO字段。@Autowired是根據類型進行自動裝配的,如果需要按名稱進行裝配,則需要配合@Qualifier。另外可以使用其它註解,
@ Resource :等同於@Qualifier
@Inject:等同於@ Autowired。
@Service用於註解業務層組件(我們通常定義的service層就用這個)
@Controller用於註解控制層組件(如struts中的action)
@Repository用於註解數據訪問組件,即DAO組件
@Component泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行註解。


裝配註解的使用

裝配註解主要有:@Autowired、@Qualifier、@Resource,它們的特點是:

@Resource

默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean纔會按照類型來裝配注入;

@Autowired

默認是按照類型裝配注入的,如果想按照名稱來轉配注入,則需要結合@Qualifier一起使用;

@Resource

註解是又J2EE提供,而@Autowired是由spring提供,故減少系統對spring的依賴建議使用@Resource的方式;如果Maven項目是1.5的JRE則需換成更高版本的。

@Resource和@Autowired都可以書寫註解在字段或者該字段的setter方法之上


@Autowired

可以對成員變量、方法以及構造函數進行註釋,而 @Qualifier 的註解對象是成員變量、方法入參、構造函數入參。

@Qualifier("XXX")

中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。

@Autowired 註釋進行自動注入時

Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,通過屬性required可以設置非必要。

@Resource裝配順序

如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
如果指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
如果指定了type,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退爲一個原始類型進行匹配,如果匹配則自動裝配;

參照:

https://howtodoinjava.com/spring-core/spring-bean-life-cycle/

 

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