Spring Framework 官方文檔中文版—Core_part_2

內(nèi)容過長,core部分分開發(fā)布驼仪,core章節(jié)第1部分點(diǎn)擊:Spring Framework 官方文檔中文版—Core_part_1
主目錄或?qū)n}地址可以點(diǎn)擊:主目錄, 專題地址

自動(dòng)裝(Autowiring)配協(xié)作者

Spring容器可以自動(dòng)裝配協(xié)作bean之間的關(guān)系授帕。你可以通過檢查ApplicationContext的內(nèi)容锋边,讓Spring自動(dòng)為你的bean解析協(xié)作者(即其他bean)用押。自動(dòng)裝配有以下優(yōu)點(diǎn):

  • 自動(dòng)裝配可以顯著減少指定屬性或構(gòu)造方法的場(chǎng)景。

  • 自動(dòng)裝配可以隨著你的對(duì)象改變而改變报慕。例如深浮,你需要往一個(gè)類中增加依賴,那么無需修改配置就可以自動(dòng)滿足這個(gè)需求卖子。因此自動(dòng)裝配在開發(fā)過程中非常有用略号,當(dāng)代碼庫趨于穩(wěn)定時(shí),不需要否定顯示的裝配洋闽。(最后一句原文:Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable.)

當(dāng)使用基于XML的配置時(shí),你需要在<bean/>標(biāo)簽內(nèi)指定屬性autowire突梦。自動(dòng)裝配功能有4種模式诫舅。你可以為每個(gè)bean單獨(dú)指定自動(dòng)裝配,所以也就可以選擇其中一個(gè)進(jìn)行自動(dòng)裝配宫患。

下面是自動(dòng)裝配的4種模式:

模式 說明
no (默認(rèn))不自動(dòng)裝配刊懈。bean的引用必須通過ref屬性標(biāo)簽來指定。對(duì)于較大型的項(xiàng)目娃闲,并不推薦修改此默認(rèn)配置虚汛,因?yàn)槊鞔_的指定bean可以更易于管理和更可控。在某種意義上皇帮,這相當(dāng)于是記錄了系統(tǒng)的結(jié)構(gòu)
byName 根據(jù)名稱自動(dòng)裝配卷哩。Spring自動(dòng)尋找同名的bean作為需要裝配的屬性。例如属拾,如果設(shè)置了一個(gè)bean定義為byName自動(dòng)裝配将谊,并且含有一個(gè)master屬性(也就是說它有一個(gè)setMaster(..)方法) ,Spring尋找到名稱為master的bean定義渐白,并設(shè)置到其屬性中
byType 如果容器中恰好和屬性類型相同的bean尊浓,那么允許將這個(gè)bean自動(dòng)裝配到屬性。如果這種bean的數(shù)量為多個(gè)則會(huì)拋出異常纯衍,表明你并不適合用詞類型的自動(dòng)裝配栋齿,如果沒有此類型的bean匹配,則不會(huì)發(fā)生什么異常襟诸,屬性也就有可能沒有被設(shè)置
constructor 和bytype類似瓦堵,但是是用于構(gòu)造函數(shù)參數(shù),如果容器中沒有一個(gè)和構(gòu)造函數(shù)參數(shù)類型一樣的bean励堡,則會(huì)引發(fā)致命異常

使用byType 或 constructor 自動(dòng)裝配模式谷丸,你可以裝配數(shù)組和集合類型。這種情況下应结,容器內(nèi)所有與預(yù)期類型匹配的bean都會(huì)被裝配至此數(shù)據(jù)或集合刨疼。你可以自動(dòng)裝配強(qiáng)類型的map泉唁,如果key類型正好的String。自動(dòng)裝配的map的value是由和預(yù)期類型一樣的bean組成揩慕,key的值會(huì)包含bean的名稱亭畜。

你可以將自動(dòng)裝配的行為與依賴檢查相結(jié)合,依賴檢查是在自動(dòng)裝配結(jié)束后開始執(zhí)行的迎卤。

自動(dòng)裝配的局限性和缺點(diǎn)*

自動(dòng)裝配在項(xiàng)目中應(yīng)用時(shí)拴鸵,要么全部使用,不要部分使用蜗搔。如果一般不使用自動(dòng)裝配劲藐,只是在少數(shù)的一兩個(gè)bean定義中使用它,自動(dòng)裝配可能會(huì)讓開發(fā)者產(chǎn)生混淆樟凄。

考慮自動(dòng)裝配的局限性和缺點(diǎn):

  • property 和 constructor-arg中明確的依賴會(huì)覆蓋自動(dòng)裝配聘芜。你不能自動(dòng)裝配簡(jiǎn)單類型的屬性,如原始類型缝龄,String汰现,Class(以及這些簡(jiǎn)單類型組成的數(shù)組)。這個(gè)局限是因?yàn)榫褪沁@么設(shè)計(jì)的

  • 明確的裝配要比自動(dòng)裝配準(zhǔn)確叔壤。盡管如上面的表所示瞎饲,Spring小心的避免猜測(cè)所導(dǎo)致預(yù)期外的結(jié)果,但是項(xiàng)目中被Spring管理的對(duì)象關(guān)系并不會(huì)被明確記錄炼绘。

  • 可能無法從Spring容器生成文檔的工具獲得使用連接信息嗅战。

  • setter方法或構(gòu)造函數(shù)所指的的類型,容器中可能會(huì)存在多個(gè)bean匹配上饭望。對(duì)于數(shù)組仗哨,集合或map來說這并不是一個(gè)問題。然而對(duì)依賴來說只需要一個(gè)結(jié)果的話铅辞,這并不會(huì)被有效的解決厌漂。如果非唯一值存在,則會(huì)拋出致命的異常斟珊。

在下面苇倡,你可以有以下幾個(gè)選擇:

  • 放棄自動(dòng)裝配,全部使用顯示的(常規(guī))裝配

  • 將其autowire-candidate屬性設(shè)置為false囤踩,避免bean定義進(jìn)行自動(dòng)裝配旨椒,如下一節(jié)所示。

  • 指派一個(gè)單獨(dú)的bean定義作為主要對(duì)象堵漱,需要將<bean/>標(biāo)簽的屬性primary屬性設(shè)置為true综慎。

  • 使用基于注解的容器配置,實(shí)現(xiàn)更細(xì)粒度的配置勤庐。

