天天看點

Spring IoC,IoC原理

IOC的别名:依賴注入(DI)

2004年,Martin Fowler探讨了同一個問題,既然IOC是控制反轉,那麼到底是“哪些方面的控制被反轉了呢?”,經過詳細地分析和論證後,他得出了答案:“獲得依賴對象的過程被反轉了”。控制被反轉之後,獲得依賴對象的過程由自身管理變為了由IOC容器主動注入。于是,他給“控制反轉”取了一個更合适的名字叫做“依賴注入(Dependency Injection)”。他的這個答案,實際上給出了實作IOC的方法:注入。所謂依賴注入,就是由IOC容器在運作期間,動态地将某種依賴關系注入到對象之中。

是以,依賴注入(DI)和控制反轉(IOC)是從不同的角度的描述的同一件事情,就是指通過引入IOC容器,利用依賴關系注入的方式,實作對象之間的解耦。

我們舉一個生活中的例子,來幫助了解依賴注入的過程。大家對USB接口和USB裝置應該都很熟悉吧,USB為我們使用電腦提供了很大的友善,現在有很多的外部裝置都支援USB接口。

Spring IoC,IoC原理

圖5:USB接口和USB裝置

現在,我們利用電腦主機和USB接口來實作一個任務:從外部USB裝置讀取一個檔案。

電腦主機讀取檔案的時候,它一點也不會關心USB接口上連接配接的是什麼外部裝置,而且它确實也無須知道。它的任務就是讀取USB接口,挂接的外部裝置隻要符合USB接口标準即可。是以,如果我給電腦主機連接配接上一個U盤,那麼主機就從U盤上讀取檔案;如果我給電腦主機連接配接上一個外置硬碟,那麼電腦主機就從外置硬碟上讀取檔案。挂接外部裝置的權力由我作主,即控制權歸我,至于USB接口挂接的是什麼裝置,電腦主機是決定不了,它隻能被動的接受。電腦主機需要外部裝置的時候,根本不用它告訴我,我就會主動幫它挂上它想要的外部裝置,你看我的服務是多麼的到位。這就是我們生活中常見的一個依賴注入的例子。在這個過程中,我就起到了IOC容器的作用。

通過這個例子,依賴注入的思路已經非常清楚:當電腦主機讀取檔案的時候,我就把它所要依賴的外部裝置,幫他挂接上。整個外部裝置注入的過程和一個被依賴的對象在系統運作時被注入另外一個對象内部的過程完全一樣。

我們把依賴注入應用到軟體系統中,再來描述一下這個過程:

對象A依賴于對象B,當對象 A需要用到對象B的時候,IOC容器就會立即建立一個對象B送給對象A。IOC容器就是一個對象制造工廠,你需要什麼,它會給你送去,你直接使用就行了,而再也不用去關心你所用的東西是如何制成的,也不用關心最後是怎麼被銷毀的,這一切全部由IOC容器包辦。

在傳統的實作中,由程式内部代碼來控制元件之間的關系。我們經常使用new關鍵字來實作兩個元件之間關系的組合,這種實作方式會造成元件之間耦合。IOC很好地解決了該問題,它将實作元件間關系從程式内部提到外部容器,也就是說由容器在運作期将元件間的某種依賴關系動态注入元件中。

 IOC為我們帶來了什麼好處

我們還是從USB的例子說起,使用USB外部裝置比使用内置硬碟,到底帶來什麼好處?

第一、USB裝置作為電腦主機的外部裝置,在插入主機之前,與電腦主機沒有任何的關系,隻有被我們連接配接在一起之後,兩者才發生聯系,具有相關性。是以,無論兩者中的任何一方出現什麼的問題,都不會影響另一方的運作。這種特性展現在軟體工程中,就是可維護性比較好,非常便于進行單元測試,便于調試程式和診斷故障。代碼中的每一個Class都可以單獨測試,彼此之間互不影響,隻要保證自身的功能無誤即可,這就是元件之間低耦合或者無耦合帶來的好處。

