<div style="font-size:34px">Spring4參考手冊中文版</div>
<h1>作者簡介</h1>
翻譯 鐵柱 wwwshiym@gmail.com
顧問 張丙天
鐵柱 曾任中科軟科技股份有限公司應(yīng)用系統(tǒng)事業(yè)群部技術(shù)副總監(jiān)、首席架構(gòu)師,2008年加入中科軟。擅長SOA、企業(yè)信息化架構(gòu),精通Java、Spring,在多線程、io、虛擬機(jī)調(diào)優(yōu)、網(wǎng)絡(luò)通信及支撐大型網(wǎng)站的領(lǐng)域有較多經(jīng)驗(yàn),對技術(shù)有濃厚的興趣?,F(xiàn)致力于無線、數(shù)據(jù)、業(yè)務(wù)平臺、組件化方面取得突破。
張丙天
<h1>前言</h1>
我是前言。
<h1 id="spring-core">Part III. 核心技術(shù)</h1>
本部分參考手冊完全覆蓋了Srping 框架的全部技術(shù)
首先是Spring IoC控制反轉(zhuǎn)。深入徹底的IoC講解之后,緊隨其后的是全面解說Spring AOP。Spring有自己的AOP框架,該框架概念簡單易于理解,能解決Java企業(yè)應(yīng)用中80%的需求
Spring也集成了AspectJ,AspectJ是現(xiàn)今java領(lǐng)域功能最豐富、最成熟的AOP實(shí)現(xiàn)。
最后面的部分是tdd測試驅(qū)動開發(fā),也是Spring 開發(fā)團(tuán)隊(duì)最為推崇的開發(fā)方法,主要內(nèi)容有單元測試和spirng對集成測試的支持。Spring 團(tuán)隊(duì)發(fā)現(xiàn),正確的使用Ioc,會使單元測試和集成測試更加簡單(因?yàn)轭愔惺褂肧etter和構(gòu)造函數(shù),將使它們更容易的配合,而無需使用set up組裝)。同時(shí),為測試弄了專門的單獨(dú)章節(jié),希望你能領(lǐng)悟這一點(diǎn)
- Chapter 4, The IoC container
- Chapter 5, Resources
- Chapter 6, Validation, Data Binding, and Type Conversion
- Chapter 7, Spring Expression Language (SpEL)
- Chapter 8, Aspect Oriented Programming with Spring
- Chapter 9, Spring AOP APIs
- Chapter 10, Testing
<h2 id="beans">IoC容器</h2>
<h3 id="beans-introduction">springIOC容器和beans簡介</h3>
本章講解spring的控制反轉(zhuǎn)(IoC)的spring 框架實(shí)現(xiàn) [1] 原理. IoC 又名 依賴注入 (DI). 它是一個(gè)由對象定義依賴的處理手法,也就是如何與其他對象協(xié)同工作, 可以通過以下途徑定義依賴:構(gòu)造參數(shù)注入、工廠方法的參數(shù)注入、屬性注入(是指對象實(shí)例化后或者從工廠方法返回一個(gè)實(shí)例后設(shè)置其屬性)。容器創(chuàng)建bean時(shí)候, 注入 依賴。 這個(gè)控制倒轉(zhuǎn)了, 因此得名控制反轉(zhuǎn) (IoC)。反轉(zhuǎn)了哪些控制,不再是由bean自己控制依賴類的實(shí)例化和定位, 而是使用了類似于 服務(wù)定位 模式的機(jī)制來控制。
org.springframework.beans 和 org.springframework.context 這兩個(gè)包是spring IOC容器的基礎(chǔ)包. BeanFactory 接口 提供了各種配置,用于管理任何對象. ApplicationContext 是 BeanFactory的子接口. 它提供以下功能,使集成Spring’s AOP功能 更容易; 消息資源處理(用于國際化);事件發(fā)布;應(yīng)用層指定上下文環(huán)境,像用于web應(yīng)用的WebApplicationContext.
簡而言之, BeanFactory 提供了配置框架和基礎(chǔ)功能, ApplicationContext 增加了更多的企業(yè)應(yīng)用用能. ApplicationContext 是 BeanFactory的超集, 是本章的spring IOC示例中的指定容器. 用BeanFactory 替代 ApplicationContext, 更多的信息請參看 Section 4.17, “The BeanFactory”.
應(yīng)用中的對象并且是由spring 容器 管理的,被稱為beans.就是對象,由spring容器管理的諸如實(shí)例化、組裝等等操作. bean可以由應(yīng)用中的多個(gè)對象組成。Bean通過容器和配置元數(shù)據(jù) ,使用反射技術(shù),去組裝依賴對象。
<h3 id="beans-basics">容器概述</h3>
接口org.springframework.context.ApplicationContext代表了srping IoC 容器,負(fù)責(zé)實(shí)例化、配置和組裝前面提到的beans。容器依據(jù)配置配置元數(shù)據(jù)去實(shí)例化、配置、組裝。配置元數(shù)據(jù)可以用XML、Java 注解、或者Java編碼表示。在配置元數(shù)據(jù)中,可以定義組成應(yīng)用的對象,以及對象之間的依賴關(guān)系。
Spring 提供了一些開箱即用的ApplicationContext接口的實(shí)現(xiàn)。在單獨(dú)的應(yīng)用中,通常使用ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext。當(dāng)使用XML定義配置元數(shù)據(jù)時(shí),可通過一小段xml配置使容器支持其他格式的配置元數(shù)據(jù),比如Java 注解、Java Code。
大多數(shù)的應(yīng)用場景中,不需要硬編碼來實(shí)例化一個(gè)Spring IoC 的容器。舉個(gè)栗子,web應(yīng)用中,在web.xml中大概8行左右的配置就可以實(shí)例化一個(gè)Spring Ioc容器(see Section 4.16.4, “Convenient ApplicationContext instantiation for web applications”)。若再有STS(Spring eclipse 套件),簡單的鉤鉤點(diǎn)點(diǎn)即可完成此配置。
下面的示意圖是spring工作原理。ApplicationContext將應(yīng)用中的類與配置元數(shù)據(jù)相結(jié)合,實(shí)例化后,即可得到一個(gè)可配置、可執(zhí)行的系統(tǒng)或應(yīng)用。
The Spring IoC container