自動(dòng)裝配中排除一個(gè)bean

基于每個(gè)bean的配置示惊,你可以將一個(gè)bean從自動(dòng)裝配中排除好港。在Spring XML格式中,設(shè)置<bean/>標(biāo)簽中的屬性autowire-candidate為false米罚;容器會(huì)讓此bean定義無法進(jìn)行自動(dòng)裝配(包括注解風(fēng)格的配置例如@Autowired)钧汹。

屬性autowire-candidate只對(duì)基于類型的自動(dòng)裝配有效。它不對(duì)明確的裝配例如byName類型的自動(dòng)裝配有效录择,即使某bean沒有被標(biāo)記為autowire選項(xiàng)拔莱,它也會(huì)被解析。因此隘竭,只要名稱匹配塘秦,自動(dòng)裝配總會(huì)注入一個(gè)bean。

你也可以利用表達(dá)式來限制自動(dòng)裝配候選者的名稱动看。最頂層標(biāo)簽<beans/>default-autowire-candidates屬性接受一個(gè)或多個(gè)表達(dá)式嗤形。例如,限制候選bean的名稱是以 Repository 結(jié)尾弧圆,只需要將表達(dá)式寫為 *Repository 。要寫入多個(gè)表達(dá)式笔咽,則表達(dá)式之間用逗號(hào)分隔搔预。對(duì)于一個(gè)bean定義來說,明確指定autowire-candidate屬性的值為true或false總是優(yōu)先的叶组,對(duì)于這個(gè)bean定義來說拯田,表達(dá)式匹配規(guī)則并不生效。

這些技術(shù)甩十,對(duì)于那些你不希望通過自動(dòng)裝配注入到其他bean的bean十分有用船庇。這并不意味著被排除的bean,本身不能配置自動(dòng)裝配侣监。相反的鸭轮,而是bean本身不是其他自動(dòng)裝配bean的候選者。

方法注入(Method injection)

大多數(shù)應(yīng)用場(chǎng)景中橄霉,容器中大部分bean都是單例的窃爷。當(dāng)一個(gè)單例的bean,需要另一個(gè)單例bean協(xié)作姓蜂,或者一個(gè)非單例bean需要另一個(gè)非單例bean協(xié)作按厘,你通常通過定義一個(gè)bean作為另一個(gè)bean的屬性來處理這種依賴關(guān)系。當(dāng)bean的生命周期不同時(shí)問題就出現(xiàn)了钱慢。假設(shè)一個(gè)單例bean A需要一個(gè)非單例的bean B逮京,也許A的每個(gè)方法都調(diào)用了。容器只創(chuàng)建bean A一次束莫,因此也就只有一次機(jī)會(huì)來設(shè)置A的屬性懒棉。當(dāng)需要時(shí)草描,容器不一定能為bean A提供一個(gè)新的bean B的實(shí)例。

一個(gè)解決辦法是放棄一些控制反轉(zhuǎn)漓藕。你可以通過實(shí)現(xiàn)ApplicationContextAware接口陶珠,創(chuàng)建一個(gè)bean A aware(此單詞不翻譯靖榕,直譯為:知道的索昂,了解的)嫉晶,當(dāng)bean A需要beanB的時(shí)候铐然,容器調(diào)用getBean("B")方法來獲得bean B幅垮。下面是相關(guān)的例子:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // 創(chuàng)建一個(gè) Command實(shí)例
        Command command = createCommand();
        // 將狀態(tài)設(shè)置為命令實(shí)例
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // 注意引用的spring依賴
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

上面的這個(gè)例子耿导,在實(shí)際應(yīng)用中是不可取的箱亿,因?yàn)槟愕臉I(yè)務(wù)代碼耦合到了Spring代碼中剂买!方法注入是Spring IoC容器的一個(gè)高級(jí)特性狐肢,它能夠以一種干凈的方式來處理這個(gè)例子添吗!

基于查找的方法注入(Lookup method injection)

Lookup方法注入,是指容器在其管理的bean上重寫方法份名,將查找結(jié)果返回給容器中定義好的bean碟联。查找通常涉及前面所講的prototype類型的bean。Spring Framework實(shí)現(xiàn)這中方法注入主要使用了CGLIB的字節(jié)碼生成技術(shù)去動(dòng)態(tài)生成子類去覆蓋方法僵腺。

  • 為了成功生成和使用這個(gè)動(dòng)態(tài)子類鲤孵,Spring要去繼承的類不能為final,同樣的辰如,要被覆蓋的方法也不能是final的普监。
  • 單元測(cè)試時(shí),擁有抽象方法的類需要你自己去繼承這個(gè)類并且實(shí)現(xiàn)它的抽象方法琉兜。
  • 組件掃描也需要非抽象方法凯正,這需要非抽象類來提取。
  • 進(jìn)一步的限制就是豌蟋,方法查找注入不能用于工廠方法廊散,尤其是不能在配置類中使用@Bean注解,因?yàn)檫@個(gè)情況下容器不負(fù)責(zé)創(chuàng)建實(shí)例夺饲。所以在運(yùn)行時(shí)創(chuàng)建子類奸汇。

在前面的CommandManager類中,你可以看見往声,Spring容器會(huì)動(dòng)態(tài)的覆蓋createCommand()方法的實(shí)現(xiàn)擂找。CommandManager類不會(huì)有任何Spring依賴項(xiàng),我們可以看其重寫的例子:

package fiona.apple;
// 沒有任何Spring的依賴!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // 獲取Command接口的新實(shí)例
        Command command = createCommand();
        command.setState(commandState);
        // 為新Command實(shí)例設(shè)置狀態(tài)
        return command.execute();
    }
    // ok!但是浩销,這個(gè)抽象方法在哪里來實(shí)現(xiàn)呢贯涎?
    protected abstract Command createCommand();
}

