Spring-IOC容器的使用

1- IOC的概念

IOC:也即控制反轉(zhuǎn),DI即依賴注入,控制反轉(zhuǎn)IOC和依賴注入DI其實(shí)就是同個(gè)概念的兩個(gè)不同角度的解釋。

控制反轉(zhuǎn)的概念

控制反轉(zhuǎn)可以理解為獲取依賴對(duì)象的控制反轉(zhuǎn)過(guò)來(lái)。有反轉(zhuǎn)的概念自然就有正轉(zhuǎn)的概念。

若有兩個(gè)類,類A和類B,若類A依賴與類B,則類A要獲取類B的方法,這時(shí)我們可以按照傳統(tǒng)的JavaSE思想,在A類里,寫代碼B b = new B();,通過(guò)new關(guān)鍵字獲取A類的依賴對(duì)象,或者通過(guò)工廠模式進(jìn)行獲取,當(dāng)然還有等等其它方法。這些方法,就屬于正轉(zhuǎn)的方法,因?yàn)锳類獲取B類就是通過(guò)主動(dòng)的方法進(jìn)行獲取的,統(tǒng)稱為正轉(zhuǎn)的方法。
然后,什么是反轉(zhuǎn)呢?既然正轉(zhuǎn)是主動(dòng)的方式,那么反轉(zhuǎn)就是被動(dòng)的方式。也即我們要獲取某個(gè)類的依賴對(duì)象,不需要類主動(dòng)去獲取。然后在Spring框架里是怎么實(shí)現(xiàn)的呢?在Spring框架里,實(shí)現(xiàn)IOC,是通過(guò)IOC容器實(shí)現(xiàn)的,由IOC容器負(fù)責(zé)創(chuàng)建和獲取依賴對(duì)象,對(duì)象只是被動(dòng)地接受依賴對(duì)象。
比如我們要在一個(gè)客戶端類里獲取用戶信息類,下圖給出正轉(zhuǎn)的方式

有了IOC容器之后,獲取依賴對(duì)象的方式就變成如圖所示了


這里寫圖片描述

IOC是一種很好的解耦合思想,在軟件開發(fā)中有很好的作用,不僅被應(yīng)用在JavaEE里,在其它語(yǔ)言里同樣適用。


2- Bean的裝載與管理

Spring框架中,一旦把一個(gè)Bean納入到Spring IoC容器之中,這個(gè)Bean的生命周期就會(huì)交由容器進(jìn)行管理,一般擔(dān)當(dāng)管理者角色的是BeanFactory或ApplicationContex

將Bean元數(shù)據(jù)收集裝載

BeanDefinition:用于管理各種對(duì)象以及它們之間相互依賴關(guān)系的核心數(shù)據(jù)結(jié)構(gòu)。Resource:用來(lái)封裝IO操作的類。

收集過(guò)程:通過(guò)refresh方法啟動(dòng),包括BeanDefinition和Resource的定位、載入、注冊(cè)三個(gè)基本過(guò)程。

  • 定位:即尋找數(shù)據(jù)的過(guò)程,指對(duì)BeanDefinition的資源定位,由ResourceLoader通過(guò)統(tǒng)一的Resource接口完成。
  • 載入:把用戶定義好的Bean表示成IOC容器的內(nèi)部數(shù)據(jù)結(jié)構(gòu),即BeanDefinition(POJO對(duì)象在IOC容器中的抽象)
  • 注冊(cè):將BeanDefinition注入到HashMap中,IOC容器就是通過(guò)這個(gè)HahMap來(lái)管理數(shù)據(jù)。
BeanFactory接口

Spring 的容器最基本的接口就是:BeanFactory。BeanFactory 負(fù)責(zé)配置、創(chuàng)建及管理bean。

BeanFactory接口的方法

調(diào)用者只需使用getBean 方法即可獲得指定bean的引用,無(wú)須關(guān)心bean 的實(shí)例化過(guò)程。即bean 實(shí)例的創(chuàng)建過(guò)程完全透明。



BeanFactory有很多實(shí)現(xiàn)類,通常使用org.springframework.beans.factory.xml.XmlBeanFactory 類。