第二、USB裝置和電腦主機的之間無關性,還帶來了另外一個好處,生産USB裝置的廠商和生産電腦主機的廠商完全可以是互不相幹的人,各幹各事,他們之間唯一需要遵守的就是USB接口标準。這種特性展現在軟體開發過程中,好處可是太大了。每個開發團隊的成員都隻需要關心實作自身的業務邏輯,完全不用去關心其它的人工作進展,因為你的任務跟别人沒有任何關系,你的任務可以單獨測試,你的任務也不用依賴于别人的元件,再也不用扯不清責任了。是以,在一個大中型項目中,團隊成員分工明确、責任明晰,很容易将一個大的任務劃分為細小的任務,開發效率和産品品質必将得到大幅度的提高。

第三、同一個USB外部裝置可以插接到任何支援USB的裝置,可以插接到電腦主機,也可以插接到DV機,USB外部裝置可以被反複利用。在軟體工程中,這種特性就是可複用性好,我們可以把具有普遍性的常用元件獨立出來,反複利用到項目中的其它部分,或者是其它項目,當然這也是面向對象的基本特征。顯然,IOC不僅更好地貫徹了這個原則,提高了子產品的可複用性。符合接口标準的實作,都可以插接到支援此标準的子產品中。

第四、同USB外部裝置一樣,子產品具有熱插拔特性。IOC生成對象的方式轉為外置方式,也就是把對象生成放在配置檔案裡進行定義,這樣,當我們更換一個實作子類将會變得很簡單,隻要修改配置檔案就可以了,完全具有熱插撥的特性。

以上幾點好處,難道還不足以打動我們,讓我們在項目開發過程中使用IOC架構嗎?

5.  IOC容器的技術剖析(原理)

IOC中最基本的技術就是“反射(Reflection)”程式設計,目前.Net C#、Java和PHP5等語言均支援,其中PHP5的技術書籍中,有時候也被翻譯成“映射”。有關反射的概念和用法,大家應該都很清楚,通俗來講就是根據給出的類名(字元串方式)來動态地生成對象。這種程式設計方式可以讓對象在生成時才決定到底是哪一種對象。反射的應用是很廣泛的,很多的成熟的架構,比如象Java中的Hibernate、Spring架構,.Net中 NHibernate、Spring.Net架構都是把“反射”做為最基本的技術手段。

反射技術其實很早就出現了,但一直被忽略,沒有被進一步的利用。當時的反射程式設計方式相對于正常的對象生成方式要慢至少得10倍。現在的反射技術經過改良優化,已經非常成熟,反射方式生成對象和通常對象生成方式,速度已經相差不大了,大約為1-2倍的差距。

我們可以把IOC容器的工作模式看做是工廠模式的升華,可以把IOC容器看作是一個工廠,這個工廠裡要生産的對象都在配置檔案中給出定義,然後利用程式設計語言的的反射程式設計,根據配置檔案中給出的類名生成相應的對象。從實作來看,IOC是把以前在工廠方法裡寫死的對象生成代碼,改變為由配置檔案來定義,也就是把工廠和對象生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。

6.  IOC容器的一些産品

Sun ONE技術體系下的IOC容器有:輕量級的有Spring、Guice、Pico Container、Avalon、HiveMind;重量級的有EJB;不輕不重的有JBoss,Jdon等等。Spring架構作為Java開發中SSH(Struts、Spring、Hibernate)三劍客之一,大中小項目中都有使用,非常成熟,應用廣泛,EJB在關鍵性的工業級項目中也被使用,比如某些電信業務。

.Net技術體系下的IOC容器有:Spring.Net、Castle等等。Spring.Net是從Java的Spring移植過來的IOC容器,Castle的IOC容器就是Windsor部分。它們均是輕量級的架構,比較成熟,其中Spring.Net已經被逐漸應用于各種項目中。

7. 使用IOC架構應該注意什麼