<h4 id="beans-factory-metadata">配置元數(shù)據(jù)</h4>
如上圖所示,Spring IoC容器使用某種格式的配置元數(shù)據(jù);配置元數(shù)據(jù),就是告訴Ioc容器如何將對象實(shí)例化、配置、組裝。
配置元數(shù)據(jù)是默認(rèn)使用簡單直觀的xml格式,也是本章樣例中使用最多的,這些樣例程序用以說明spring Ioc 容器的核心概念和功能

基于XML的元數(shù)據(jù)并不是唯一的格式。Spring IoC容器已經(jīng)和提及到的元數(shù)據(jù)格式完全解耦了。目前,很多碼農(nóng)都選擇Java-based configuration
欲了解其他元數(shù)據(jù)格式的使用,請參看:
- Annotation-based configuration: Spring 2.5 引進(jìn)的支持java 注解配置元數(shù)據(jù)
-
Java-based configuration: Spring3.0時(shí),將Spring JavaConfig project的很多功能集成到核心Spring框架中。Thus you can define beans external to your application classes by using Java rather than XML files.你可以使用java配置類定義bean,無需xml,該配置類與應(yīng)用類無關(guān)。想要嘗鮮的話,請參看
@Configuration,@Bean,@Import,@DependsOn注解
Spring配置由Spring bean的定義組成,這些bean必須被容器管理,至少1個(gè),通常會有多個(gè)。基于XML的配置元數(shù)據(jù),大概這么配置,根節(jié)點(diǎn)<beans>中配置子節(jié)點(diǎn)<bean>。Java configuration使用是這樣的,一個(gè)帶有@Configuration類注解的類中,方法上使用@Bean方法注解。
bean的定義要與應(yīng)用中實(shí)際的類相一致??梢远xservice 層的對象、Dao對象、類似Struts的表現(xiàn)成的對象、像Hibernate SessionFactories這樣的基礎(chǔ)對象,JMS隊(duì)列等等。通常不會去定義細(xì)粒度域?qū)ο螅驗(yàn)樗鼈冇蒁AO或者Service負(fù)責(zé)創(chuàng)建、加載。然而,通過集成AspectJ,可以配置非Srping容器創(chuàng)建的對象。參看Using AspectJ to dependency-inject domain objects with Spring
下面的樣例展示了基于XML配置元數(shù)據(jù)的基本格式:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- bean的詳細(xì)配置 -->
</bean>
<bean id="..." class="...">
<!-- bean的詳細(xì)配置 -->
</bean>
<!-- 其他bean -->
</beans>
id屬性是個(gè)字串,是bean的唯一標(biāo)示符。class屬性定義了bean的類型,要使用類的全限定類名(含有包路徑)。id屬性的值,可以作為合作bean的引用標(biāo)示符。上面未展示如何引用其他對象;詳情參看Dependencies
<h4 id='beans-factory-instantiation'>容器實(shí)例化</h4>
Spring IoC的實(shí)例化易如反掌。ApplicationContext構(gòu)造函數(shù)支持定位路徑,定位路徑也可以是多個(gè),它是標(biāo)識實(shí)際資源的字串,容器使用該標(biāo)識加載配置元數(shù)據(jù),支持多種資源,比如:本地文件系統(tǒng)、CLASSPATH等等。
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});