通過(guò)BeanFactory獲取Bean

在UserService中注入tool字段,通過(guò)BeanFactory來(lái)實(shí)現(xiàn)獲取


UserService實(shí)現(xiàn)BeanFactoryAware接口的setBeanFactory方法,Spring會(huì)調(diào)用setBeanFactory方法將BeanFactory注入到UserService的factory字段(需要注意UserService需要被實(shí)例化,可以通過(guò)xml來(lái)配置bean,也可以通過(guò)@component類的注解來(lái)將Bean裝載到容器實(shí)例化),然后就可以通過(guò)setBeanFactory獲取Bean

ApplicationContext接口

對(duì)大部分J2EE 應(yīng)用而言,獲取Bean推薦使用ApplicationContext,因?yàn)槠涫荁eanFactory 的子接口,提供了更多面向應(yīng)用的功能,其常用的實(shí)現(xiàn)類是org.springframework.context.support.FileSystemXmlApplicationContext。

通過(guò)ApplicationContext獲取Bean


和BeanFactory一樣,SpringConfigTool類也需要實(shí)例化,ApplicationContext才能被注入到context字段上,可以通過(guò)XML或者@component類型的注解,將Bean注入。

BeanFactory與ApplicationContext區(qū)別
  1. BeanFacotry延遲加載,即只有在使用到某個(gè)Bean時(shí)(調(diào)用getBean()),才對(duì)該Bean進(jìn)行加載實(shí)例化,如果Bean的某一個(gè)屬性沒有注入,BeanFacotry加載后,直至第一次使用調(diào)用getBean方法才會(huì)拋出異常;而ApplicationContext則在初始化自身是檢驗(yàn),這樣有利于檢查所依賴屬性是否注入;所以通常情況下我們選擇使用 ApplicationContext。ApplicationContext則會(huì)在上下文啟動(dòng)后預(yù)載入所有的單實(shí)例Bean。通過(guò)預(yù)載入單實(shí)例bean ,確保當(dāng)你需要的時(shí)候,你就不用等待,因?yàn)樗鼈円呀?jīng)創(chuàng)建好了。

  2. 一旦單例被實(shí)例化就會(huì)被放入緩存Bean池,下次獲取直接返回,而多例則每一次都創(chuàng)建一個(gè)新對(duì)象返回,并將控制權(quán)移交給用戶,由用戶管理Bean的生命周期,使用完后需要顯示設(shè)置為null,幫助回收。BeanFactory只能管理單例Bean,而ApplicationContext也可以管理多例Bean。

  3. BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區(qū)別是:BeanFactory需要手動(dòng)注冊(cè),而ApplicationContext則是自動(dòng)注冊(cè)。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 BeanFactory的許多功能需要通過(guò)編程實(shí)現(xiàn)而 Applicationcontext 可以通過(guò)配置實(shí)現(xiàn)。比如后處理 bean , Applicationcontext 直接配置在配置文件即可而 BeanFactory這要在代碼中顯示的寫出來(lái)才可以被容器識(shí)別)。

  4. BeanFactory主要是面對(duì)與 Spring 框架的基礎(chǔ)設(shè)施,面對(duì) spring 自己。而Applicationcontext主要面對(duì)與 Spring 使用的開發(fā)者?;径紩?huì)使用 Applicationcontext并非 BeanFactory。

3- Bean的生命周期,以及作用域

Bean的加載(生命周期)
以BeanFactory為例
  1. 實(shí)例化一個(gè)Bean(由BeanFactory讀取Bean定義文件,并生成各個(gè)實(shí)例)--也就是我們常說(shuō)的new;

  2. 按照Spring上下文對(duì)實(shí)例化的Bean的屬性值進(jìn)行設(shè)置--也就是IOC注入;

  3. 如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了BeanNameAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的id值

  4. 如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了BeanFactoryAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(可以用這個(gè)方式來(lái)獲取其它Bean,只需在Spring配置文件中配置一個(gè)普通的Bean就可以);

  5. 如果這個(gè)Bean已經(jīng)實(shí)現(xiàn)了ApplicationContextAware接口,會(huì)調(diào)用setApplicationContext(ApplicationContext)方法,傳入Spring上下文(同樣這個(gè)方式也可以實(shí)現(xiàn)步驟4的內(nèi)容,但比4更好,因?yàn)锳pplicationContext是BeanFactory的子接口,有更多的實(shí)現(xiàn)方法);

  6. 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postProcessBeforeInitialization(Object obj, String s)方法