使用IOC架構産品能夠給我們的開發過程帶來很大的好處,但是也要充分認識引入IOC架構的缺點,做到心中有數,杜絕濫用架構。

第一、軟體系統中由于引入了第三方IOC容器,生成對象的步驟變得有些複雜,本來是兩者之間的事情,又憑空多出一道手續,是以,我們在剛開始使用IOC架構的時候,會感覺系統變得不太直覺。是以,引入了一個全新的架構,就會增加團隊成員學習和認識的教育訓練成本,并且在以後的運作維護中,還得讓新加入者具備同樣的知識體系。

第二、由于IOC容器生成對象是通過反射方式,在運作效率上有一定的損耗。如果你要追求運作效率的話,就必須對此進行權衡。

第三、具體到IOC架構産品(比如:Spring)來講,需要進行大量的配制工作,比較繁瑣,對于一些小的項目而言,客觀上也可能加大一些工作成本。

第四、IOC架構産品本身的成熟度需要進行評估,如果引入一個不成熟的IOC架構産品,那麼會影響到整個項目,是以這也是一個隐性的風險。

我們大體可以得出這樣的結論:一些工作量不大的項目或者産品,不太适合使用IOC架構産品。另外,如果團隊成員的知識能力欠缺,對于IOC架構産品缺乏深入的了解,也不要貿然引入。最後,特别強調運作效率的項目或者産品,也不太适合引入IOC架構産品,象WEB2.0網站就是這種情況。

7.IoC的概述

  IoC(控制反轉:Inverse of Control)是Spring容器的核心,AOP、聲明式事務等功能在此基礎上開花結果。它涉及代碼解耦、設計模式、代碼優化等問題的考量。

  Spring通過一個配置檔案描述Bean及Bean之間的依賴關系,利用Java語言的反射功能執行個體化Bean并建立Bean之間的依賴關系。Spring的IoC容器在完成這些底層工作的基礎上,還提供了Bean執行個體緩存、生命周期管理、Bean執行個體代理、事件釋出、資源裝載的進階服務。

 BeanFactory繼承關系圖

Spring IoC,IoC原理

  org.springframework.beans及org.springframework.context包是Spring IoC容器的基礎。Bean工廠(org.springframework.beans.factory.BeanFactory)是Spring架構中最核心的接口,它提供了進階IoC的配置機制。BeanFactory使管理不同類型的Java對象成為可能,ApplicationContext是BeanFactory的擴充,功能得到了進一步增強,比如更易與Spring AOP內建、消息資源處理(國際化處理)、事件傳遞、其它第三方架構(ehCache)內建及各種不同應用層的context實作(如針對web應用的WebApplicationContext)。一般稱BeanFactory為IoC容器,而稱ApplicationContext為應用上下文。

  對于兩者用途,我們可以進行簡單劃分:BeanFactory是Spring架構的基礎設施,面向Spring本身;ApplicationContext面向使用Spring架構的開發者。我們一般都不直接用BeanFactory,而是用它的實作類ApplicationContext,這個類會自動解析我們配置的applicationContext.xml,然後根據我們配置的bean來new對象,将new好的對象放進一個Map中,鍵就是我們bean的id,值就是new的對象。幾乎所有的應用場合我們都直接使用ApplicationContext而非底層的BeanFactory。BeanFactory與ApplicationContext的差別

  由IOC容器管理的那些組成你應用程式的對象我們就叫它Bean, Bean就是由Spring容器初始化、裝配及管理的對象,除此之外,bean就與應用程式中的其他對象沒有什麼差別了。那IOC怎樣确定如何執行個體化Bean、管理Bean之間的依賴關系以及管理Bean呢?這就需要配置中繼資料,在Spring中由BeanDefinition代表,後邊會詳細介紹,配置中繼資料指定如何執行個體化Bean、如何組裝Bean等。

在Spring中,對象間的協作是通過IoC機制完成的。