包含被注入方法的類(本例中的CommandManager),被注入的方法需要滿足下面的格式:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

如果方法是抽象的慢洋,會(huì)用動(dòng)態(tài)生成子類來實(shí)現(xiàn)這個(gè)方法塘雳。動(dòng)態(tài)生成子類會(huì)覆蓋那個(gè)方法陆盘。例如:

<!-- bean的scope被設(shè)置成prototype(非單例的) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

不管什么時(shí)候,只要commandManager bean調(diào)用自己的createCommand()方法它都需要一個(gè)myCommand bean的新實(shí)例败明。你必須要注意將myCommand bean定義設(shè)置為prototype隘马,如果實(shí)際需求是這樣。如果他是單例的妻顶,則每次myCommand bean都會(huì)返回同一個(gè)實(shí)例酸员。

作為選擇,也可以使用基于注解的配置讳嘱,你可以通過@Lookup注解來聲明:

public abstract class CommandManager {

    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者幔嗦,更通俗的,你可以依賴目標(biāo)bean的類型沥潭,將抽象方法的返回類型修改為目標(biāo)bean的類型:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

注意邀泉,你會(huì)聲明這樣一個(gè)注解來查找一個(gè)一般方法的實(shí)現(xiàn),以便讓它們能夠讓Spring的組件去掃描钝鸽。這種方式不適用于顯式的注冊(cè)或者顯式導(dǎo)入bean class汇恤。

接受不同scope目標(biāo)bean的另一個(gè)方法是ObjectFactory/ Provider 注入點(diǎn)。具體辦法在bean scope章節(jié)給出拔恰。
感興趣的讀者可以去研究一下ServiceLocatorFactoryBean(在org.springframework.beans.factory.config包下面)屁置。

替換任意方法

比方法查找注入應(yīng)用還少的一種方法注入是,可以在管理的bean中用另一個(gè)方法實(shí)現(xiàn)來替換任意方法仁连。如果你在實(shí)際中沒有此需求,那么可以完全跳過本節(jié)內(nèi)容阱穗。

基于XML的配置中饭冬,你可以使用replaced-method標(biāo)簽去替換已經(jīng)實(shí)現(xiàn)的方法實(shí)現(xiàn)【窘祝考慮一下下面的類昌抠,有一個(gè)我們要覆蓋的方法:

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }
    // some other methods...
}

一個(gè)實(shí)現(xiàn)了org.springframework.beans.factory.support.MethodReplacer接口的類,定義了一個(gè)新方法:

/**
 * meant to be used to override the existing computeValue(String)
 * implementation in MyValueCalculator
 */
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鲁僚,并且指定覆蓋方法:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- 任意方法替換-->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

你可以看到炊苫,<replaced-method/>標(biāo)簽內(nèi)包含一個(gè)或多個(gè)<arg-type/>標(biāo)簽,來表示被覆蓋的方法簽名冰沙。只有類中方法是重載的侨艾,這個(gè)參數(shù)聲明才是必須的。方便起見拓挥,字符串類型的參數(shù)可以是完全限定的類型名字的一部分唠梨,例如,下面的類型都表示的是java.lang.String

java.lang.String
String
Str

因?yàn)閰?shù)數(shù)量侥啤,通常足夠去彼此區(qū)分当叭,這種簡(jiǎn)寫可以節(jié)省很多拼寫茬故。

bean的作用域

當(dāng)你定義了一個(gè)bean,你就創(chuàng)建了一個(gè)bean實(shí)例化的規(guī)則蚁鳖。這是非常重要的磺芭,因?yàn)檫@意味著,使用一個(gè)類醉箕,你可以從一個(gè)bean定義來創(chuàng)建多個(gè)實(shí)例钾腺。

你不光可以控制各種各樣的的依賴,將配置的值注入到由bean的配置創(chuàng)建的對(duì)象琅攘,也可以控制由bean定義創(chuàng)建的對(duì)象的范圍垮庐。這種方法非常強(qiáng)大和有效,因?yàn)槟阃ㄟ^配置文件創(chuàng)建的對(duì)象坞琴,是可以選擇它的范圍的哨查,這樣避免了你在Java類級(jí)別去設(shè)置它的范圍。bena可以被部署在以下幾個(gè)范圍:Spring支持6個(gè)范圍剧辐,其中4個(gè)是只有在web項(xiàng)目中管用的寒亥。

下面的范圍都是開箱即用的,你也可以定制自己的范圍荧关。

范圍 描述
singleton (默認(rèn))整個(gè)Spring IoC容器中只有一個(gè)實(shí)例
prototype 每次使用都會(huì)創(chuàng)建一個(gè)新的實(shí)例
request 在一個(gè)JTTP請(qǐng)求生命周期內(nèi)創(chuàng)建一個(gè)單實(shí)例溉奕;就是,每個(gè)HTTP請(qǐng)求有它自己的單例的bean忍啤,只有在web項(xiàng)目的Spring ApplicationContext的上下文中有效
session 在HTTP session的生命周期內(nèi)有效加勤。只有在web項(xiàng)目的Spring ApplicationContext的上下文中有效
application 在ServletContext的生命周期內(nèi)有效。只有在web項(xiàng)目的Spring ApplicationContext的上下文中有效
webSocket 在webSocket的生命周期內(nèi)有效同波。 只有在web項(xiàng)目的Spring ApplicationContext的上下文中有效

Spring 3.0中鳄梅,是有線程范圍的,但是默認(rèn)是不注冊(cè)的未檩。更多信息戴尸,請(qǐng)參考<font style=text-decoration:underline color=#548e2e>SimpleThreadScope</font>。像獲得如何注冊(cè)它和其他的自定義scope冤狡,可以查看<font style=text-decoration:underline color=#548e2e>使用自定義scope</font>

單例(singleton)作用域

單例的bean孙蒙,只存在一個(gè)被管理的共享實(shí)例,對(duì)于所有對(duì)這個(gè)bean實(shí)例的請(qǐng)求悲雳,例如用id去匹配bean定義挎峦,Spring容器都會(huì)返回一個(gè)特定的bean實(shí)例。