在學(xué)習(xí)了Spring IoC容器之后,也許你想了解更多的Spring的資源,如前所述在第6章,資源使用URI語法定位輸入流,Spring提供了方便的機(jī)制讀取輸入流。在第6.7章“Application contexts and Resource paths”,專門講述5用 資源路徑構(gòu)造應(yīng)用上下文,資源路徑也是慣用手法。
接下來的樣例展示了配置service層對象:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- 有關(guān)屬性配置 -->
</bean>
<!--更多的Service bean -->
</beans>
下面的樣例展示了數(shù)據(jù)訪問對象dao.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
上述內(nèi)容中service層由PetStoreServiceImpl類、2個(gè)dao對象JpaAccountDao和JpaItemDao(基于JPA ORM標(biāo)準(zhǔn))。屬性name元素引用了JavaBean的屬性,ref元素引用了其他bean定義。這個(gè)引用表示實(shí)際對象之間的引用依賴。配置一個(gè)對象的依賴,詳情請參看Dependencies
<h5 id='beans-factory-xml-import'>引入基于xml的元數(shù)據(jù)</h5>
多個(gè)配置文件共同定義bean非常有用。通常,每個(gè)XML配置文件在你的架構(gòu)中代表一個(gè)邏輯層或者一個(gè)模塊。
你可以使用應(yīng)用上下文(applicationContext)的構(gòu)造函數(shù)去加載所有xml中定義的bean。這個(gè)構(gòu)造函數(shù)使用多個(gè)資源定位,就像前面中提到的。或者,也可以用一個(gè)或者多個(gè)資源引用,即使用<import/>標(biāo)簽加載其他文件定義的bean。舉個(gè)栗子:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
上例中,從三個(gè)外部文件加載定義的bean:services.xml,messageSource.xml,themeSource.xml 。被引入的文件的路徑對于引入配置文件來說都是相對路徑,所以service.xml必須在引入配置文件的相同文件路徑或者相同的類路徑中。而messageSource.xml和themeSource.xml必須在引入配置文件所在的文件夾下的resouce文件夾下。正如你所看到的 /開頭會被忽略掉,因?yàn)檫@些路徑是相對路徑,推薦不要使用/開頭的格式。導(dǎo)入(imported)文件內(nèi)容,包含根節(jié)點(diǎn)<beans/>,配置中XML bean定義 必須經(jīng)過Spring語法校驗(yàn)通過。