反向控制也叫依賴注入(Dependency Injection,DI),簡單來說就是将JavaBean需要的對象通過配置檔案加載進來。

Spring提供了兩種裝配Bean的容器,一是BeanFactoy,另一個是ApplicationContext。

兩者做為容器,所有的Bean都應該通過容器裝配,而容器也知道自己都裝配了哪些Bean。

Bean裝配實際上就是讓容器知道程式中都有哪些Bean,可以通過以下兩種方式實作:

配置檔案(最為常用,展現了依賴注入DI的思想)

程式設計方式(寫的過程中向BeanFactory去注冊)

ApplicationContext與BeanFactory都是接口,ApplicationContext是由BeanFactory接口擴充而來,它增強了BeanFactory的功能。

Bean容器能夠加載的對象并不一定是嚴格遵循JavaBeans規範的Java類,任何可執行個體化的類都可以通過Spring Bean容器加載進來。通常稱這些類為POJO。

要記住,BeanFactory不僅僅隻具備執行個體化Bean的功能,它還知道所有的Bean,可以配置和管理它們。

Spring給出一些BeanFactory的實作類,其中最為常用的是XmlBeanFactory。

1、通過檔案系統

Resource res = new FileSystemResource("beans.xml");

XmlBeanFactory factory = new XmlBeanFactory(res);

2、通過類路徑

ClassPathResource res = new ClassPathResource("beans.xml");

3、通過ApplicationContext加載

ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});

BeanFactory factory = (BeanFactory) appContext;

通過Setter方法、通過構造函數、自動裝配三種方法都可以達到裝配Bean的目的。

其中使用setter方法裝配Bean是最為常用的。

先說一下setter方法的裝配:

setter方法可以裝配基本類型、Bean類型、内部bean類型、集合類型、設定空值

所謂Bean類型屬性是指這個屬性類型是個javaBean,并且這個javaBean必須已經在Spring中注冊過了!

自動裝配:

可以通過spring架構自動為bean裝配屬性。

自動裝配隻能裝配bean類型,即取代ref元素。

使用自動裝配隻需在bean元素中加入屬性autowire,自動裝配可被手動裝配覆寫。

也可以選擇在beans中加入default-autowire屬性,為所有bean設定預設自動裝配。

“no“-不使用自動裝配,spring推薦。

“byName”-依據名稱或ID裝配bean,如果沒有找到,則不裝配,可依賴dependency-check屬性檢查是否裝配。

 “byType”-依據類型自動裝配,如果存在兩個以上同種類型,則抛出異常。

“constructor”-依據目前bean構造函數裝配,查找與構造函數參數同類型bean

“autodetect”-自動檢測,先通過constructor,再使用byType

Spring是不推薦使用自動裝配的,首先是系統開銷的問題,還有就是同名的設定可能會導緻意外的問題……

優點

自動裝配可以大大地減少屬性和構造器參數的指派。

自動裝配也可以在解析對象時更新配置。

任何事物有好就有壞,那自動裝配有啥缺點呢?

缺點

在property和constructor-arg設定中的依賴總是重載自動裝配,我們無法對原始類型(如int,long,boolean等就是首字母小寫的那些類型),還有String,Classes做自動裝配。這是受限于設計。

自動裝配跟直接裝配(explicit wiring)相比較,在準确性友善還是差那麼點,雖然沒有明确地說明,但是Spring還是盡量避免這種模棱兩可的情況,導緻出現沒預料到的結果。

Spring容器生成文檔的工具可能會不能使用裝配的資訊。

容器中多個bean的定義可能要對setter和構造器參數做類型比對才能做依賴注入,雖然對于array,collection和map來說不是啥問題,但是對于隻有單一值的依賴來講,這就有點講不清楚了,是以如果沒有唯一的bean定義,那隻能抛出異常。

單例和原型的問題:依賴于singleton參數,true為單例模式。