換句話說合瓢,當(dāng)你在bean定義中把bean的范圍設(shè)置成單例的時(shí)候浑测,Spring Ioc容器會(huì)根據(jù)bean的定義只創(chuàng)建一個(gè)實(shí)例。此單個(gè)實(shí)例會(huì)被存在一個(gè)單例bean的緩存中,后面的所有請(qǐng)求和對(duì)這個(gè)bean的指向迁央,都會(huì)返回緩存中的bean實(shí)例掷匠。

從始至終只創(chuàng)建一個(gè)bean

Spring的單例bean概念,不同于Gang of Four (GoF)設(shè)計(jì)模式書籍中的單例模式岖圈。GoF的單例是硬編碼對(duì)象的范圍讹语,對(duì)于每個(gè)類加載器來說,類的對(duì)象有且只有一個(gè)實(shí)例蜂科。Spring的單例范圍最好的理解是每一個(gè)容器和每一個(gè)bean內(nèi)顽决,有且只有一個(gè)實(shí)例。這意味著导匣,在一個(gè)Spring IoC容器中才菠,你為一個(gè)類定義了一個(gè)bean定義,Spring容器會(huì)為這個(gè)bean定義創(chuàng)建一個(gè)且只創(chuàng)建一個(gè)實(shí)例贡定。單例范圍是Spring中的默認(rèn)范圍赋访。在xml中創(chuàng)建一個(gè)單例的bean,你可以利用下面的方式:

<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- 這個(gè)是與上一個(gè)等價(jià)的, 通過多余的指定 (singleton的配置是默認(rèn)的) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
prototype作用域

非單例的缓待,prototype范圍的bean蚓耽,在每次對(duì)bean實(shí)例的請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean的實(shí)例。就是說旋炒,bean被注入到另一個(gè)bean中或通過getBean()調(diào)用bean的實(shí)例步悠。一般來說,使用prototype范圍的bean來創(chuàng)建有狀態(tài)的bean瘫镇,使用singleton(單例)范圍來創(chuàng)建無狀態(tài)的bean鼎兽。下面的圖表闡明了Spring的prototype范圍的bean。比較典型的铣除,一個(gè)數(shù)據(jù)訪問對(duì)象(DAO)并不會(huì)被配置成prototype的bean接奈,因?yàn)榈湫偷腄AO不具有任何會(huì)話狀態(tài);對(duì)作者來說只是簡(jiǎn)單的重復(fù)使用而已通孽,就像上一節(jié)圖表中展示的那樣。

每次創(chuàng)建一個(gè)新的bean實(shí)例

下面的例子是如何在xml創(chuàng)建prototype的bean:

<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

對(duì)比于其他的范圍睁壁,Spring并不會(huì)完整的管理prototype范圍bean的生命周期:容器的實(shí)例化背苦,配置,其他方式組裝對(duì)象潘明,和將其交由客戶端行剂,整過程并沒有對(duì)prototype進(jìn)行進(jìn)一步記錄。因此钳降,盡管初始化生命周期的回調(diào)方法不管是什么范圍厚宰,在所有對(duì)象上都會(huì)被調(diào)用,但在prototype范圍情況下,為其配置生命周期的回調(diào)并不會(huì)被調(diào)用铲觉〕候客戶端代碼必須要清理prototype對(duì)象,并釋放prototype bean所占用的珍貴的資源撵幽。為了讓Spring容器釋放prototype bean占用的資源灯荧,可以使用BeanPostProcessor進(jìn)行自定義擴(kuò)展,這里面包含了需要清理的bean的引用盐杂。

在某些方面逗载,Spring容器在prototype域中的角色,就相當(dāng)于Java中new操作链烈。所有生命周期的操作都需要由客戶端自己來處理厉斟。

單例bean依賴于prototype bean

當(dāng)你使用單例域的bean依賴于prototype bean的時(shí)候,要注意强衡,依賴是在實(shí)例化的時(shí)候才解析擦秽。所以如果你將一個(gè)peototype域的bean注入到單例域的bean,一個(gè)新的prototype bean實(shí)例化并且被注入到單例域的bean中食侮。prototype域的實(shí)例是提供給單例域的bean的唯一實(shí)例号涯。

然而,假設(shè)你想單例域的bean在運(yùn)行時(shí)反復(fù)獲取一個(gè)prototype的bean的新實(shí)例锯七。你不能將一個(gè)prototype的bean依賴注入到單例域的bean中链快,因?yàn)樽⑷胫话l(fā)生一次,是在Spring容器實(shí)例化單例域的bean并解析它的依賴時(shí)眉尸。如果你需要在運(yùn)行時(shí)獲得一次或多次prototype bean的實(shí)例時(shí)域蜗,可以參考前面的方法注入章節(jié)。

Request噪猾,session霉祸,application,和WebSocket作用域

只有你在使用一個(gè)web方面的Spring ApplicationContext(例如XmlWebApplicationContext)實(shí)現(xiàn)時(shí)袱蜡,Request丝蹭,session,application坪蚁,和WebSocket作用域才會(huì)起作用奔穿。如果你將這些作用域用在一個(gè)常規(guī)的Spring IoC容器中例如ClassPathXmlApplicationContext,則會(huì)拋出一個(gè)IllegalStateException異常敏晤,告訴你這是一個(gè)未知的bean作用域贱田。

初始化web配置

為了支持requestsession嘴脾,applicationwebsocket等域男摧,在你進(jìn)行bean定義之前,需要做一些小的配置(這些小的配置在標(biāo)準(zhǔn)域中不需要配置,singleton和 prototype)耗拓。

怎么樣完成這些初始化步驟拇颅,這取決于你的具體Servlet環(huán)境。

如果你使用Spring Web MVC來訪問這些域的bean帆离,實(shí)際上蔬蕊,就是通過DispatcherServlet來調(diào)用, 根本不需要其他的步驟。

如果你使用Servlet 2.5容器時(shí)哥谷,也就是說不使用DispatcherServlet時(shí)(例如使用JSF岸夯,Struts等),你需要注冊(cè)org.springframework.web.context.request.RequestContextListener ServletRequestListener们妥。對(duì)于Servlet 3.5+猜扮,可以實(shí)現(xiàn)WebApplicationInitializer接口,或者對(duì)于老版本容器可以在web.xml中做如下的配置:

