內(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í)例掷匠。
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é)圖表中展示的那樣。
下面的例子是如何在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配置
為了支持request
,session
嘴脾,application
和websocket
等域男摧,在你進(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)建appPreferences
bean實(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的InitializingBean
和 DisposableBean
兩個(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.AutoCloseable
或java.io.Closeable
接口的累也會(huì)因此而匹配上). 這個(gè)值也可以設(shè)置再default-destroy-method
中反惕,下一節(jié)內(nèi)容會(huì)詳解介紹.
默認(rèn)的初始化和銷毀方法
當(dāng)你編寫初始化和銷毀回調(diào)時(shí), 可以不使用使用Spring指定的InitializingBean
和DisposableBean
接口. 通常你應(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-method
和 destroy-method
, 這樣就可以覆蓋默認(rèn)值了.
多種方式的結(jié)合使用
在Spring2.5 中, 你有三種方式來控制bean的生命周期, 他們分別是: 實(shí)現(xiàn) InitializingBean
和 DisposableBean
回調(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();
}
需要注意的是, LifecycleProcessor
是Lifecycle
的子接口. 它增加了兩個(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)用ConfigurableApplicationContext
的registerShutdownHook()
方法:
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接口
除了上面提到的ApplicationContextAware
和 BeanNameAware
以外, 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.