延遲加載:展現在整個容器初始化的時候立刻加載bean還是等到使用這個bean的時候才加載。

   factory.getBean的時候就已經是開始使用bean了,并不一定要通路其中的屬性才算是使用。

   隻有ApplicationContext才能做到容器初始化的時候立刻加載bean,BeanFactory是做不到的。

注冊bean的時候,name屬性是用的别名,id屬性标明真名。name一般是在web開發裡面去用,name裡面是可以包含一些特殊符号的,用于告訴開發者目前的屬性代表什麼具體的實際意思。

setter方法、構造函數和自動裝配三種裝配bean的方式,見Spring Bean管理

任何一個事物都有自己的生命周期,生命的開始、生命中、生命結束。大家最熟悉的應該是servlet 的生命周期吧。和 servlet 一樣 spring bean 也有自己的生命周期。本文我就向大家講述 spring bean 的生命周期,這個對了解 spring 架構有非常好的作用。

大家應該知道spring 中有幾種供 springbean 生存的容器: BeanFactory 、 ApplicationContext 、webApplicationContext 。由于 ApplicationContext 和 webApplicationContext 基本一樣。所有這裡就隻介紹BeanFactory 和 ApplicationContext 。

了解springBean 的生命周期主要通過兩個層面來了解。其一是 Bean 的作用範圍,其一是執行個體化 Bean 時所經曆的一系列階段。

一、 BeanFactory

下圖描述了BeanFactory 中 bean 生命周期的完整過程

Spring IoC,IoC原理

1. 當調用者通過 getBean( name )向 容器尋找Bean 時,如果容器注冊了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在執行個體 bean 之前,将調用該接口的 postProcessBeforeInstantiation ()方法,

2. 根據配置情況調用 Bean構造函數或工廠方法執行個體化 bean  

3. 如果容器注冊了 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在執行個體 bean 之後,調用該接口的 postProcessAfterInstantiation ()方法,可以在這裡對已經執行個體化的對象進行一些裝飾。

4. 受用依賴注入,Spring 按照 Bean 定義資訊配置 Bean 的所有屬性 ,在設定每個屬性之前将調用InstantiationAwareBeanPostProcess接口的 postProcessPropertyValues ()方法 。 

5 .如果 Bean 實作了 BeanNameAware 接口,工廠調用 Bean 的 setBeanName() 方法傳遞 Bean 的 ID 。 

6 .如果 Bean 實作了 BeanFactoryAware 接口,工廠調用 setBeanFactory() 方法傳入工廠自身。 

7 .如果 BeanPostProcessor 和 Bean 關聯,那麼 将調用該接口 的postProcessBeforeInitialzation() 方法 對 bean進行加工操作,這個非常重要, spring 的 AOP 就是用它實作的。  

8. 如果bean 實作了 InitializingBean 接口,将調用 afterPropertiesSet ()方法

9 如果Bean 指定了 init-method 方法,它将被調用。 

10 如果有BeanPsotProcessor 和 Bean 關聯,那麼它們的 postProcessAfterInitialization() 方法将被調用。 到這個時候, Bean 已經可以被應用系統使用了  。

11. 如果在<bean> 中指定了該 bean 的作用範圍為 scope="prototype", 将 bean 的調用者,調用者管理該 bean 的生命周期, spring 不在管理該 bean 。

12. 如果在<bean> 中指定了該 bean 的作用範圍為 scope="singleton", 則将該 bean 放入 springIOC 的緩存池中,将觸發 spring 對該 bean 的生命周期管理。

13. 有兩種方法可以把它從Bean Factory 中删除掉 :  

1.如果 Bean 實作了 DisposableBean 接口, destory() 方法被調用。 

2.如果指定了訂制的銷毀方法,就調用這個方法。 

總結:

Bean的完整生命周期從 spring 容器開始執行個體化 bean 開始,到銷毀。可以從三點來了解

1、 bean自身的方法:包括構造方法、 set 方法、 init-method 指定的方法、 destroy-method 指定的方法