<web-app>
    ...
    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

如果你的listener啟動(dòng)有了問題监婶,可以考慮使用Spring的RequestContextFilter旅赢。這個(gè)過濾器的配置依賴與web配置文件,可以參考如下配置:

<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>

其實(shí)DispatcherServlet, RequestContextListener, 和 RequestContextFilter惑惶,它們都在做一件事煮盼,就是將HTTP request對(duì)象綁定到處理請(qǐng)求的線程上,這就可以讓request或session域加入到請(qǐng)求鏈中了带污。

Request scope

請(qǐng)先看一下下面的XML配置:

<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

Spring容器會(huì)創(chuàng)建一個(gè)beanLoginAction的實(shí)例僵控,這樣它就可以處理各個(gè)HTTP請(qǐng)求。也就是說鱼冀,LoginAction是作用在了Http請(qǐng)求級(jí)別报破。你可以隨便改變這個(gè)實(shí)例的內(nèi)部狀態(tài),因?yàn)?code>LoginAction是多實(shí)例的千绪,所以實(shí)例之間不會(huì)受影響充易;每個(gè)請(qǐng)求都會(huì)有它自己的實(shí)例。當(dāng)請(qǐng)求結(jié)束的時(shí)候荸型,實(shí)例也會(huì)跟著銷毀盹靴。

可以使用注解的方式來配置請(qǐng)求域,即@RequestScope瑞妇,如下所示:

@RequestScope
@Component
public class LoginAction {
    // ...
}
Session作用域

看一下下面xml配置的bean:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

對(duì)于每個(gè)HTTP Session稿静,Spring容器都會(huì)創(chuàng)建一個(gè)UserPreferences的實(shí)例。也就是說UserPreferences是作用范圍是Session級(jí)別的踪宠。和上面講的request作用域類似,你也可以隨便改變實(shí)例的內(nèi)部狀態(tài)而其他的實(shí)例則不受影響妈嘹。當(dāng)HTTP Session被銷毀的時(shí)候柳琢,這個(gè)實(shí)例也就隨之銷毀。

當(dāng)基于注解來配置的時(shí)候,可以使用@SessionScope來配置:

@SessionScope
@Component
public class UserPreferences {
    // ...
}
Application作用域

先看一下下面的xml配置:

<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>

上面配置的含義是柬脸,Spring容器會(huì)為整個(gè)web應(yīng)用而創(chuàng)建appPreferencesbean實(shí)例他去,并且只創(chuàng)建一次。也就是說倒堕,appPreferences的作用級(jí)別是ServletContext級(jí)別的灾测,是作為ServletContext的屬性來保存的。這與Spring 單例bean有些類似垦巴,但是有兩點(diǎn)不同:它是對(duì)于每個(gè)ServletContext來說的媳搪,不是針對(duì)ApplicationContext來說的,還有就是骤宣,它是徹底暴露出來的秦爆,是作為ServletContext屬性來存儲(chǔ)和使用的。

基于注解的配置憔披,可以參考以下內(nèi)容:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
非單例的bean作為被依賴

Spring IoC容器不光實(shí)例化你的bean等限,同時(shí)也會(huì)實(shí)例化你的bean的依賴。如果你想將一個(gè)HTTP請(qǐng)求域的bean注入到另一個(gè)bean芬膝,并且這個(gè)bean將會(huì)長期存在望门,那么你可以注入一個(gè)aop代理來替代這個(gè)bean。也就是說锰霜,你需要注入一個(gè)代理對(duì)象筹误,并且暴露出一樣的public接口,也可以從相關(guān)域(例如HTTP request域)來檢索實(shí)際的目標(biāo)對(duì)象锈遥,并將該方法委托給實(shí)際對(duì)象纫事。

使用單例bena的過程里,可以使用<aop:scoped-proxy/>標(biāo)簽所灸,然后可以引用一個(gè)可以序列話的中間代理丽惶,從而在反序列化時(shí)候的時(shí)候重新獲得這個(gè)單例的bean。

當(dāng)對(duì)一個(gè)有具體域范圍的bean(例如session域的bean)使用<aop:scoped-proxy/> 標(biāo)簽時(shí)爬立,共享代理的的每一次調(diào)用都會(huì)導(dǎo)致重新創(chuàng)建一次目標(biāo)實(shí)例钾唬,然后將該實(shí)例轉(zhuǎn)發(fā)給調(diào)用方。

另外侠驯,代理并不是從短生命周期(相對(duì)而言)bean獲取bean的唯一方式抡秆。你也可以簡(jiǎn)單的聲明你的注入點(diǎn)(例如構(gòu)造/setter方法參數(shù)或成員變量)來作為ObjectFactory,同時(shí)提供getObject()方法來每次檢索按需調(diào)用--無需保留實(shí)例或單獨(dú)存儲(chǔ)實(shí)例吟策。

考慮擴(kuò)展性儒士,你可以聲明一個(gè)ObjectProvider<MyTargetBean>,額外提供幾個(gè)訪問方法檩坚,例如getIfAvailable 和 getIfUnique着撩。

Provider是JSR-330變體诅福,同時(shí)為每次檢索和嘗試來聲明Provider<MyTargetBean>和對(duì)應(yīng)的get()。有關(guān)JSR-330的相信信息可以參閱這里

下配置的配置文件很簡(jiǎn)單拖叙,但是足夠讓你明白“為什么”和“如何”去明白上面講的是什么意思:

<?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">

    <!-- 一個(gè) HTTP Session-scoped bean 是作為一個(gè)代理被暴露出來 -->
    <bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean -->
        <aop:scoped-proxy/>
    </bean>

    <!-- 一個(gè) singleton-scoped bean 作為代理注入上面的bean -->
    <bean id="userService" class="com.foo.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>

