前言:對於使用Spring框架的開發人員來說,我們主要做的主要有兩件事情:①開發Bean;②配置Bean;而Spring幫我們做的就是根據配置文件來創建Bean實例,並調用Bean實例的方法來完成“依賴注入”,可以把Spring容器理解成一個大型工廠,Bean就是該工廠的產品,工廠(Spirng容器)里能生產出來什么樣的產品(Bean),完全取決於我們在配置文件中的配置。我們那么今天我們就來說說關於Bean的故事。。。
容器中Bean的作用域
singleton | 在整個Spring IoC 容器中,使用 singleton 定義的Bean將只有一個實例 |
prototype | 原型模式,每次通過容器的getBean 方法獲取prototype定義的Bean 時,都將產生一個新的Bean實例 |
request | 對於每次HTTP請求,使用request定義的Bean都將產生一個新的實例,每次HTTP請求都將產生不同的Bean實例,該作用域僅在給予web的Spring ApplicationContext情形下有效 |
session | 對於每次HTTP Session ,使用session定義的Bean都將產生一個新實例,該作用域僅在給予web的Spring ApplicationContext情形下有效 |
global session | 每個全局得HTTP Session對應一個Bean實例,該作用域僅在給予web的Spring ApplicationContext情形下有效 |
比較常用的是singleton 和 prototype 兩種作用域,對於singleton作用域,每次請求該Bean都將獲得相同的實例,Spring容器負責跟蹤監視Bean實例的狀態,負責維護Bean實例的生命周期行為,如果一個Bean被設置成prototype作用域,程序每次請求該id的Bean,Spring都會創建一個新的Bean實例,然后返回給程序,在這種情況下,Spring容器僅僅使用new 關鍵字創建Bean實例,一旦創建成功,容器Spring不再對Bean的生命周期負責,也不會維護Bean實例的狀態。
如果不指定Bean的作用域,Spring默認使用singleton作用域。Java在常見Java實例時,需要進行內存申請,銷毀實例是,需要完成垃圾回收,這些工作都會導致系統開銷的增加。因此prototype作用域Bean 的創建銷毀代價比較大。而singleton作用域的Bean 實例一旦創建成功,可以重復使用,因此,除非必要,否則避免將Bean作用域設置成prototype。
如果要使用 request,session,global session作用域的Bean,在配置Bean之前,還需要做少量的初始配置(將HTTP 請求banding到該提供服務的線程上,這使得具有request 和 session作用域的Bean實例能夠在后面的調用鏈中被訪問到),如果只是配置常規的作用域(singleton,prototype),則無須設置。如果Web 應用直接使用Spring MVC 作為MVC框架,即使用SpringDispatcherServlet 或DispatcherPorlet 來攔截所有用戶請求,則無須設置,因為DispatcherServlet 和DispatcherPorlet已經處理了所有和請求有關的額狀態處理。
當使用Spring's DispatcherServlet 以外的Servlet 2.4 及以上規范的Web 容器時需要在“web.xml”中增加如下配置:
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
如果使用的是早起版本的Web容器,則該容器不支持Listener 規范,故無法使用上述配置,只能改為Filter配置方式,配置代碼如下:
<filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
容器中Bean的生命周期
Spring可以管理singleton作用域Bean的生命周期,Spring可以精確地知道singleton域bean何時被創建,何時初始化完成,以及容器何時准備銷毀Bean實例。因為,對於singleton作用域的Bean,客戶端的每次請求都返回同一個Bean實例,客戶端代碼不能控制Bean的銷毀,它的生命周期都在Spring的掌握之中。這么一來,容器就可以管理實例化結束后(某些資源的申請)和銷毀之前(進行某些資源的回收)的行為。管理Bean的生命周期行為主要有兩個時機:注入依賴關系后,銷毀實例之前;具體的管理方法如下
Spring提供兩種方式在Bean全部屬性設置成功后執行特定行為
使用init-method 屬性(代碼污染小)
在類中編寫一個方法,在屬性中指定該方法在依賴關系設置完成后自動執行。
實現InitializingBean接口(耦合較高)
編寫afterPropertiesSet()方法的具體實現
同理,若在Bean銷毀之前,執行特定的方法,只需要①使用 destroy-method屬性②實現DisposableBean接口(實現destroy()方法)
對於prototype作用域的Bean,Spring容器只負責Bean的創建,當容器創建實例完成后,Bean將完全交給客戶端代碼管理,容器不再負責其生命周期。每次客戶端請求prototype作用域的Bean時,Spring容器都會產生一個全新的Bean實例交個客戶端(prototype就是這么任性),Spring容器本省也不知道自己創建了多少個實例,更無從知道這些實例什么時候才會被銷毀。
Spring 的 Bean 和 JavaBean比較
-
- 規范:Spring容器對Bean 沒有特殊要求,不像JavaBean 一樣遵循一些規范(為每個屬性提供相應的setter 和 getter 方法),不過對於設值注入的Bean,一定要提供setter 方法。
- 作用:Spring 中的Bean 是 java 實例,java組件,它的作用幾乎無所不包,任何應用組件都被稱為Bean,而傳統的Java應用中的JavaBean通常作為DTO(數據傳輸對象),來封裝值對象,在各層之間傳遞數據。
- 生命周期:傳統的JavaBean作為值對象傳遞,不接受任何容器管理其生命周期,Spring中的Bean有Spring管理其生命周期行為。
Spring 的Bean繼承和java繼承的區別
-
- Spring中子Bean和父Bean可以是不同的類型,而java中的繼承子類只是父類的一種特殊類型。
- Spring中Bean的繼承是實例之間的關系,主要表現為參數值的延續,而java終的繼承是類之間的關系,主要表現為屬性,方法的延續。
- Spring中子Bean不可做父Bean使用,不具備多態性,java中的子類實例完全可以做父類的實例使用
Bean 實例的創建方式及依賴配置
大多數情況下,BeanFactory 直接通過new 關鍵字調用構造器來創建Bean 實例,而class屬性指定了Bean實例的實現類。但這並不是實例化Bean的唯一方法。
創建Bean通常有以下三種方式
-
- 調用構造器創建Bean 實例
- 調用靜態工廠方法創建Bean
- 調用實例工廠方法創建Bean
-
- 調用構造器創建Bean 實例
調用構造器創建Bean實例是最常見的情況,,BeanFactory 將使用默認的構造器來創建Bean實例,該實例是個默認實例,Spring對Bean實例的所有屬性執行默認初始化,即所有基本類型的值初始化為0或false,引用類型初始化為null,接下來BeanFactory會根據配置文件決定依賴關系,先實例化被依賴的Bean 實例,然后為Bean注入依賴關系,最后將一個完整的Bean實例返回給程序,該Bean實例化的所有屬性,已經由Spring容器完成了初始化。
-
- 使用靜態工廠方法創建Bean
使用靜態工廠方法創建Bean實例時,class屬性也必須指定,但此時class屬性並不是指定Bean實例的實現類,而是靜態工廠類,Spring需要根據工廠類的工廠方法來創建Bean實例,除此之外,還需要使用factory-method 屬性來指定靜態工廠方法名,Spring調用靜態工廠方法(可能包含一組參數,若需要,使用<constructor-arg.../>元素傳入)返回一個Bean實例,獲得Bean實例后,Spring后面的處理步驟與采用普通方法創建Bean實例完全一樣。
使用靜態工廠方法創建Bean實例時,Spring將先解析配置文件,並根據配置文件指定的信息,通過反射調用靜態工廠類的靜態工廠方法,將該靜態工廠方法的凡湖值作為Bean實例。在這個過程中,Spring不再負責創建Bean實例,Bean實例的創建是用戶提供的靜態工廠類負責創建的。但是Spring容器依然可以管理Bean實例的依賴關系,生命周期。
-
- 使用實例工廠方法
實例工廠方法與靜態工廠方法不同:調用靜態工廠方法只需使用工廠類,調用實例工廠方法則必須使用工廠實例。采用實例工廠方法時,配置Bean實例的<bean../>元素無需class屬性,因為Spring容器不再直接實例化該Bean,Spring容器僅僅調用實例工廠的工廠方法,工廠方法負責創建Bean實例。采用實例工廠方法創建Bean時,需要為<bean../>元素指定如下兩個屬性:factory-bean:該屬性的值為工廠Bean的id.factory-method:該屬性指定實例工廠的工廠方法。與靜態工廠方法相似,如果需要調用工廠方法時傳入參數,使用<constructor-arg.../>元素確定參數值。
靜態工廠方法創建實例和工廠方法創建實例的異同點:
調用實例工廠方法創建Bean,必須將實例工廠配置成Bean實例。而靜態工廠方法創建Bean,則無需配置工廠Bean。
調用實例工廠方法創建Bean,必須使用factory-bean屬性確定工廠Bean。而靜態工廠方法創建Bean,則使用class 元素確定靜態工廠類。
都需要factory-method 指定相應產生Bean實例的工廠方法。
工廠方法需要參數都可以使用<constructor-art.../>元素指定參數。