2、 Bean級生命周期接口方法:如 BeanNameAware 、 BeanFactoryAware 等這些接口方法由 bean類實作。

3、 容器級生命周期接口方法:上圖中帶星的。有InstantiationAwareBeanPostProcessor 、 BeanPostProcessor 等。一般稱為後處理 器。他們一般不由bean 本身實作,獨立存在,注冊到 spring 容器中。 Spring 通過接口反射預先知道,當 spring 容器建立任何 bean 時,這些後處理器都會發生作用。是以他們是全局的,使用者可以通過編碼對隻感興趣的 bean 進行處理。

 Bean級生命周期接口和容器級生命周期接口是個性和共性辯證統一的思想,前者解決 bean 的個性化處理問題,而後者解決容器中某些 bean 共性化處理問題。

二、 ApplicationContext

下圖描述了ApplicationContext 的生命周期

Spring IoC,IoC原理

通過上圖很容易發現其實應該上下文和BeanFactory 隻是多了一個接口, 如果Bean 實作了 ApplicationContextAwre接口, setApplicationContext() 方法被調用。

還有如果配置檔案中生明了工廠後處理器接口 BeanFactoryPostProcessor的實作類,則應用上下文在裝配配置檔案之後初始化 bean 之前将調用該接口對配置資訊進行加工。

還有應該上下文的這些後處理器隻要和普通的bean 一樣配置在 spring 配置檔案中就行了,不需要事先聲明。

三、 總結  

Spring為 bean 提供了細緻周全的生命周期過程,通過實作特定的接口或通過《 bean 》屬性設定,都可以對 bean 的生命周期過程施加影響。我們可以随意的配置 bean 的屬性,使用非常靈活。但筆者在這裡建議大家不要過多的使用 bean 實作接口,因為這樣會使你的代碼和 spring 聚合比較緊密。可以考慮使用後處理 bean ,來實作一些特殊的功能,并且非常的友善。

一、Spring Framework支援五種作用域(其中有三種隻能用在基于web的Spring ApplicationContext)。 

内置支援的作用域分列如下:

當通過spring容器建立一個Bean執行個體時,不僅可以完成Bean執行個體的執行個體化,還可以為Bean指定特定的作用域。Spring支援如下5種作用域:

singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的Bean将隻有一個執行個體

prototype:原型模式,每次通過容器的getBean方法擷取prototype定義的Bean時,都将産生一個新的Bean執行個體

request:對于每次HTTP請求,使用request定義的Bean都将産生一個新執行個體,即每次HTTP請求将會産生不同的Bean執行個體。隻有在Web應用中使用Spring時,該作用域才有效

session:對于每次HTTP Session,使用session定義的Bean都将産生一個新執行個體。同樣隻有在Web應用中使用Spring時,該作用域才有效

globalsession:每個全局的HTTP Session,使用session定義的Bean都将産生一個新執行個體。典型情況下,僅在使用portlet context的時候有效。同樣隻有在Web應用中使用Spring時,該作用域才有效

  其中比較常用的是singleton和prototype兩種作用域。對于singleton作用域的Bean,每次請求該Bean都将獲得相同的執行個體。容器負責跟蹤Bean執行個體的狀态,負責維護Bean執行個體的生命周期行為;如果一個Bean被設定成prototype作用域,程式每次請求該id的Bean,Spring都會建立一個Bean執行個體,然後傳回給程式。在這種情況下,Spring容器僅僅使用new 關鍵字建立Bean執行個體,一旦建立成功,容器不在跟蹤執行個體,也不會維護Bean執行個體的狀态。

  如果不指定Bean的作用域,Spring預設使用singleton作用域。Java在建立Java執行個體時,需要進行記憶體申請;銷毀執行個體時,需要完成垃圾回收,這些工作都會導緻系統開銷的增加。是以,prototype作用域Bean的建立、銷毀代價比較大。而singleton作用域的Bean執行個體一旦建立成功,可以重複使用。是以,除非必要,否則盡量避免将Bean被設定成prototype作用域。