使用"../"表示父目錄的相對路徑是可以的,但是真心不推薦這樣創(chuàng)建一個(gè)依賴應(yīng)用外部文件的做法。尤其指出,使用"classpath:"資源類型的URLs(像這樣:"classpath:../services.xml"),也是不推薦的,因?yàn)檫\(yùn)行時(shí)處理過程會選擇"最近的"根路徑然后引入他的父目錄配置文件。Classpath配置的改變,會導(dǎo)致應(yīng)用選擇一個(gè)不同的、錯(cuò)誤的目錄。
你可以使用全路徑限定資源定位取代相對路徑,比如:"file:C:/config/services.xml" 或者"classpath:/config/services.xml"。還有,你可以使用抽象路徑來解耦應(yīng)用和配置文件。使用一個(gè)邏輯定位更可取 ,比如:通過"${..}"占位符,使用JVM運(yùn)行時(shí)計(jì)算出的路徑。
<h4 id='beans-factory-client'>使用容器</h4>
ApplicationContext是一個(gè)高級工廠的接口,能維護(hù)各種bean以及他們之間依賴的注冊。使用方法T getBean(String name, Class<T> requiredType),就能從定義的bean中獲取實(shí)例。
ApplicationContext能讓你讀取bean定義、訪問他們,如下:
// create and configure beans
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
使用getBean()從beans中獲取實(shí)例。ApplicationContext接口有幾種方法可以辦到,但是理想的做法是不要使用他們。實(shí)際上,應(yīng)用中根本就不該使用getBean()方法,這樣就不依賴Sprig API了。比如,Spring集成了很多web框架,為各種web框架類提供了依賴注入,比如web框架的Controller和JSF-managed beans
<h3 id='beans-definition'>Bean概述</h3>
Spring IoC容器管理一個(gè)或多個(gè)bean。這些bean根據(jù)提供給容器的配置元數(shù)據(jù)創(chuàng)建的,比如使用XML格式<bean/>定義。
在容器內(nèi)部,這些bean的定義用 BeanDefinition對象表示,BeanDefinition包含了下列元數(shù)據(jù):
- 全路徑(含包路徑)類名:代表bean的實(shí)際實(shí)現(xiàn)類。
- Bean行為配置元素,它規(guī)定了bean在容器中行為(作用域,生命周期回調(diào)函數(shù)等等)
- 引用其他bean,就是為了bean能正常工作而所需的其他bean的引用。這些引用類也稱為合作類或者依賴類。
- 其他配置,為實(shí)例設(shè)置的其他屬性配置。比如說,管理連接池的bean的連接數(shù),池大小的上限。
這些元數(shù)據(jù)將轉(zhuǎn)換成bean定義(BeanDefinition類)的屬性。
The bean definition
| 屬性 | 詳情 |
|---|---|
| class | Section 5.3.2, “Instantiating beans” |
| name | Section 5.3.1, “Naming beans” |
| scope | Section 5.5, “Bean scopes” |
| constructor arguments | Section 5.4.1, “Dependency injection” |
| properties | Section 5.4.1, “Dependency injection” |
| autowiring mode | Section 5.4.5, “Autowiring collaborators” |
| lazy-initialization mode | Section 5.4.4, “Lazy-initialized beans” |
| initialization method | the section called “Initialization callbacks” |
| destruction method | the section called “Destruction callbacks” |
除了bean的信息以外,BeanDefinition也包含創(chuàng)建特殊bean的信息,ApplicationContext的實(shí)現(xiàn)也允許注冊由用戶創(chuàng)建而非IoC容器創(chuàng)建的對象。通過訪問ApplicationContext’s BeanFactory的方法getBeanFactory(),該方法返回BeanFactory的實(shí)現(xiàn)DefaultListableBeanFactory。DefaultListableBeanFactory類支持這種注冊,通過registerSingleton(..)和registerBeanDefinition(..)方法實(shí)現(xiàn)。然而,典型的應(yīng)用只用元數(shù)據(jù)定義的bean就可以單獨(dú)運(yùn)行。
<h4 id='beans-beanname'>beans命名</h4>
bean有一個(gè)或者多個(gè)標(biāo)示符。這些標(biāo)示符必須是所在容器范圍內(nèi)必唯一的。通常情況一下,一個(gè)bean僅有一個(gè)標(biāo)示符,如果有需求需要多個(gè),多出來的將被當(dāng)做別名。
在XML格式配置元數(shù)據(jù)中,使用 id 或者 name 屬性來作為bean的標(biāo)示符。id屬性只能有1個(gè)。命名規(guī)范是字符數(shù)字混編(myBean,fooService,等等),但也支持特殊字符,可以包含。若想給bean起個(gè)別名,則可使用name屬性來指定,可以是多個(gè),用英文的逗號(,)分隔、分號(;)也行、空格也行。注意,在Spring3.1以前,id屬性定義成了xsd:ID類型,該類型強(qiáng)制為字符(譯者心里說:估計(jì)字母+特殊字符,不支持?jǐn)?shù)字的意思,有待驗(yàn)證,沒工夫驗(yàn)證去了,翻譯進(jìn)度太慢了。再說了,現(xiàn)在都用4了,你再說3.0怎么著怎么著,那不跟孔乙己似的跟別人吹噓茴香豆有四種寫法)。3.1版開始,它被定義為xsd:string類型。注意,bean id的唯一性約束依然被容器強(qiáng)制使用,盡管xml解析器不再支持了。譯者注:在spring3(含)以前,id是可以相同的,容器會替換相同id的bean,而在新版中,容器初始化過程中發(fā)現(xiàn)id相同拋出異常,停止實(shí)例化
id 和name屬性不是bean所必須的。若未明確指定id或者name屬性,容器會給它生成一個(gè)唯一name屬性。當(dāng)然了,如果你想通過bean的name屬性引用,使用ref元素方式,或者是類似于Service Locator模式方式檢索bean(譯者想:應(yīng)該是指調(diào)用ApplicationContext.getBean()方法獲取bean,類似這種方式。Service Locator是一種設(shè)計(jì)模式,其實(shí)換個(gè)名字是不是更合適,DL(Dependency Lookup依賴查找)。雖然現(xiàn)在我也不明白,但是下面有專門的章節(jié)講解,翻到時(shí)候再詳細(xì)了解),就必須給bean指定 name了。之所以支持無name bean特性,是為了使內(nèi)部類自動裝配。
Bean命名規(guī)范
bean命名規(guī)范使用java命名規(guī)范中實(shí)例屬性名(也稱域,F(xiàn)ield)規(guī)范。小寫字母開頭的駝峰式。像這樣
(不包括單引號)`accountManager`,`accountService`,`userDao`,
`loginController`,等等
規(guī)范的命名使配置易讀易理解。若使用Spring AOP,通過名字增強(qiáng)(譯注:大多數(shù)Spring AOP教材中
的 通知)一坨bean時(shí),規(guī)范的命名將帶來極大的方便。
<h5 id='beans-beanname-alias'>bean定義之外設(shè)置別名</h5>
定義的bean內(nèi),可以給bean多個(gè)標(biāo)識符,組合id屬性值和任意數(shù)量的name屬性值。這些標(biāo)識符均可作為該bean的別名,對于有些場景中,別名機(jī)制非常有用,比如應(yīng)用中組件對自身的引用。(譯注:一個(gè)類持有一個(gè)本類的實(shí)例作為屬性,看起來應(yīng)該是這樣的,以下代碼為推測,可以執(zhí)行)
Bean類
public class SomeBean {
//注意看這個(gè)屬性,就是本類
private SomeBean someBean;
public SomeBean(){}
public void setSomeBean(SomeBean someBean) {
this.someBean = someBean;
}
}
配置元數(shù)據(jù)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--看bean的別名,使用,/;/空格 分隔都是可以是-->
<bean id="someBeanId" name="someBean,someBeanA;someBeanB someBeanC" class="com.example.spring.bean.SomeBean">
<!--將別名為someBeanA 的bean 注入給 id為someBeanId 的bean的屬性 'someBean'-->
<property name="someBean" ref="someBeanA"></property>
</bean>
</beans>
測試代碼
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeBeanTests {
@Autowired
@Qualifier("someBeanId")
private SomeBean someBean;
@Test
public void testSimpleProperties() throws Exception {
}
}
在bean的定義處指定所有別名有時(shí)候并不合適,然而,在其他配置文件中給bean設(shè)置別名卻更為恰當(dāng)。此法通常應(yīng)用在大型系統(tǒng)的場景中,配置文件分散在各個(gè)子系統(tǒng)中,每個(gè)子系統(tǒng)都有本系統(tǒng)的bean定義。XML格式配置元數(shù)據(jù),提供<alias/>元素,可以搞定此用法。
<alias name="fromName" alias="toName"/>
這種情況下,在同容器中有個(gè)叫fromName的bean,或者叫其他的阿貓阿狗之類的,再使用此別名定義之后,即可被當(dāng)做toName來引用。
舉個(gè)栗子,子系統(tǒng)A中的配置元數(shù)據(jù)也許引用了一個(gè)被命名為subsystemA-dataSource的bean。子系統(tǒng)B也許引用了一個(gè)subsystemB-dataSource。將這兩個(gè)子系統(tǒng)整合到主應(yīng)用中,而主應(yīng)用使用了一個(gè)myApp-dataSource,為了使3個(gè)bean引用同一個(gè)對象,得在MyApp配置元數(shù)據(jù)中使用別名定義:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
現(xiàn)在,每個(gè)組件和主應(yīng)用都能通過bean 名引用dataSource,而bean名都是唯一的保證不與其他定義沖突(實(shí)際上創(chuàng)建了一個(gè)命名空間),但他們引用的都是同一個(gè)bean。
Java-configuration
如果你使用了Java-configuration,@Bean注解也提供了別名,詳見Section 5.12.3, “Using the @Bean annotation”
<h4 id='beans-factory-class'>bean實(shí)例化</h4>
bean的定義,本質(zhì)是如何創(chuàng)建一個(gè)或多個(gè)對象的配方。容器被請求時(shí),會解析配置元數(shù)據(jù)中的bean定義并封裝,使用封裝配置創(chuàng)建(或者獲?。ο髮?shí)例。
若使用XML格式配置元數(shù)據(jù),得為將要實(shí)例化的對象指定類型(或者說是類),使用<bean/>元素的class屬性實(shí)現(xiàn)。class屬性 映射到BeanDefinition類實(shí)例的Class屬性(域),這個(gè)class屬性是<bean/>元素必須的。(例外情況,參看“Instantiation using an instance factory method” 和 Section 5.7, “Bean definition inheritance”。使用Class域?qū)傩?,通過以下兩種方式:
- 通常,通過指定bean的
class屬性,容器使用反射調(diào)用其構(gòu)造函數(shù)直接創(chuàng)建bean,有點(diǎn)像Java 編碼中使用new操作符。 - 指定
class實(shí)際類含有用于創(chuàng)建對象的靜態(tài)工廠方法,這是不常使用的場景,容器會調(diào)用類的靜態(tài)工廠方法創(chuàng)建bean。調(diào)用靜態(tài)工廠方法返回的對象類型也許是相同類型,也許完全是其他類。
<div class="sidebar">
<b>內(nèi)部類命名</b> 若要定義靜態(tài)內(nèi)部類,得將類名劈開。
舉例來說,現(xiàn)在在<span class="scode">com.example</span>包有個(gè)類<span class="scode">Foo</span>,該類有靜態(tài)內(nèi)部類<span class="scode">Bar</span>,定義<span class="scode">Bar</span>的Spring bean的<span class="scode">class</span>屬性差不多是這樣
<code class="scode">com.example.Foo$Bar</code>
注意<span class="scode">$</span>字符,用它來分隔內(nèi)部類名和外圍類名
</div>
<h4 id='beans-factory-class-ctor'>用構(gòu)造函數(shù)實(shí)例化</h4>
若是使用構(gòu)造函數(shù)方式創(chuàng)建bean,所有的常規(guī)類都可以使用Spring來創(chuàng)建、管理。也就是說,開發(fā)的類無需實(shí)現(xiàn)任何特殊接口或者使用某種特殊編碼風(fēng)格。僅需指定bean的class即可。對于特殊的bean管理,取決于你使用的IoC類型,也許需要一個(gè)默認(rèn)的空構(gòu)造。
Spring IoC容器幾乎能管理任何你需要管理的類,不局限于真正的JavaBeans。大多數(shù)Spring的用戶心中,真正的JavaBean是這樣的:僅有1個(gè)默認(rèn)的無參構(gòu)造函數(shù)、屬性、setter、getter。嗯,比如,現(xiàn)在需要使用一個(gè)廢棄連接池,它肯定不符合JavaBean規(guī)范,Spring照樣能管理。
使用XML格式配置元數(shù)據(jù) 定義bean的class,如下所示:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
如何為構(gòu)造函數(shù)指定參數(shù)?如何在對象實(shí)力話之后設(shè)置其屬性?請參看Injecting Dependencies
<h5 id='beans-factory-class-static-factory-method'>使用靜態(tài)工廠方法實(shí)例化</h5>
定義使用使用靜態(tài)工廠方法創(chuàng)建的bean時(shí),得指定工廠方法類的作為class屬性值,并且還得指定工廠方法類中用于創(chuàng)建bean的方法名稱,作為factory-method屬性值。工廠方法可以有參數(shù),調(diào)用該方法即可返回對象實(shí)例,就像通過構(gòu)造函數(shù)創(chuàng)建對象實(shí)例一樣。此種bean定義是為了兼容遺留系統(tǒng)中的靜態(tài)工廠
下面的bean定義,是使用工廠方法創(chuàng)建bean的方式。定義中,無需指定返回對象的類型(class),而是指定工廠方法類的class。下例中,createInstance()方法必須是一個(gè)static靜態(tài)方法。
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
繼續(xù)
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
<h5 id='beans-factory-class-instance-factory-method'>使用實(shí)例工廠方法實(shí)例化</h5>
和靜態(tài)工廠方法類似的還有實(shí)例工廠方法,使用實(shí)例工廠方法的方式實(shí)例化,是調(diào)用容器中已存在的bean的一個(gè)非靜態(tài)方法來創(chuàng)建一個(gè)bean。用法是,1、class屬性置空設(shè)置。 2、設(shè)置factory-bean屬性,其值為當(dāng)前容器(或者父容器)中bean的名字,該bean包含可供調(diào)用的創(chuàng)建對象的實(shí)例方法。3、設(shè)置factory-method屬性,其值為工廠方法名。
<!-- 工廠類, 包含一個(gè)方法createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
工廠類如下
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
}
工廠類可以有多個(gè)工廠方法:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
工廠類如下:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
上例中展示了工廠類本身也可以通過 DI 管理和配置。參看DI詳情