要?jiǎng)?chuàng)建這樣一個(gè)代理氓润,你需要在有作用域的bean定義中插入一個(gè)子元素<aop:scoped-proxy/>,那么為什么需要在request, session 和 custom-scope級(jí)別中定義一個(gè)<aop:scoped-proxy/> 元素薯鳍?讓門來觀察一下下面的單例bean咖气,并與上面的例子做對(duì)比:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

<bean id="userManager" class="com.foo.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
深入bean的本質(zhì)
生命周期中的回調(diào)

如果想深入到容器對(duì)bean生命周期的管理, 你可以實(shí)現(xiàn)Spring的InitializingBeanDisposableBean兩個(gè)接口. 容器在初始化時(shí)調(diào)用afterPropertiesSet()方法, 在銷毀時(shí)調(diào)用destroy()方法.

在spring內(nèi)部是使用BeanPostProcessor接口去執(zhí)行它所能發(fā)現(xiàn)的所有回調(diào)接口或方法. 如果你想定制一些spring沒有提供的功能, 那么你可以自己去實(shí)現(xiàn)BeanPostProcessor接口.

除了初始化回調(diào), 銷毀回調(diào)以外, spring所管理的對(duì)象也可以去實(shí)現(xiàn)生命周期接口, 這樣的話, 這些對(duì)象可以隨著容器的生命周期來初始化和停止了.

這些生命周期的回調(diào)接口被在此章節(jié)來說明.

初始化的回調(diào)
實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean接口, 讓bean在加載完必要的屬性之后, 執(zhí)行自己所需的初始化工作, 這個(gè)接口只指定了一個(gè)方法:

void afterPropertiesSet() throws Exception;

其實(shí)并不一定推薦你去使用這個(gè)接口, 因?yàn)檫@相當(dāng)于和Spring耦合到了一起. 你也可以使用@PostConstruct注解或指定一個(gè)POJO初始化方法. 使用xml配置的時(shí)候, 使用init-method屬性去指定沒有返回值的, 沒有參數(shù)的方法. 使用Java配置的話, 使用@bean注解的initMethod屬性, 如下面的例子:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

bean的java類:

public class ExampleBean {
    public void init() {
        // do some initialization work
    }
}

實(shí)現(xiàn)InitializingBean:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

java類:

public class AnotherExampleBean implements InitializingBean {
    public void afterPropertiesSet() {
        // 做一些初始化工作
    }
}

銷毀的回調(diào)
當(dāng)容器關(guān)閉的時(shí)候,如果你想讓你的bean在這時(shí)候執(zhí)行一些操作挖滤,那么可以實(shí)現(xiàn)接口org.springframework.beans.factory.DisposableBean崩溪,這個(gè)接口只有一個(gè)方法:

void destroy() throws Exception;

并不建議以上面這種方式去處理,因?yàn)檫@相當(dāng)于和spring代碼相耦合了壶辜。你可以使用@PreDestroy注解或指定一個(gè)普通方法悯舟。基于xml的配置砸民,使用<bean/>標(biāo)簽下的destroy-method屬性抵怎。基于Java的配置岭参,可以使用@Bean注解的destroyMethod屬性. 如下面得例子所示:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>

ExampleBean得Java代碼:

public class ExampleBean {
    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

上面的例子和下面的事一樣的, 但是沒有和Spring解耦:

<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>

對(duì)應(yīng)的java代碼:

public class AnotherExampleBean implements DisposableBean {
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

標(biāo)簽<bean/>下的destroy-method屬性, 可以指定一個(gè)值, 然后通過Spring會(huì)去自動(dòng)發(fā)現(xiàn)這個(gè)bean上的此方法(一些實(shí)現(xiàn)了java.lang.AutoCloseablejava.io.Closeable接口的累也會(huì)因此而匹配上). 這個(gè)值也可以設(shè)置再default-destroy-method中反惕,下一節(jié)內(nèi)容會(huì)詳解介紹.

默認(rèn)的初始化和銷毀方法
當(dāng)你編寫初始化和銷毀回調(diào)時(shí), 可以不使用使用Spring指定的InitializingBeanDisposableBean接口. 通常你應(yīng)該寫類似init(), initialize(), dispose()的方法, 此時(shí)應(yīng)該盡可能的讓這些生命周期方法保持一致, 以便讓項(xiàng)目中所有的開發(fā)者一目了然.

可以通過配置Spring容器, 讓他可以去每個(gè)bean中尋找已經(jīng)命名好的生命周期方法. 這意味著, 作為一個(gè)應(yīng)用開發(fā)者, 你可以在你自己的類中定義叫init()的初始化回調(diào)方法, 并且不用在bean的定義中配置init-method="init". Spring IoC容器在bean創(chuàng)建時(shí)調(diào)用此方法. 這種特性會(huì)強(qiáng)制執(zhí)行初始化或回調(diào)方法.

可以參考以下例子:

public class DefaultBlogService implements BlogService {
    private BlogDao blogDao;
    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }
    // 這是初始化方法
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

xml:

<beans default-init-method="init">
    <bean id="blogService" class="com.foo.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
</beans>

bean的配置中, 頂級(jí)標(biāo)簽為beans, 在這個(gè)標(biāo)簽中配置default-init-method="init", 就可以讓Spring容器創(chuàng)建beans下所有的bean時(shí), 都去執(zhí)行init方法.

同樣的, 在配置銷毀回調(diào)方法時(shí), 在beans標(biāo)簽中配置default-destroy-method屬性.

如果有的bean的命名和beans標(biāo)簽重命名不一致, 可以在bean標(biāo)簽中配置init-methoddestroy-method, 這樣就可以覆蓋默認(rèn)值了.

多種方式的結(jié)合使用
在Spring2.5 中, 你有三種方式來控制bean的生命周期, 他們分別是: 實(shí)現(xiàn) InitializingBeanDisposableBean 回調(diào)接口; 自定義init()destroy()方法; 還有使用@PostConstruct@PreDestroy注解. 對(duì)于某一個(gè)bean, 你可以結(jié)合上面三種方法, 來控制它的生命周期.

如果對(duì)一個(gè)bean配置了多種生命周期, 并且每種配置的方法名都不一樣, 這些配置會(huì)按照下面所講的順序來執(zhí)行. 然而, 如果配置了多個(gè)一樣的方法名的話, 例如初始化回調(diào)的init()方法, 如果有像上節(jié)所講的內(nèi)容一樣, 它只會(huì)執(zhí)行一次.

在一個(gè)bean上使用多種初始化方式, 調(diào)用優(yōu)先級(jí):

