4

有個類實現(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眉尸,則無需特別配置:DispatcherServletDispatcherPortlet已經(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都是做相同的事兒奔穿,也就是綁定HTTPrequest對象到服務(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)建一個新的LoginActionbean 的實例贱田。也就是,loginActionbean作用域范圍在HTTP 請求級別缅茉。可以改變實例的內(nèi)部狀態(tài)男摧,多少實例都可以蔬墩,因為根據(jù)此loginAcitonbean定義創(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)建了一個UserPreferencesbean的實例帆离。換句話說userPreferencesbean在HTTP Session會話期間有效。和request-scopedbean相類似,可以改變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)建一個AppPreferencesbean的實例。appPreFerencesbean作用域是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注入,該如何配置垦巴。(下面的userPreferencesbean媳搪,實際上并不完整)。

<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秦爆。注意,userManagerbean是一個單例bean:每個容器只會實例化一個憔披,他的依賴(本例中只有一個等限,userPreferencesbean)也僅會注入一次。也就是說芬膝,userManagerbean只能操作相同的userPreferences對象望门,就是注入的那一個。

將一個短生命周期作用域bean注入給長生命周期作用域bean蔗候,比如將HTTP Session作用域bean作為依賴注入給一個單例bean怒允。然而,你需要一個userManager對象锈遥,在HTTP Session會話期間纫事,需要與session同生命周期的對象userPreferences勘畔。 因此,容器會創(chuàng)建一個對象丽惶,該對象擁有和UserPreferences完全相同的public接口并暴露所有的public接口炫七。,該對象能根據(jù)作用域機制獲取真真的UserPreferences對象钾唬。容器會將這個代理對象注入給userManagerbean,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-basedinterface-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)置的作用域singletonprototype。第二個參數(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)SpringInitializingBeanDisposableBean接口。容器會調(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-methoddestroy-method

Spring使用BeanPostProcessor實現(xiàn)類處理所有的回調(diào)接口并調(diào)用相應(yīng)的方法尚洽,接口由Spring 負責(zé)查找。若需要自定義功能或其他生命周期行為靶累,Spring并未提供開箱即用的支持,但是可以自己實現(xiàn)BeanPostProcessor類腺毫。詳情參看"Section 5.8, “Container Extension Points”

除了initializationdestruction方法,Spring bean也可以實現(xiàn)Lifecycle接口癣疟,這些接口可以參與Spring容器生命周期的startupshutdown過程。

本章講解生命周期回調(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將會自動探測指定類上的public close或者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>
若不是使用InitializingBeanDisposableBean接口實現(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):InitializingBeanDisposableBean 回調(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();

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市畅卓,隨后出現(xiàn)的幾起案子擅腰,更是在濱河造成了極大的恐慌,老刑警劉巖髓介,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惕鼓,死亡現(xiàn)場離奇詭異,居然都是意外死亡唐础,警方通過查閱死者的電腦和手機箱歧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來一膨,“玉大人呀邢,你說我怎么就攤上這事”鳎” “怎么了价淌?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵申眼,是天一觀的道長。 經(jīng)常有香客問我蝉衣,道長括尸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任病毡,我火速辦了婚禮濒翻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘啦膜。我一直安慰自己有送,他們只是感情好,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布僧家。 她就那樣靜靜地躺著雀摘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪八拱。 梳的紋絲不亂的頭發(fā)上阵赠,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音乘粒,去河邊找鬼豌注。 笑死,一個胖子當著我的面吹牛灯萍,可吹牛的內(nèi)容都是我干的轧铁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼旦棉,長吁一口氣:“原來是場噩夢啊……” “哼齿风!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绑洛,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤救斑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后真屯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脸候,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年绑蔫,在試婚紗的時候發(fā)現(xiàn)自己被綠了运沦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡配深,死狀恐怖携添,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情篓叶,我是刑警寧澤烈掠,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布羞秤,位于F島的核電站,受9級特大地震影響左敌,放射性物質(zhì)發(fā)生泄漏瘾蛋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一矫限、第九天 我趴在偏房一處隱蔽的房頂上張望瘦黑。 院中可真熱鬧,春花似錦奇唤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至廊勃,卻和暖如春懈贺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坡垫。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工梭灿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冰悠。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓堡妒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溉卓。 傳聞我的和親對象是個殘疾皇子皮迟,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容