Srping 資料中,factory bean是指一個(gè)Spring配置的bean,該bean能通過實(shí)例或者靜態(tài)工廠方法創(chuàng)建對象。對比之下,
FactoryBean(注意大寫)是指Spring術(shù)語FactoryBean。這段沒太理解,解釋factory bean和FactoryBean。
<h3 id='beans-dependencies'>依賴</h3>
企業(yè)應(yīng)用絕不會只有1個(gè)簡單對象(或者說Spring bean)。哪怕是最簡單的應(yīng)用,也會包含許多對象協(xié)同工作。下一章節(jié)講述,如何為真正的應(yīng)用定義大量的、獨(dú)立的bean,并讓這些對象一起合作。
<h4 id="beans-factory-collaborators">依賴注入</h4>
依賴注入(DI),是一個(gè)有對象定義依賴的手法,也就是,如何與其他對象合作,通過構(gòu)造參數(shù)、工廠方法參數(shù)、或是在對象實(shí)例化之后設(shè)置對象屬性,實(shí)例化既可以構(gòu)造也可以是使用工廠方法。容器在它創(chuàng)建bean之后注入依賴。這個(gè)過程從根本上發(fā)生了反轉(zhuǎn),因此又名控制反轉(zhuǎn)(Ioc),因?yàn)镾pring bean自己控制依賴類的實(shí)例化或者定位 ,Spring bean中就有依賴類的定義,容器使用依賴類構(gòu)造器創(chuàng)建依賴類實(shí)例,使用Service Locator模式定位依賴類。
DI機(jī)制使代碼簡潔,對象提供它們的依賴,解耦更高效。對象無需自己查找依賴。同樣的,類更容易測試,尤其當(dāng)依賴接口或者抽象類時(shí),測試允許在單元測試中使用stub或者mock(模擬技術(shù))實(shí)現(xiàn)。
DI有2種主要方式,構(gòu)造注入 和 setter注入
構(gòu)造注入,容器調(diào)用構(gòu)造函數(shù)并傳參數(shù),每個(gè)參數(shù)都是依賴。調(diào)用靜態(tài)工廠方法并傳參數(shù)方式構(gòu)造bean和構(gòu)造注入差不多,這里是指構(gòu)造注入處理參數(shù)和靜態(tài)工廠方法處理參數(shù)像類似。下例中展示了一個(gè)只能使用構(gòu)造注入的類。注意,此類無任何特別之處,并未依賴容器指定的接口、基類、注解,就是一個(gè)POJO
public class SimpleMovieLister {
// the SimpleMovieLister 依賴 a MovieFinder
private MovieFinder movieFinder;
//Spring容器能注入MovieFinder的構(gòu)造函數(shù)
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// 實(shí)際如何使用MovieFinder的業(yè)務(wù)邏輯省略了
}
<h5 id='beans-factory-ctor-arguments-resolution'>構(gòu)造函數(shù)參數(shù)解決方案</h5>
構(gòu)造參數(shù)解決方案,會匹配所使用的參數(shù)類型。如果在bean的定義中,構(gòu)造參數(shù)不存在歧義,那么,在bean定義中定義的構(gòu)造參數(shù)的次序,在bean實(shí)例化時(shí),就是提供給適合的構(gòu)造參數(shù)的次序??催@個(gè)類:
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
不存在歧義,假設(shè)Bar和Baz類沒有集成關(guān)系,那么下面的配置是合法的,而且,不需要在<constructor-arg/>元素里指定構(gòu)造參數(shù)的明確的indexes索引或者類型。
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
若需要引用另一個(gè)bean,類型已知,構(gòu)造函數(shù)就可以匹配參數(shù)類型(像上面的示例)。使用簡單類型時(shí), 想<value>true</true>,Srping不能決定value類型情況,Spring就不能自己匹配類型。例如:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
上面的場景中,如果使用type屬性明確指定構(gòu)造參數(shù)的類型,容器就可以使用類型匹配。比如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
使用index屬性明確指定構(gòu)造參數(shù)的次序。比如
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>