初始化之前構(gòu)造注入依賴之后執(zhí)行

  1. 如果Bean類實(shí)現(xiàn)了org.springframework.beans.factory.InitializingBean接口,則執(zhí)行其afterPropertiesSet()方法。

一般是用來(lái)在實(shí)例構(gòu)造完成后,實(shí)例的應(yīng)該注入的依賴屬性已經(jīng)完成注入,需要對(duì)不需要注入的實(shí)例屬性進(jìn)行自定義初始化配置;當(dāng)然也可指定一個(gè)init-method方法完成初始化,兩者的作用一樣,都是完成用戶自定義初始化。對(duì)于單例,如果bean實(shí)現(xiàn)了InitializingBean接口則afterPropertiesSet方法只會(huì)被調(diào)用一次;否則每次創(chuàng)建bean時(shí)afterPropertiesSet方法都會(huì)被重新調(diào)用。

  1. 如果Bean在Spring配置文件中配置了init-method屬性會(huì)自動(dòng)調(diào)用其配置的初始化方法。

或者使用@PostConstruct后構(gòu)造注解來(lái)將任意方法名稱的方法標(biāo)明為一個(gè)init-method方法

  1. 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postProcessAfterInitialization(Object obj, String s)方法;

注:以上工作完成以后就可以應(yīng)用這個(gè)Bean了,如果這個(gè)Bean是一個(gè)Singleton的,則將這準(zhǔn)備就緒的Bean放入Spring緩存池中,已被下次使用,所以一般情況下我們調(diào)用同一個(gè)id的Bean會(huì)是在內(nèi)容地址相同的實(shí)例,當(dāng)然在Spring配置文件中也可以配置非Singleton,此時(shí)不會(huì)將Bean放入緩存池中,而是直接將控制權(quán)交給Bean的使用者。

  1. 當(dāng)Bean不再需要時(shí),會(huì)經(jīng)過(guò)清理階段,如果Bean實(shí)現(xiàn)了DisposableBean這個(gè)接口,會(huì)調(diào)用那個(gè)其實(shí)現(xiàn)的destroy()方法;

  2. 最后,如果這個(gè)Bean的Spring配置中配置了destroy-method屬性,會(huì)自動(dòng)調(diào)用其配置的銷毀方法。

可以使用@PreDestroy前銷毀注解,標(biāo)明一個(gè)方法為destroy-method

說(shuō)明如果Bean是由BeanFactory管理,則沒有第五步,同樣如果由ApplicationContext管理,則沒有第四步。

Bean的作用域Scope

通過(guò)@Scope注解來(lái)標(biāo)注Bean的作用域

示例:


Scope的類型:

主要分為單例(singleton)和多例(除了singleton外的Bean)

  1. singleton:單例模式(如果不指定Scope,默認(rèn)是singleton
  2. Prototype:原型模式
  3. request:對(duì)于 每次http 請(qǐng)求,bean都會(huì)產(chǎn)生一個(gè)新的實(shí)例,只有在web 應(yīng)用中才會(huì)生效
  4. session 對(duì)于 每次HttpSession時(shí)會(huì)產(chǎn)生一個(gè)新的實(shí)例,只有在web 應(yīng)用中才會(huì)生效
  5. global session ,只有在portlet context中有效

比較常用到的是singleton和prototype,java在創(chuàng)建實(shí)例,要進(jìn)行內(nèi)存申請(qǐng),銷毀實(shí)例時(shí),要垃圾回收,這些都會(huì)導(dǎo)致系統(tǒng)開銷的增加,因此prototype作用域的bean在創(chuàng)建、銷毀代價(jià)會(huì)比較大,而singleton的bean實(shí)例,一旦創(chuàng)建,就可以重復(fù)使用

單例Bean的線程安全問(wèn)題

在spring中,singleton屬性默認(rèn)是true,只有設(shè)定為false,對(duì)單線程的程序說(shuō)并不會(huì)有什么問(wèn)題,但對(duì)于多線程的程序,就必須注意安全(Thread-safe)的議題,防止多個(gè)線程同時(shí)存取共享資源所引發(fā)的數(shù)據(jù)不同步問(wèn)題。

如果多線程都會(huì)并發(fā)的修改Bean的實(shí)例字段時(shí),一定要進(jìn)行同步,如果只是只讀共享域則不用關(guān)心。為了防止修改共享字段,可以使用不可變對(duì)象,或者直接讓Bean無(wú)狀態(tài)(沒有任何字段)

Prototype的Bean的使用

多例Bean通過(guò)BeanFactory和ApplicationContext 的getBean方法獲取,每次返回的都是新創(chuàng)建的Bean;當(dāng)然單例也可以使用上面方法獲得,但是每次返回都是同一個(gè)Bean。

4- bean的注入方式(DI)

  1. XML配置方式
  1. 注解方式

采用注解的方式,比起xml配置方式更加簡(jiǎn)潔,不然也要注意耦合方面
除了@Component外,Spring提供了3個(gè)功能基本和@Component等效的注解,分別對(duì)應(yīng)于用于對(duì)DAO,Service,和Controller進(jìn)行注解。

  • 1:@Repository 用于對(duì)DAO實(shí)現(xiàn)類進(jìn)行注解。
  • 2:@Service 用于對(duì)業(yè)務(wù)層注解,但是目前該功能與 @Component 相同。
  • 3:@Constroller用于對(duì)控制層注解,但是目前該功能與 @Component 相同。
  1. Aotowired自動(dòng)裝配方式
  • 實(shí)現(xiàn)自動(dòng)轉(zhuǎn)配需要兩個(gè)步驟:
  • 組件掃描(component scanning):Spring會(huì)自動(dòng)發(fā)現(xiàn)應(yīng)用上下文中所創(chuàng)建的bean
  • 自動(dòng)裝配(autowiring):Spring自動(dòng)滿足bean之間的依賴



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

裝配注解的使用

裝配注解主要有:@Autowired、@Qualifier、@Resource,它們的特點(diǎn)是:

@Resource

默認(rèn)是按照名稱來(lái)裝配注入的,只有當(dāng)找不到與名稱匹配的bean才會(huì)按照類型來(lái)裝配注入;

@Autowired

默認(rèn)是按照類型裝配注入的,如果想按照名稱來(lái)轉(zhuǎn)配注入,則需要結(jié)合@Qualifier一起使用;

@Resource

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

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

@Autowired

可以對(duì)成員變量、方法以及構(gòu)造函數(shù)進(jìn)行注釋,而 @Qualifier 的注解對(duì)象是成員變量、方法入?yún)ⅰ?gòu)造函數(shù)入?yún)ⅰ?/p>

@Qualifier("XXX")

中的 XX是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結(jié)合使用時(shí),自動(dòng)注入的策略就從 byType 轉(zhuǎn)變成 byName 了。

@Autowired 注釋進(jìn)行自動(dòng)注入時(shí)

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

@Resource裝配順序

  1. 如果同時(shí)指定了name和type,則從Spring上下文中找到唯一匹配的bean進(jìn)行裝配,找不到則拋出異常
  2. 如果指定了name,則從上下文中查找名稱(id)匹配的bean進(jìn)行裝配,找不到則拋出異常
  3. 如果指定了type,則從上下文中找到類型匹配的唯一bean進(jìn)行裝配,找不到或者找到多個(gè),都會(huì)拋出異常
  4. 如果既沒有指定name,又沒有指定type,則自動(dòng)按照byName方式進(jìn)行裝配;如果沒有匹配,則回退為一個(gè)原始類型進(jìn)行匹配,如果匹配則自動(dòng)裝配;

參考鏈接
http://uule.iteye.com/blog/2094609
http://www.blogjava.net/wmcoo/articles/334044.html
http://www.cnblogs.com/xiaoxi/p/5846416.html
http://blog.csdn.net/titilover/article/details/6736556
http://www.itdecent.cn/p/9a8a89c1cfd1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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