  • 加了@PostConstruct注解的方法
  • 實(shí)現(xiàn)了InitializingBean接口的afterPropertiesSet()方法
  • 自己實(shí)現(xiàn)的init()方法

對(duì)于銷毀的回調(diào)方法, 他們會(huì)同時(shí)執(zhí)行

  • 加了@PreDestroy注解的方法
  • 實(shí)現(xiàn)了DisposableBean接口的destroy()方法
  • 自定義的destroy()方法

啟動(dòng)和停止回調(diào)
任何有自己生命周期需求的對(duì)象都可以實(shí)現(xiàn)接口Lifecycle接口(例如開始或結(jié)束后臺(tái)進(jìn)程).

public interface Lifecycle {
    void start();
    void stop();
    boolean isRunning();
}

任何由Spring管理的對(duì)象都可以實(shí)現(xiàn)Lifecycle接口. 當(dāng)ApplicationContext在運(yùn)行時(shí)接收到停止或重啟的信號(hào)時(shí), 它將這些實(shí)現(xiàn)了Lifecycle接口的對(duì)象作為一個(gè)級(jí)聯(lián)的上下文對(duì)象. 它將這些操作委派給了LifecycleProcessor接口:

public interface LifecycleProcessor extends Lifecycle {
    void onRefresh();
    void onClose();
}

需要注意的是, LifecycleProcessorLifecycle的子接口. 它增加了兩個(gè)方法來支持contenx的刷新和關(guān)閉.

請(qǐng)注意, org.springframework.context.Lifecycle接口只是簡(jiǎn)單的規(guī)定了 開始/結(jié)束通知, 在context刷新時(shí)并不會(huì)自動(dòng)啟動(dòng). 可以考慮實(shí)現(xiàn)org.springframework.context.SmartLifecycle接口以來替代某個(gè)bean的自動(dòng)啟動(dòng)的精確控制. 同樣的, 請(qǐng)注意停止信號(hào)并不是穩(wěn)定的: 在正常停止下, 所有的生命周期bean都會(huì)第一時(shí)間接收到停止信號(hào), 然而, 在熱刷新的時(shí)候, 只有銷毀方法會(huì)被回調(diào), start方法是不會(huì)被回調(diào)的.

啟動(dòng)和停止的調(diào)用順序是很重要的. 如果兩個(gè)對(duì)象之間是有依賴的, 依賴方會(huì)在其依賴啟動(dòng)后啟動(dòng), 在依賴停止前停止. 但是有時(shí)候依賴關(guān)系是未知的.你可能只是知道某一對(duì)象可能在另一個(gè)對(duì)象啟動(dòng)之前啟動(dòng). 在這個(gè)場(chǎng)景下, SmartLifecycle接口定義了另一種方法叫做getPhase(), 這個(gè)方法實(shí)際上是在其父接口Phased中定義的.

public interface Phased {
    int getPhase();
}

接口SmartLifecycle:

public interface SmartLifecycle extends Lifecycle, Phased {
    boolean isAutoStartup();
    void stop(Runnable callback);
}

啟動(dòng)的時(shí)候, 最底層的對(duì)象首先啟動(dòng), 關(guān)閉的時(shí)候, 順序則正好相反. 所以實(shí)現(xiàn)了SmartLifecycle接口, 并且在getPhase()方法中返回Integer.MIN_VALUE的話, 它就會(huì)最先啟動(dòng)并且最后停止. 沒有實(shí)現(xiàn)SmartLifecycle接口的話則為"0".

SmartLifecycle的停止方法會(huì)接收一個(gè)回調(diào). 實(shí)現(xiàn)它的對(duì)象, 會(huì)在其停止前調(diào)用調(diào)用run(). 所以停止就實(shí)現(xiàn)了異步操作.

在非web應(yīng)用中優(yōu)雅的關(guān)閉Spring容器

此章節(jié)僅針對(duì)非web應(yīng)用⊙莺睿基于Spring的web容器的關(guān)閉已經(jīng)說得很詳細(xì)了姿染。

如果你再非web項(xiàng)目中使用了Spring IoC容器,例如桌面程序中秒际,你在其中注冊(cè)了一個(gè)JVM關(guān)閉的hook悬赏。這樣通過調(diào)用單例上的銷毀方法來實(shí)現(xiàn)優(yōu)雅的關(guān)閉,可以釋放掉相關(guān)的資源娄徊。當(dāng)然闽颇,你必須做正確的配置和實(shí)現(xiàn)。

想要注冊(cè)一個(gè)停止鉤子寄锐,你需要調(diào)用ConfigurableApplicationContextregisterShutdownHook()方法:

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Boot {

    public static void main(final String[] args) throws Exception {
        ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // 為上面的 context 增加一個(gè)hook
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
    }
}
ApplicationContextAware 和 BeanNameAware

當(dāng)ApplicationContext創(chuàng)建了一個(gè)實(shí)現(xiàn)了org.springframework.context.ApplicationContextAware的對(duì)象時(shí)兵多,此時(shí)對(duì)象和容器之間就存在某種關(guān)聯(lián)了。

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

這樣就可以讓bean通過編程的方式來操作ApplicationContext, 也可以使用它的子類, 例如ConfigurableApplicationContext, 它也提供了更多的功能. 可以通過編程的方式來檢索其他的bean. 一般這是比較實(shí)用的; 但是一般你需要避免實(shí)用它, 因?yàn)樵谝话愕臉I(yè)務(wù)代碼中, 這樣做會(huì)將Spring的代碼耦合近業(yè)務(wù)代碼, 這并不符合Spring的控制反轉(zhuǎn)的風(fēng)格.

當(dāng)ApplicationContext創(chuàng)建了一實(shí)現(xiàn)了org.springframework.beans.factory.BeanNameAware接口的類, 這個(gè)類就被賦予了定義它的地方的引用.

public interface BeanNameAware {
    void setBeanName(String name) throws BeansException;
}

這個(gè)回調(diào)會(huì)在一般的的bean之后被調(diào)用, 但是會(huì)在初始化回調(diào)之前被調(diào)用, 例如InitializingBean或自定義初始化方法.

其他的Aware接口

除了上面提到的ApplicationContextAwareBeanNameAware以外, Spring還提供了一些Aware接口, 也允許bean指向創(chuàng)建它的容器, 但是需要一些依賴. 大部分重要的Aware接口可以總結(jié)如下: 一般可以通過名字來看他的依賴類型:

表: Aware相關(guān)接口

名稱 需要的依賴 解釋說明
ApplicationContextAware 聲明ApplicationContext 前面章節(jié)中
ApplicationEventPublisherAware 圍繞ApplicationContext發(fā)布時(shí)間 Additional capabilities of the ApplicationContext
BeanClassLoaderAware 加載bean的class的類加載器 實(shí)例化bean
BeanFactoryAware 聲明BeanFactory 前面章節(jié)
BeanNameAware 聲明bean的命名 前面章節(jié)
BootstrapContext BootstrapContext的資源裝飾器 JCA CCI
LoadTimeWeaverAware 在加載時(shí)定義執(zhí)行類的操作 Load-time weaving with AspectJ in the Spring Framework
MessageSourceAware 配置解析消息的策略 ApplicationContext
NotificationPublisherAware Spring JMX信號(hào)發(fā)布 信號(hào)
ResourceLoaderAware 配置底層資源的加載器 資源
ServletConfigAware 當(dāng)前運(yùn)行中容器的servletConfig,只在ApplicationContext加載的web應(yīng)用有效 Spring MVC
ServletContextAware 同上 Spring MVC

需要再次注意, 使用上述這些接口同時(shí)也意味著不再遵循控制反轉(zhuǎn)的style. 只有再你構(gòu)建項(xiàng)目中比較基礎(chǔ)的部分時(shí), 才推薦使用上面的接口(普通業(yè)務(wù)代碼不推薦).

bean定義的繼承

一個(gè)bean定義會(huì)包含很多配置信息, 包括構(gòu)造方法參數(shù), 屬性值 以及容器指定的信息(初始化方法, 靜態(tài)工廠名稱等等). 子bena定義可以從父bean定義繼承配置信息. 子定義可以根據(jù)自身所需來覆蓋一些值, 也可以增加一些配置. 使用父子bean定義可以讓配置更加簡(jiǎn)潔.

如果你的項(xiàng)目使用的是 ApplicationContext, 子bean定義可以由ChildBeanDefinition定義. 很多用戶并不在這個(gè)層面上使用它們, 而是使用了像ClassPathXmlApplicationContext的方式. 當(dāng)你使用基于XML的配置, 你需要使用parent屬性來生命當(dāng)前配置是一個(gè)子bean.

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age屬性值為1, 是從父bean繼承的 -->
</bean>

如果子bean沒有指定bean的class, 那么他就會(huì)從父bean繼承過來, 當(dāng)然也是可以覆蓋父的class的. 稍后的例子中, 子bean必須要兼容父類的class, 也就是說它必須要接受父bena的屬性值.

子bean定義會(huì)從父bean繼承和覆蓋scope, 構(gòu)造方法參數(shù), 屬性值等, 也可以根據(jù)自己所需添加新值. 所有scope, 初始化方法, 銷毀方法, 或靜態(tài)工廠方法等其他你在子bean指定的, 會(huì)覆蓋父bean的設(shè)置.

除上面提到的設(shè)置外, 其他的設(shè)置都會(huì)在子bean上定義: 依賴檢查, 裝配模式, 依賴檢查, 單例, 延遲初始化等等

上一個(gè)例子中, 通過使用abstract屬性來顯式標(biāo)記父bean定義是抽象的. 如果父定義沒有指定一個(gè)類, 那么必須要使用abstract屬性來生命這是一個(gè)抽象配置, 如下所示:

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>

因?yàn)楦竍ean是抽象且不完整的, 所以是不可以被實(shí)例化的, 當(dāng)一個(gè)bean定義被生命成抽象的, 那么他就是只能是一個(gè)模板, 被子bean使用.

