有個類實現(xiàn)了org.springframework.beans.factory.support.MethodReplacer
接口材义,類中有新的方法定義
/**
* 意味著用來重寫MyValueCalculator類中computeValue(String)方法的實現(xiàn)
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
bean定義抓督,用來部署的源類孩等,要設(shè)置方法重寫幕屹,大概這么搞:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- arbitrary method replacement -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
可以在<replaced-method/>
元素內(nèi)設(shè)置一個或多個<arg-type/>
元素來指明被替換方法的參數(shù)類型满葛。只有被覆蓋的方法在類有重載昌罩,參數(shù)簽名才是必要的。為了方便饭冬,String
類型的參數(shù)只需要其完全限定類型名稱的字串即可使鹅。比如昌抠,下面列出的均可匹配java.lang.String
:
java.lang.String
String
Str
因為參數(shù)的數(shù)量基本就可以確定方法(重載的方法患朱,基本上是參數(shù)數(shù)量有區(qū)別),此簡寫能大量減少打字,讓你僅打幾個字符就能匹配參數(shù)類型炊苫。
譯注裁厅,Spring是工業(yè)品質(zhì)的框架,如此細微的人性化設(shè)計侨艾,值得學(xué)習(xí)
<h3 id='beans-factory-scopes'>bean作用域</h3>
Spring bean定義時执虹,實際上是創(chuàng)建類實例的配方。這個觀點非常重要唠梨,因為她意味著袋励,通過一個配方,即可創(chuàng)建很多類的對象当叭。
對于依據(jù)bean定義產(chǎn)生的bean,不僅可以控制依賴茬故、設(shè)置對象的值,還可以對象作用域蚁鳖。這個手法強大而靈活磺芭,因為在配置過程中就可以可以控制的bean的作用域,無需在代碼層面去控制才睹,用代碼去控制簡直就是煎熬徘跪。要部署的bean可有設(shè)置1個或多個作用域:開箱即用,Spring框架支持5中作用域琅攘,其中有三種只有用web-awareApplicationContext
才能使用垮庐。
下面了列出的作用域開箱即用,你也可以自定義作用域
Table 5.3. Bean scopes
作用域 | 描述 |
---|---|
單例singleton | 默認的坞琴。一個bean定義哨查,在一個IoC容器內(nèi)只會產(chǎn)生一個對象。 |
prototype原型 | 一個bean定義會產(chǎn)生多個對象實例 |
request請求 | 一個bean定義產(chǎn)生的bean生命周期為一個HTTP請求剧辐;也就是寒亥,每一個HTTP請求都會根據(jù)bean定義產(chǎn)生一個對象實例。該作用域只有在Spring web上下文環(huán)境中才有效荧关。 |
session會話 | 產(chǎn)生的bean生命周期在HTTP 會話期間溉奕。該作用域只有在Spring web上下文環(huán)境中才有效 |
gloabal session全局session | 聲明周期為全局HTTP會話。通常使用portlet context時常用忍啤。該作用域只有在Spring web上下文環(huán)境中才有效加勤。 |
application應(yīng)用 | 生命周期與ServletContext 一樣。該作用域只有在Spring web上下文環(huán)境中才有效 |
Spring3.0起 多了一個作用域-thred,但它默認是未注冊的(不可用的意思?)。詳情請參看文檔去吧
SimpleThreadScope
鳄梅。有關(guān)如何注冊該作用域和注冊自定義作用域叠国,參看本章使用自定義作用域
<h4 id="#beans-factory-scopes-singleton">單例作用域</h4>
單例bean只會產(chǎn)生一個實例,對于所有的請求,Spring容器都只會返回一個實例戴尸。
換句話說,當定義了單例bean粟焊,Srping容器只會創(chuàng)建一個實例,這個實例存儲在單例池中孙蒙,單例池應(yīng)該屬于緩存项棠,接下來所有對于該單例bean的請求和引用,都將返回緩存中的對象马篮。
Figure 5.2.
Spring單例bean的概念沾乘,和四人幫GOF那本《設(shè)計模式》中定義的單例模式不同。GOF的單例是硬編碼級的對象作用域浑测,因此導(dǎo)致每一個類加載器內(nèi)會產(chǎn)生單例類的一個實例。Spring的單例恰如其名歪玲,在容器范圍內(nèi)只會產(chǎn)生一個類實例迁央。Spring中,bean默認的作用域都是單例作用域滥崩。使用xml 定義單例bean岖圈,像這樣:
<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- 和下面的寫法相等,因為單例作用域是默認的钙皮,所以這么寫有些畫蛇添足蜂科,意思就是廢話了 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
<h4 id='beans-factory-scopes-prototype'>prototype原型作用域</h4>
設(shè)置bean作用域為prototype
,就是非單例,對于每次請求都將返回一個該類的新實例短条。也就是說导匣,原型bean注入另一個bean,或者是請求原型bean茸时,都是通過在容器上調(diào)用getBean()
方法產(chǎn)生的贡定。一般來說 ,原型bean用于有狀態(tài)bean可都,單例bean用于無狀態(tài)bean缓待。
下圖示例了Srping原型作用域。一個數(shù)據(jù)訪問對象(DAO)通常不會配置成原型作用域,因為通常DAO不會持有任何會話狀態(tài)渠牲;因為作者偷懶旋炒,所以重用了上面單例示意圖。
接下來看看如何在XML中定義原型bean:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
和其他作用域相比签杈,Srping并不管理原型bean的完整的生命周期:容器實例化瘫镇,配置或者組裝原型獨享,注入給其他類,然后并未進一步記錄那個原型bean汇四。因此接奈,盡管對象的初始化回調(diào)方法會調(diào)用,不受scope影響,但是對于原型bean,銷毀回調(diào)不會被調(diào)用通孽⌒蚧拢客戶端代碼必須清理原型對象并且釋放原型bean持有的資源。為了讓Spring容器釋放原型bean持有的資源背苦,可以用自定義的bean[post-processor](#beans-factory-extension-bpp)
,
它持有需要被清理bean的引用互捌。
某種意義上,對于原型bean來說,Spring容器的角色就是替換了new 操作符行剂。所有的生命周期管理秕噪,在經(jīng)過實例化之后,都需要由客戶端來處理厚宰。(Spring 容器中bean的生命周期詳情腌巾,請參看本章5.6.1生命周期回調(diào))
<h4 id='beans-factory-scopes-sing-prot-interaction'>單例依賴原型</h4>
單例類依賴了原型類,要知道依賴在單例類初始化的時候就已經(jīng)注入好了铲觉。因此澈蝙,若你注入了一個原型bean給單例bean,將會是一個新的原型bean的實例注入了單例bean實例撵幽。原型bean實例將會是唯一的實例灯荧,再也不會為單例bean產(chǎn)生新的實例。
假若你需要單例bean在運行時重復(fù)的獲取新的原型bean實例盐杂。那就不能將原型bean注入給單例bean逗载,因為那樣注入只會發(fā)生一次,就是發(fā)生在在Srping容器實例化單例bean并解析注入依賴時链烈。如果需要多次獲取新的原型bean實例厉斟,參看本章5.4.6方法注入
<h4 id='beans-factory-scopes-other'> Request, session, and global session scopes</h4>
request
,session
,global session
作用域,只有在spring web ApplicationContext
的實現(xiàn)中(比如XmlWebApplicationContext
)才會起作用测垛,若在常規(guī)Spring IoC容器中使用捏膨,比如ClassPathXmlApplicationContext
中沃暗,就會收到一個異常IllegalStateException
來告訴你不能識別的bean作用域
<h5 id='beans-factory-scopes-other-web-configuration'>初始化web配置</h5>
為了支持request,sesssion,global session
這種級別bean的作用域(web作用域bean)跋破,在定義bean之前需要一些初始化的小配置。(Spring標準作用域青扔,包括單例和原型锯七,無需此配置链快。)
如何配置要根據(jù)具體的Servlet
環(huán)境
若使用 Spring Web MVC訪問這些作用域bean,實際上是使用Srping DispatcherServlet
類或者DispatcherPortlet
類處理request眉尸,則無需特別配置:DispatcherServlet
和 DispatcherPortlet
已經(jīng)暴露了所有的相關(guān)狀態(tài)域蜗。
若使用了Servlet 2.5的web容器巨双,使用了非Spring的DispacherServlet
處理請求(比如,JSF或者Struts)霉祸,則需要注冊org.springframework.web.context.request.RequestContextListener ServletRequestListener
筑累。若使用的Servlet 3.0+,這些設(shè)置可以通過編程式方式使用WebApplicationInitializer
接口完成丝蹭。若使用的是較老的容器,增加下面配置添加到你的web應(yīng)用的web.xml
文件中:
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
...
</web-app>
如果設(shè)置listener
有問題的話慢宗,可以考慮使用RequestContextFilter
。filter映射要根據(jù)web 應(yīng)用配置來調(diào)整:
<web-app>
...
<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>
...
</web-app>
DispatcherServlet
,RequestContextListener
,RequestContextFilter
都是做相同的事兒奔穿,也就是綁定HTTP
request對象到服務(wù)的Thread
線程中镜沽,并開啟接下來
用到的session-scoped
功能。
<h5 id='beans-factory-scopes-request'>Request作用域</h5>
考慮下面這種bean定義:
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
Spring 使用該bean定義為每一次HTTP 請求創(chuàng)建一個新的LoginAction
bean 的實例贱田。也就是,loginAction
bean作用域范圍在HTTP 請求級別缅茉。可以改變實例的內(nèi)部狀態(tài)男摧,多少實例都可以蔬墩,因為根據(jù)此loginAciton
bean定義創(chuàng)建的其他bean實例并不會看到這些狀態(tài)的改變;他們?yōu)楦髯缘膔equest擁有彩倚。當reqeust完成處理筹我,request作用的bean就被丟棄了。
<h5 id="beans-factory-scopes-session">session作用域</h5>
考慮下面這種bean定義:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
在一個session會話期間,Spring容器使用userPreferences
定義創(chuàng)建了一個UserPreferences
bean的實例帆离。換句話說userPreferences
bean在HTTP Session會話期間有效。和request-scoped
bean相類似,可以改變bean實例的內(nèi)部狀態(tài)结澄,不管bean創(chuàng)建了多少實例都可以哥谷,要知道,使用相同的userPreferences
定義創(chuàng)建的其他的bean實例看不到這些狀態(tài)的改變麻献,因為他們都是為各自的HTTP Session服務(wù)的们妥。當HTTP Session最終被丟棄時,該session內(nèi)的session-scoped
作用域的bean實例也會被丟棄勉吻。
<h5 id="beans-factory-scopes-global-session">全局Session作用域</h5>
考慮下面這種bean定義:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
全局session作用域與標準HTTP Session作用域類似监婶,僅能應(yīng)用于基于portlet的web應(yīng)用的上下文環(huán)境中。portlet規(guī)范中定義的global Session
概念是齿桃,在由單個portlet web應(yīng)用創(chuàng)建的所有的的portlets中共享惑惶。全局session作用域的bean和global portlet Session
全局portlet會話生命周期相同。
若是在標準的基于Servelt web應(yīng)用中定義了全局session作用域bean短纵,那么將會使用標準的Session作用域,不會報錯带污。
<h5 id="beans-factory-scopes-application">應(yīng)用作用域</h5>
考慮下面這種bean定義:
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
Spring 容器使用該定義為整個web應(yīng)用創(chuàng)建一個AppPreferences
bean的實例。appPreFerences
bean作用域是ServeletContext
級別,存儲為一個常規(guī)的ServletContext
屬性香到。這個Spring單例作用域有幾分相似鱼冀,但是和單例作用域相比有兩個重要不同:1报破、他是每一個ServeltContext
一個實例,而不是SpringApplicationContext
范圍千绪。2充易、它是直接暴露的,作為ServletContext
屬性荸型,因此可見盹靴。
<h5 id='beans-factory-scopes-other-injection'>不同級別作用域bean之間依賴</h5>
Spring IoC容器不僅管理bean的實例化,也負責(zé)組裝(或者依賴)帆疟。如果想將HTTP request作用域bean注入給其他bean鹉究,就得給作用域bean(request或者session)注入一個AOP代理用來替換作用域bean。通過注入一個代理對象暴露于作用域bean相同的的接口踪宠,他是代理對象也能從相關(guān)作用域(request或者session)中檢索到真正的被代理對象,并委派方法調(diào)用實際對象的方法自赔。
不需要再單例或者原型bean內(nèi)部使用
<aop:scoped-proxy/>
下面的配置雖然簡單,但是重要的理解“為什么”和“如何搞”
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- instructs the container to proxy the surrounding bean -->
<aop:scoped-proxy/>
</bean>
<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
為了創(chuàng)建一個代理柳琢,得在作用域bean定義內(nèi)插入子元素<aop:scoped-proxy/>
绍妨。詳情參看 “Choosing the type of proxy to create” 和 Chapter 34, XML Schema-based configuration.)。request
, session
, globalSession
, custom-scope
柬脸,為什么這些級別的作用域需要<aop:scoped-proxy/>
元素他去? 下面來做個小測驗,一個單例bean定義倒堕,對比一下灾测,它如果要實現(xiàn)前面提到的作用域bean注入,該如何配置垦巴。(下面的userPreferences
bean媳搪,實際上并不完整)。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
上例中骤宣,HTTP Session作用域beanuserPreferences
注入給了單例beanuserManger
秦爆。注意,userManager
bean是一個單例bean:每個容器只會實例化一個憔披,他的依賴(本例中只有一個等限,userPreferences
bean)也僅會注入一次。也就是說芬膝,userManager
bean只能操作相同的userPreferences
對象望门,就是注入的那一個。
將一個短生命周期作用域bean注入給長生命周期作用域bean蔗候,比如將HTTP Session作用域bean作為依賴注入給一個單例bean怒允。然而,你需要一個userManager
對象锈遥,在HTTP Session會話期間纫事,需要與session同生命周期的對象userPreferences
勘畔。 因此,容器會創(chuàng)建一個對象丽惶,該對象擁有和UserPreferences
完全相同的public接口并暴露所有的public接口炫七。,該對象能根據(jù)作用域機制獲取真真的UserPreferences
對象钾唬。容器會將這個代理對象注入給userManager
bean,userManager
類則渾然不知這貨居然是個代理万哪。樣例中,當UserManager
實例調(diào)用依賴UserPreferences
對象上的方法時抡秆,奕巍,實際上調(diào)用的是代理對象上的方法。代理對象從 Session
范圍內(nèi)獲取真正的UserPreferences
對象儒士,并將在代理對象上方法的調(diào)用“呼叫轉(zhuǎn)移”給檢索到的真正的UserPreferences
對象的止。
將一個request
,session
,globalSession
作用域bean注入給其他作用域bean,下面是正確的着撩、完整的配置
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
<h5 id='beans-factory-scopes-other-injection-proxies'>選擇代理類型</h5>
使用<aop:scoped-proxy/>
元素為bean 創(chuàng)建代理時诅福,Spring 容器默認使用CGLIB
類型創(chuàng)建代理。
CGLIB代理只會攔截public方法調(diào)用拖叙。非public方法不會“呼叫轉(zhuǎn)移”給實際的作用域bean氓润。
還有個選擇,通過配置薯鳍,使Spring容器為這些作用域bean創(chuàng)建標準的JDK interface-based
代理,設(shè)置<aop:scoped-proxy/>
元素proxy-target-class
屬性的值為false
即可咖气。使用標準JDK接口代理好處是無需引入第三方j(luò)ar包。然而挖滤,作用域bean 至少實現(xiàn)一個接口采章,需要注入作用域bean的類則依賴這些接口。
<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
<bean id="userManager" class="com.foo.UserManager">
<property name="userPreferences" ref="userPreferences"/>
</bean>
For more detailed information about choosing class-based or interface-based proxying, see Section 9.6, “Proxying mechanisms”.
關(guān)于如何選擇class-based
和interface-based
代理壶辜,詳情參看Section 9.6, “Proxying mechanisms”.
<h4 id='beans-factory-scopes-custom'>自定義作用域</h4>
bean的作用域機制是可擴展的;可以定義自己的作用域担租,甚至重新定義已存在的作用域砸民,經(jīng)管后者不推薦,并且奋救,不能重寫內(nèi)置單例作用域和原型作用域岭参。
<h5 id='beans-factory-scopes-custom-creating'>創(chuàng)建自定義作用域</h5>
實現(xiàn)org.springframework.beans.factory.config.Scope
接口,就可以將自定義作用域集成到Srping容器中,本章主要將如何實現(xiàn)該接口尝艘。如何實現(xiàn)自定義作用域演侯,參看Spring內(nèi)置的作用域?qū)崿F(xiàn)和Scope
類的javadocs,javadocs中解釋了有關(guān)需要實現(xiàn)的方法的細節(jié)。
Scope
接口共有4個方法用于從作用域獲取對象背亥、從作用域刪除對象秒际、銷毀對象(應(yīng)該是指作用域內(nèi)悬赏,英文檔中未提到)
下面的方法作用是返回作用域中對象。比如娄徊,session
作用域的實現(xiàn)闽颇,該方法返回session-scoped
會話作用域bean(若不存在,方法創(chuàng)建該bean的實例寄锐,并綁定到session會話中兵多,用于引用,然后返回該對象)
Object get(String name, ObjectFactory objectFactory)
下面的方法作用是從作用域中刪除對象橄仆。以session
作用域?qū)崿F(xiàn)為例,方法內(nèi)刪除對象后剩膘,會返回該對象,但是若找不到指定對象盆顾,則會返回null
Object remove(String name)
下面的方法作用是注冊銷毀回調(diào)函數(shù)怠褐,銷毀是指對象銷毀或者是作用域內(nèi)對象銷毀。銷毀回調(diào)的詳情請參看javadocs或者Spring 作用域?qū)崿F(xiàn)椎扬。
void registerDestructionCallback(String name, Runnable destructionCallback)
下面的方法惫搏,用于獲取作用域會話標識。每個作用域的標識都不一樣蚕涤。比如筐赔,session
作用域的實現(xiàn)中,標識就是session
標識(應(yīng)該是指sessionId吧)
String getConversationId()
<h5 id='beans-factory-scopes-custom-using'>使用自定義作用域</h5>
可能是HelloWorld
揖铜,或者是更多的自定義的Scope
實現(xiàn),得讓Spring知道新寫的作用域茴丰。下面的方法就是如何注冊新的作用域到Spring 容器的核心方法:
void registerScope(String scopeName, Scope scope);
This method is declared on the ConfigurableBeanFactory interface, which is available on most of the concrete ApplicationContext implementations that ship with Spring via the BeanFactory property.
此方法聲明在ConfigurableBeanFactory
接口中,該在大部分ApplicationContext
具體實現(xiàn)中都是可用的,通過BeanFactor
屬性設(shè)置
registerScope(..)
方法第一個參數(shù)是作用域名稱天吓,該名稱具有唯一性贿肩。比如Spring容器內(nèi)置的作用域singleton
和prototype
。第二個參數(shù)是自定義作用域?qū)崿F(xiàn)的實例龄寞,就是你想注冊的汰规、使用的那個自定義作用域。
寫好了自定義作用域的實現(xiàn)物邑,就可以像下面那樣注冊它了:
下面的
SimpleThreadScope
作用域溜哮,是Spring內(nèi)置的,但是默認并未注冊到容器中色解。
你自定義的作用域?qū)崿F(xiàn)茂嗓,應(yīng)該也使用相同的代碼來注冊。
Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
接下來是創(chuàng)建一個bean定義科阎,該定義要遵守自定義作用域的規(guī)則
<bean id="..." class="..." scope="thread">
自定義作用域的實現(xiàn),不局限于編程式注冊述吸。也可以使用CustomScopeConfigurer
類聲明式注冊
*譯注*,編程式就是指硬編碼,hard-code锣笨,聲明式就是指配置蝌矛,可以是xml可以是注解總之無需直接使用代碼去撰寫相關(guān)代碼道批。不得不說,*編程式和聲明式*與*硬編碼和配置*相比朴读,更加高端大氣上檔次屹徘。技術(shù)人員尤其要學(xué)習(xí)這種官方的、概念性的衅金、抽象的上檔次的語言或者說式地道的表達噪伊,假若談吐用的全是這種詞匯,逼格至少提升50%氮唯,鎮(zhèn)住其他人(入行時間不長的同行鉴吹,或者面試官)的概率將大大提升。當然了惩琉,和生人談吐要用高逼格詞匯豆励,比如*聲明式*,*編程式*瞒渠,然而和自己人就要用人話了良蒸,比如*硬編碼*,*xml配置*,因為他們得能先聽懂才能干活伍玖。
總之嫩痰,**裝逼用官話,聊天用人話**,閑話少絮窍箍,看如何聲明式注冊(因為此處要裝逼串纺,人話是看如何xml)
blablablab
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
<bean id="bar" class="x.y.Bar" scope="thread">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean>
<bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</bean>
</beans>
When you place <aop:scoped-proxy/> in a FactoryBean implementation, it is the factory bean itself that is scoped, not the object returned from getObject().
如果在FactoryBean
實現(xiàn)中設(shè)置了<aop:scoped-proxy/>
,表示是工廠bean他本身的作用域椰棘,并不是getObject()
返回的對象的作用域纺棺。TODO
<h3 id='beans-factory-nature'>Customizing the nature of a bean自定義bean的xxx擦這個nature該怎么翻</h3>
<h4 id='beans-factory-lifecycle'>生命周期回調(diào)函數(shù)</h4>
Spring容器可以控制bean的生命周期,通過實現(xiàn)SpringInitializingBean
和DisposableBean
接口。容器會調(diào)用InitializingBean
接口的afterPropertiesSet()
方法,也會調(diào)用DisposableBean
接口的destroy()
方法邪狞。,也就是運行bean自定義的初始化方法和銷毀方法祷蝌。
Tip
JSR-250中,在現(xiàn)代Spring應(yīng)用中,一般都是用@PostConstruct
和@PreDestroy
注解定義生命周期回調(diào)函數(shù)帆卓。使用注解的話杆逗,你的bean就無需和Spring API耦合了。鳞疲,詳情參看Section 5.9.7, “@PostConstruct and @PreDestroy”
如果不想用JSR-250,但又想解耦(Spring API)蠕蚜,可以在定義對象的配置中指定init-method
和destroy-method
Spring使用BeanPostProcessor
實現(xiàn)類處理所有的回調(diào)接口并調(diào)用相應(yīng)的方法尚洽,接口由Spring 負責(zé)查找。若需要自定義功能或其他生命周期行為靶累,Spring并未提供開箱即用的支持,但是可以自己實現(xiàn)BeanPostProcessor
類腺毫。詳情參看"Section 5.8, “Container Extension Points”
除了initialization
和destruction
方法,Spring bean也可以實現(xiàn)Lifecycle
接口癣疟,這些接口可以參與Spring容器生命周期的startup
和shutdown
過程。
本章講解生命周期回調(diào)接口潮酒。
<h5 id='beans-factory-lifecycle-initializingbean'>初始化回調(diào)</h5>
org.springframework.beans.factory.InitializingBean
接口類的作用是睛挚,在容器設(shè)置bean必須的屬性之后,執(zhí)行初始化工作急黎。InitializingBean
接口中只有一個方法:
void afterPropertiesSet() throws Exception;
推薦扎狱,盡量不用InitializingBean
接口,因為這將導(dǎo)致不必要的與Spring的耦合勃教。還有更好的辦法淤击,使用@PostConstruct
注解,或者指定一個POJO的initialization
方法故源。XML配置元數(shù)據(jù)中污抬,使用init-method
屬性用來指定,其值為初始化方法名绳军,初始化方法得是一個無參無返回值(void)方法印机。如果使用java Config,得在@Bean
注解中使用initMehtod
屬性 ,詳情參看 the section called “Receiving lifecycle callbacks”门驾∩淙看代碼
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
和下面的效果相同,但上面的沒有耦合Spring猎唁。
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
public void afterPropertiesSet() {
// do some initialization work
}
}
<h5 id='beans-factory-lifecycle-disposablebean'>銷毀回調(diào)</h5>
實現(xiàn)org.springframework.beans.factory.DisposableBean
接口咒劲,作用是Spring銷毀bean時調(diào)用該方法。 DisposableBean
接口只有一個方法:
void destroy() throws Exception;
和上面初始化函數(shù)一樣诫隅,推薦你不要使用DisposableBean
回調(diào)接口腐魂,因為會產(chǎn)生不必要的耦合之類的balbalbal。還是和上面一樣逐纬,能使用 @PreDestroy
注解或者指定一個spring bean定義支持的方法TODO蛔屹??若使用XML配置豁生,可是使用<bean/>
元素的destroy-method
屬性來完成該設(shè)置兔毒。若是使用Java config,可以使用@Bean
注解的destroyMethod
屬性來完成銷毀回調(diào)設(shè)置甸箱。see the section called “Receiving lifecycle callbacks”育叁。看樣例:
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
和下面代碼效果一樣芍殖,但是上面的代碼不和Spring耦合
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
<bean>
元素的destroy-method
屬性可以指定一個特別的值豪嗽,設(shè)置該值后Spring將會自動探測指定類上的publicclose
或者shutdown
方法。這個設(shè)置自動探測銷毀方法的屬性,也可以設(shè)置給<beans/>
元素的default-destroy-method
屬性龟梦,用來設(shè)置<beans>
元素內(nèi)的所有的<bean>
自動探測銷毀方法隐锭,詳情參看section called “Default initialization and destroy methods”。注意计贰,在Java config配置元數(shù)據(jù)中钦睡,這種自動探測是默認的。
譯注躁倒,現(xiàn)在是羊年除夕夜23:40荞怒,再過30分鐘,公司服務(wù)器上的一些定時器就要開始運行了樱溉。不知道還會不會線程掛起了挣输,多線程中使用網(wǎng)絡(luò)輸出流時如果發(fā)生斷網(wǎng),線程則會處于阻塞狀態(tài)福贞,然后就沒有然后一直阻塞撩嚼,已經(jīng)修改過了。外面的煙花炮仗聲逐漸的密集了起來挖帘,放炮仗完丽,污染太重了,國家抑制的手段就和抑制煙草手段一樣拇舀,重稅逻族。心亂了,不能專心翻譯了骄崩。
<h5 id='beans-factory-lifecycle-default-init-destroy-methods'>默認的初始化函數(shù)和銷毀函數(shù)</h5>
若不是使用InitializingBean
和DisposableBean
接口實現(xiàn)初始化和銷毀回到方法聘鳞,通常使用規(guī)范的方法名比如init
,initialize()
,dispose()
等等。理論上要拂,生命周期回調(diào)方法名的規(guī)范性抠璃,應(yīng)該貫穿于整個項目中,所有的開發(fā)者都應(yīng)該使用相同的方法名保持一致性脱惰。譯注搏嗡,編碼規(guī)范,Spring最講究這個了
可以配置容器查找所有bean的初始化回調(diào)和銷毀回調(diào)拉一,當應(yīng)用類中的初始化回調(diào)方法命名為init()
采盒,就不需要在bean定義中配置init-method="init"
屬性。Spring IoC容器在bean初始化時調(diào)用init()
回調(diào)蔚润。該功能強制初始化和銷毀回調(diào)方法命名的規(guī)范性磅氨。
Suppose that your initialization callback methods are named init() and destroy callback methods are named destroy(). Your class will resemble the class in the following example.
假設(shè),初始化回調(diào)方法命為init()
嫡纠,銷毀回調(diào)方法命名為destroy()
悍赢。應(yīng)該和下面的樣例差不多:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
<beans default-init-method="init">
<bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
在頂級<bean/>
元素中定義了default-init-method
屬性决瞳,使Spring Ioc 容器解析bean中名為init
的方法為初始化回調(diào)方法。當bean創(chuàng)建實例并組裝時左权,若bean類中有個一個init()
方法,該初始化回調(diào)會在合適的時間調(diào)用痴颊。
<h5 id='beans-factory-lifecycle-combined-effects'>聯(lián)合混合使用多種生命周期回調(diào)機制</h5>
Spring2.5 以后,控制bean生命周期行為赏迟,有三種生命周期回調(diào)機制,或者說是三種方式實現(xiàn):InitializingBean 和 DisposableBean 回調(diào)接口蠢棱;自定義init()
和destroy()
方法; @PostConstruct and @PreDestroy 注解锌杀。這些方式可以混合使用。
如何一個bean上配置了多種生命周期回調(diào)機制,并且每種機制都使用了不同的方法泻仙,那么所有的回調(diào)方法都會按次序執(zhí)行糕再。然而,如果配置了相同的方法名玉转,比如
init()
方法作為初始化方法突想,該方法在多種生命周期回調(diào)機制中都有配置,但是究抓,該方法只會執(zhí)行一次猾担。
在一個bean中,配置多種生命周期回調(diào)機制刺下,每種機制使用了不同的初始化方法绑嘹,會按照下列次序調(diào)用:
- 帶
@PostConstruct
注解的方法 -
InitializingBean
回調(diào)接口中的afterPropertiesSet()
方法 - 自定義的
init()
方法
銷毀回調(diào)也使用相同的次序
- 帶
@PreDestroy
注解的方法 -
DisposableBean
回調(diào)接口中的destroy()
方法 - 自定義的
destroy()
方法
<h5 id='beans-factory-lifecycle-processor'>容器啟動和關(guān)閉回調(diào)</h5>
Lifecycle
接口定了對象有自己生命周期需求的必須的方法(比如啟動停止某些后臺處理)
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的對象都可以實現(xiàn)此接口。當ApplicationContext
接口啟動和關(guān)閉時橘茉,它會調(diào)用本容器內(nèi)所有的Lifecycle
實現(xiàn)工腋。通過LifecycleProcessor
來調(diào)用,
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}