Spring Bean線程安全這個問題,要從單例與原型Bean分别進行說明。

prototype Bean:對于原型Bean,每次建立一個新對象,也就是線程之間并不存在Bean共享,自然是不會有線程安全的問題。

singleton Bean:對于單例Bean,所有線程都共享一個單例執行個體Bean,是以是存在資源的競争。

如果singleton Bean,是一個無狀态Bean,也就是線程中的操作不會對Bean的成員執行查詢以外的操作,那麼這個單例Bean是線程安全的。比如Spring mvc 的 Controller、Service、Dao等,這些Bean大多是無狀态的,隻關注于方法本身。

有狀态對象(Stateful Bean) :就是有執行個體變量的對象,可以儲存資料,是非線程安全的。每個使用者有自己特有的一個執行個體,在使用者的生存期内,bean保持了使用者的資訊,即“有狀态”;一旦使用者滅亡(調用結束或執行個體結束),bean的生命期也告結束。即每個使用者最初都會得到一個初始的bean。

無狀态對象(Stateless Bean):就是沒有執行個體變量的對象,不能儲存資料,是不變類,是線程安全的。bean一旦執行個體化就被加進會話池中,各個使用者都可以共用。即使使用者已經消亡,bean 的生命期也不一定結束,它可能依然存在于會話池中,供其他使用者調用。由于沒有特定的使用者,那麼也就不能保持某一使用者的狀态,是以叫無狀态bean。但無狀态會話bean 并非沒有狀态,如果它有自己的屬性(變量),那麼這些變量就會受到所有調用它的使用者的影響,這是在實際應用中必須注意的。

對于有狀态的bean,Spring官方提供的bean,一般提供了通過ThreadLocal去解決線程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。

使用ThreadLocal的好處

使得多線程場景下,多個線程對這個單例Bean的成員變量并不存在資源的競争,因為ThreadLocal為每個線程儲存線程私有的資料。這是一種以空間換時間的方式。

當然也可以通過加鎖的方法來解決線程安全,這種以時間換空間的場景在高并發場景下顯然是不實際的。

       Spring 容器對bean沒有特殊要求,甚至不要求該bean 像标準的JavaBean一一必須為每個屬性提供對應的getter和setter 方法。

        Spring中的bean 比JavaBean 的功能要強大,用法也更複雜。當然,傳統JavaBean也可作為普通的Spring bean ,可接受Spring管理。下面的代碼示範了Spring的bean執行個體,該bean執行個體是資料源,提供資料庫連接配接:

        主程式部分由BeanFactory來擷取該bean 的執行個體,擷取執行個體時使用bean的唯一辨別符: id 屬性。該屬性是bean 執行個體在容器中的通路點。下面是主程式部分:

        從該執行個體可以看出, Spring 的bean 遠遠超出值對象的JavaBean範疇,此時bean可以代表應用中的任何元件及任何資源執行個體。

        雖然Spring 對bean沒有特殊要求,但筆者還是建議在Spring 中的bean應滿足如下幾個原則:

        (1)每個bean實作類都應提供無參數的構造器。

        (2)接受構造注入的bean,則應提供對應的構造函數。

        (3)接受設值注入的bean,則應提供對應的setter方法,并不強制要求提供對應的getter方法。

        傳統的JavaBean和SpringBean存在如下的差別。

        (1)用處不同:傳統JavaBean 更多地作為值對象傳遞參數,而Spring 中的bean用處幾乎無所不在,任何應用元件都可被稱為bean。

        (2)寫法不同:傳統JavaBean 作為值對象,要求每個屬性都提供getter 和setter 方法;但Spring 中的bean 隻需為接受設值注入的屬性提供setter 方法。

       (3)生命周期不同:傳統lavaBean作為值對象傳遞,不接收任何容器管理其生命周期;Spring 中的bean由Spring 管理其生命周期行為。