ApplicationContext默認(rèn)是提前實(shí)例化所有的單例. 所以, 如果你只是想讓一個(gè)bean定義當(dāng)成模板來使用, 那么就必須要在bean定義上設(shè)置抽象屬性為True. 否則容器就會(huì)去提前實(shí)例化(嘗試)這個(gè)抽象bean.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末橄仆,一起剝皮案震驚了整個(gè)濱河市剩膘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盆顾,老刑警劉巖怠褐,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異您宪,居然都是意外死亡奈懒,警方通過查閱死者的電腦和手機(jī)具温,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筐赔,“玉大人,你說我怎么就攤上這事揖铜≤罘幔” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵天吓,是天一觀的道長贿肩。 經(jīng)常有香客問我,道長龄寞,這世上最難降的妖魔是什么汰规? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮物邑,結(jié)果婚禮上溜哮,老公的妹妹穿的比我還像新娘。我一直安慰自己色解,他們只是感情好茂嗓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著科阎,像睡著了一般述吸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锣笨,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天蝌矛,我揣著相機(jī)與錄音,去河邊找鬼错英。 笑死入撒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的走趋。 我是一名探鬼主播衅金,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼簿煌!你這毒婦竟也來了氮唯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤姨伟,失蹤者是張志新(化名)和其女友劉穎惩琉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夺荒,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞒渠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年良蒸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伍玖。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嫩痰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窍箍,到底是詐尸還是另有隱情串纺,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布椰棘,位于F島的核電站纺棺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏邪狞。R本人自食惡果不足惜祷蝌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帆卓。 院中可真熱鬧巨朦,春花似錦、人聲如沸剑令。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尚洽。三九已至悔橄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腺毫,已是汗流浹背癣疟。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留潮酒,地道東北人睛挚。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像急黎,于是被迫代替她去往敵國和親扎狱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

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