<div style="font-size:34px">Spring4參考手冊中文版</div>
<h1>作者簡介</h1>
翻譯 鐵柱 wwwshiym@gmail.com
顧問 張丙天
鐵柱 曾任中科軟科技股份有限公司應用系統事業群部技術副總監、首席架構師,2008年加入中科軟。擅長SOA、企業信息化架構,精通Java、Spring,在多線程、io、虛擬機調優、網絡通信及支撐大型網站的領域有較多經驗,對技術有濃厚的興趣?,F致力于無線、數據、業務平臺、組件化方面取得突破。
張丙天
<h1>前言</h1>
我是前言。
<h1 id="spring-core">Part III. 核心技術</h1>
本部分參考手冊完全覆蓋了Srping 框架的全部技術
首先是Spring IoC控制反轉。深入徹底的IoC講解之后,緊隨其后的是全面解說Spring AOP。Spring有自己的AOP框架,該框架概念簡單易于理解,能解決Java企業應用中80%的需求
Spring也集成了AspectJ,AspectJ是現今java領域功能最豐富、最成熟的AOP實現。
最后面的部分是tdd測試驅動開發,也是Spring 開發團隊最為推崇的開發方法,主要內容有單元測試和spirng對集成測試的支持。Spring 團隊發現,正確的使用Ioc,會使單元測試和集成測試更加簡單(因為類中使用Setter和構造函數,將使它們更容易的配合,而無需使用set up組裝)。同時,為測試弄了專門的單獨章節,希望你能領悟這一點
- 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的控制反轉(IoC)的spring 框架實現 [1] 原理. IoC 又名 依賴注入 (DI). 它是一個由對象定義依賴的處理手法,也就是如何與其他對象協同工作, 可以通過以下途徑定義依賴:構造參數注入、工廠方法的參數注入、屬性注入(是指對象實例化后或者從工廠方法返回一個實例后設置其屬性)。容器創建bean時候, 注入 依賴。 這個控制倒轉了, 因此得名控制反轉 (IoC)。反轉了哪些控制,不再是由bean自己控制依賴類的實例化和定位, 而是使用了類似于 服務定位 模式的機制來控制。
org.springframework.beans
和 org.springframework.context
這兩個包是spring IOC容器的基礎包. BeanFactory
接口 提供了各種配置,用于管理任何對象. ApplicationContext
是 BeanFactory
的子接口. 它提供以下功能,使集成Spring’s AOP功能 更容易; 消息資源處理(用于國際化);事件發布;應用層指定上下文環境,像用于web應用的WebApplicationContext
.
簡而言之, BeanFactory
提供了配置框架和基礎功能, ApplicationContext
增加了更多的企業應用用能. ApplicationContext
是 BeanFactory
的超集, 是本章的spring IOC示例中的指定容器. 用BeanFactory
替代 ApplicationContext
, 更多的信息請參看 Section 4.17, “The BeanFactory”.
應用中的對象并且是由spring 容器 管理的,被稱為beans.就是對象,由spring容器管理的諸如實例化、組裝等等操作. bean可以由應用中的多個對象組成。Bean通過容器和配置元數據 ,使用反射技術,去組裝依賴對象。
<h3 id="beans-basics">容器概述</h3>
接口org.springframework.context.ApplicationContext
代表了srping IoC 容器,負責實例化、配置和組裝前面提到的beans。容器依據配置配置元數據去實例化、配置、組裝。配置元數據可以用XML、Java 注解、或者Java編碼表示。在配置元數據中,可以定義組成應用的對象,以及對象之間的依賴關系。
Spring 提供了一些開箱即用的ApplicationContext
接口的實現。在單獨的應用中,通常使用ClassPathXmlApplicationContext
或者FileSystemXmlApplicationContext
。當使用XML定義配置元數據時,可通過一小段xml配置使容器支持其他格式的配置元數據,比如Java 注解、Java Code。
大多數的應用場景中,不需要硬編碼來實例化一個Spring IoC 的容器。舉個栗子,web應用中,在web.xml中大概8行左右的配置就可以實例化一個Spring Ioc容器(see Section 4.16.4, “Convenient ApplicationContext instantiation for web applications”)。若再有STS(Spring eclipse 套件),簡單的鉤鉤點點即可完成此配置。
下面的示意圖是spring工作原理。ApplicationContext
將應用中的類與配置元數據相結合,實例化后,即可得到一個可配置、可執行的系統或應用。
The Spring IoC container
<h4 id="beans-factory-metadata">配置元數據</h4>
如上圖所示,Spring IoC容器使用某種格式的配置元數據;配置元數據,就是告訴Ioc容器如何將對象實例化、配置、組裝。
配置元數據是默認使用簡單直觀的xml格式,也是本章樣例中使用最多的,這些樣例程序用以說明spring Ioc 容器的核心概念和功能
基于XML的元數據并不是唯一的格式。Spring IoC容器已經和提及到的元數據格式完全解耦了。目前,很多碼農都選擇Java-based configuration
欲了解其他元數據格式的使用,請參看:
- Annotation-based configuration: Spring 2.5 引進的支持java 注解配置元數據
-
Java-based configuration: Spring3.0時,將Spring JavaConfig project的很多功能集成到核心Spring框架中。Thus you can define beans external to your application classes by using Java rather than XML files.你可以使用java配置類定義bean,無需xml,該配置類與應用類無關。想要嘗鮮的話,請參看
@Configuration
,@Bean
,@Import
,@DependsOn
注解
Spring配置由Spring bean的定義組成,這些bean必須被容器管理,至少1個,通常會有多個。基于XML的配置元數據,大概這么配置,根節點<beans>
中配置子節點<bean>
。Java configuration使用是這樣的,一個帶有@Configuration
類注解的類中,方法上使用@Bean
方法注解。
bean的定義要與應用中實際的類相一致??梢远xservice 層的對象、Dao對象、類似Struts的表現成的對象、像Hibernate SessionFactories這樣的基礎對象,JMS隊列等等。通常不會去定義細粒度域對象,因為它們由DAO或者Service負責創建、加載。然而,通過集成AspectJ,可以配置非Srping容器創建的對象。參看Using AspectJ to dependency-inject domain objects with Spring
下面的樣例展示了基于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="..." class="...">
<!-- bean的詳細配置 -->
</bean>
<bean id="..." class="...">
<!-- bean的詳細配置 -->
</bean>
<!-- 其他bean -->
</beans>
id
屬性是個字串,是bean的唯一標示符。class
屬性定義了bean的類型,要使用類的全限定類名(含有包路徑)。id
屬性的值,可以作為合作bean的引用標示符。上面未展示如何引用其他對象;詳情參看Dependencies
<h4 id='beans-factory-instantiation'>容器實例化</h4>
Spring IoC的實例化易如反掌。ApplicationContext
構造函數支持定位路徑,定位路徑也可以是多個,它是標識實際資源的字串,容器使用該標識加載配置元數據,支持多種資源,比如:本地文件系統、CLASSPATH等等。
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
在學習了Spring IoC容器之后,也許你想了解更多的Spring的資源,如前所述在第6章,資源使用URI語法定位輸入流,Spring提供了方便的機制讀取輸入流。在第6.7章“Application contexts and Resource paths”,專門講述5用 資源路徑構造應用上下文,資源路徑也是慣用手法。
接下來的樣例展示了配置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"/>
<!-- 有關屬性配置 -->
</bean>
<!--更多的Service bean -->
</beans>
下面的樣例展示了數據訪問對象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>
上述內容中service層由PetStoreServiceImpl
類、2個dao對象JpaAccountDao
和JpaItemDao
(基于JPA ORM標準)。屬性name
元素引用了JavaBean的屬性,ref元素引用了其他bean定義。這個引用表示實際對象之間的引用依賴。配置一個對象的依賴,詳情請參看Dependencies
<h5 id='beans-factory-xml-import'>引入基于xml的元數據</h5>
多個配置文件共同定義bean非常有用。通常,每個XML配置文件在你的架構中代表一個邏輯層或者一個模塊。
你可以使用應用上下文(applicationContext)的構造函數去加載所有xml中定義的bean。這個構造函數使用多個資源定位,就像前面中提到的?;蛘?,也可以用一個或者多個資源引用,即使用<import/>
標簽加載其他文件定義的bean。舉個栗子:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
上例中,從三個外部文件加載定義的bean:services.xml
,messageSource.xml
,themeSource.xml
。被引入的文件的路徑對于引入配置文件來說都是相對路徑,所以service.xml
必須在引入配置文件的相同文件路徑或者相同的類路徑中。而messageSource.xml
和themeSource.xml
必須在引入配置文件所在的文件夾下的resouce
文件夾下。正如你所看到的 /
開頭會被忽略掉,因為這些路徑是相對路徑,推薦不要使用/
開頭的格式。導入(imported)文件內容,包含根節點<beans/>
,配置中XML bean定義 必須經過Spring語法校驗通過。
使用"../"表示父目錄的相對路徑是可以的,但是真心不推薦這樣創建一個依賴應用外部文件的做法。尤其指出,使用"classpath:"資源類型的URLs(像這樣:"classpath:../services.xml"),也是不推薦的,因為運行時處理過程會選擇"最近的"根路徑然后引入他的父目錄配置文件。Classpath配置的改變,會導致應用選擇一個不同的、錯誤的目錄。
你可以使用全路徑限定資源定位取代相對路徑,比如:"file:C:/config/services.xml" 或者"classpath:/config/services.xml"。還有,你可以使用抽象路徑來解耦應用和配置文件。使用一個邏輯定位更可取 ,比如:通過"${..}"占位符,使用JVM運行時計算出的路徑。
<h4 id='beans-factory-client'>使用容器</h4>
ApplicationContext
是一個高級工廠的接口,能維護各種bean以及他們之間依賴的注冊。使用方法T getBean(String name, Class<T> requiredType)
,就能從定義的bean中獲取實例。
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中獲取實例。ApplicationContext
接口有幾種方法可以辦到,但是理想的做法是不要使用他們。實際上,應用中根本就不該使用getBean()
方法,這樣就不依賴Sprig API了。比如,Spring集成了很多web框架,為各種web框架類提供了依賴注入,比如web框架的Controller和JSF-managed beans
<h3 id='beans-definition'>Bean概述</h3>
Spring IoC容器管理一個或多個bean。這些bean根據提供給容器的配置元數據創建的,比如使用XML格式<bean/>
定義。
在容器內部,這些bean的定義用 BeanDefinition
對象表示,BeanDefinition
包含了下列元數據:
- 全路徑(含包路徑)類名:代表bean的實際實現類。
- Bean行為配置元素,它規定了bean在容器中行為(作用域,生命周期回調函數等等)
- 引用其他bean,就是為了bean能正常工作而所需的其他bean的引用。這些引用類也稱為合作類或者依賴類。
- 其他配置,為實例設置的其他屬性配置。比如說,管理連接池的bean的連接數,池大小的上限。
這些元數據將轉換成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
也包含創建特殊bean的信息,ApplicationContext
的實現也允許注冊由用戶創建而非IoC容器創建的對象。通過訪問ApplicationContext’s BeanFactory的方法getBeanFactory()
,該方法返回BeanFactory的實現DefaultListableBeanFactory
。DefaultListableBeanFactory
類支持這種注冊,通過registerSingleton(..)
和registerBeanDefinition(..)
方法實現。然而,典型的應用只用元數據定義的bean就可以單獨運行。
<h4 id='beans-beanname'>beans命名</h4>
bean有一個或者多個標示符。這些標示符必須是所在容器范圍內必唯一的。通常情況一下,一個bean僅有一個標示符,如果有需求需要多個,多出來的將被當做別名。
在XML格式配置元數據中,使用 id
或者 name
屬性來作為bean的標示符。id
屬性只能有1個。命名規范是字符數字混編(myBean,fooService,等等),但也支持特殊字符,可以包含。若想給bean起個別名,則可使用name
屬性來指定,可以是多個,用英文的逗號(,
)分隔、分號(;
)也行、空格也行。注意,在Spring3.1以前,id
屬性定義成了xsd:ID
類型,該類型強制為字符(譯者心里說:估計字母+特殊字符,不支持數字的意思,有待驗證,沒工夫驗證去了,翻譯進度太慢了。再說了,現在都用4了,你再說3.0怎么著怎么著,那不跟孔乙己似的跟別人吹噓茴香豆有四種寫法)。3.1版開始,它被定義為xsd:string
類型。注意,bean id
的唯一性約束依然被容器強制使用,盡管xml解析器不再支持了。譯者注:在spring3(含)以前,id是可以相同的,容器會替換相同id的bean,而在新版中,容器初始化過程中發現id相同拋出異常,停止實例化
id
和name
屬性不是bean所必須的。若未明確指定id
或者name
屬性,容器會給它生成一個唯一name屬性。當然了,如果你想通過bean的name
屬性引用,使用ref
元素方式,或者是類似于Service Locator模式方式檢索bean(譯者想:應該是指調用ApplicationContext.getBean()方法獲取bean,類似這種方式。Service Locator是一種設計模式,其實換個名字是不是更合適,DL(Dependency Lookup依賴查找)。雖然現在我也不明白,但是下面有專門的章節講解,翻到時候再詳細了解),就必須給bean指定 name
了。之所以支持無name bean特性,是為了使內部類自動裝配。
Bean命名規范
bean命名規范使用java命名規范中實例屬性名(也稱域,Field)規范。小寫字母開頭的駝峰式。像這樣
(不包括單引號)`accountManager`,`accountService`,`userDao`,
`loginController`,等等
規范的命名使配置易讀易理解。若使用Spring AOP,通過名字增強(譯注:大多數Spring AOP教材中
的 通知)一坨bean時,規范的命名將帶來極大的方便。
<h5 id='beans-beanname-alias'>bean定義之外設置別名</h5>
定義的bean內,可以給bean多個標識符,組合id
屬性值和任意數量的name
屬性值。這些標識符均可作為該bean的別名,對于有些場景中,別名機制非常有用,比如應用中組件對自身的引用。(譯注:一個類持有一個本類的實例作為屬性,看起來應該是這樣的,以下代碼為推測,可以執行)
Bean類
public class SomeBean {
//注意看這個屬性,就是本類
private SomeBean someBean;
public SomeBean(){}
public void setSomeBean(SomeBean someBean) {
this.someBean = someBean;
}
}
配置元數據
<?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的定義處指定所有別名有時候并不合適,然而,在其他配置文件中給bean設置別名卻更為恰當。此法通常應用在大型系統的場景中,配置文件分散在各個子系統中,每個子系統都有本系統的bean定義。XML格式配置元數據,提供<alias/>
元素,可以搞定此用法。
<alias name="fromName" alias="toName"/>
這種情況下,在同容器中有個叫fromName
的bean,或者叫其他的阿貓阿狗
之類的,再使用此別名定義之后,即可被當做toName
來引用。
舉個栗子,子系統A中的配置元數據也許引用了一個被命名為subsystemA-dataSource
的bean。子系統B也許引用了一個subsystemB-dataSource
。將這兩個子系統整合到主應用中,而主應用使用了一個myApp-dataSource
,為了使3個bean引用同一個對象,得在MyApp配置元數據中使用別名定義:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
現在,每個組件和主應用都能通過bean 名引用dataSource,而bean名都是唯一的保證不與其他定義沖突(實際上創建了一個命名空間),但他們引用的都是同一個bean。
Java-configuration
如果你使用了Java-configuration
,@Bean
注解也提供了別名,詳見Section 5.12.3, “Using the @Bean annotation”
<h4 id='beans-factory-class'>bean實例化</h4>
bean的定義,本質是如何創建一個或多個對象的配方。容器被請求時,會解析配置元數據中的bean定義并封裝,使用封裝配置創建(或者獲?。ο髮嵗?。
若使用XML格式配置元數據,得為將要實例化的對象指定類型(或者說是類),使用<bean/>
元素的class
屬性實現。class
屬性 映射到BeanDefinition
類實例的Class
屬性(域),這個class
屬性是<bean/>
元素必須的。(例外情況,參看“Instantiation using an instance factory method” 和 Section 5.7, “Bean definition inheritance”。使用Class
域屬性,通過以下兩種方式:
- 通常,通過指定bean的
class
屬性,容器使用反射調用其構造函數直接創建bean,有點像Java 編碼中使用new
操作符。 - 指定
class
實際類含有用于創建對象的靜態工廠方法,這是不常使用的場景,容器會調用類的靜態工廠方法創建bean。調用靜態工廠方法返回的對象類型也許是相同類型,也許完全是其他類。
<div class="sidebar">
<b>內部類命名</b> 若要定義靜態內部類,得將類名劈開。
舉例來說,現在在<span class="scode">com.example</span>包有個類<span class="scode">Foo</span>,該類有靜態內部類<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>字符,用它來分隔內部類名和外圍類名
</div>
<h4 id='beans-factory-class-ctor'>用構造函數實例化</h4>
若是使用構造函數方式創建bean,所有的常規類都可以使用Spring來創建、管理。也就是說,開發的類無需實現任何特殊接口或者使用某種特殊編碼風格。僅需指定bean的class
即可。對于特殊的bean管理,取決于你使用的IoC類型,也許需要一個默認的空構造。
Spring IoC容器幾乎能管理任何你需要管理的類,不局限于真正的JavaBeans
。大多數Spring的用戶心中,真正的JavaBean
是這樣的:僅有1個默認的無參構造函數、屬性、setter、getter。嗯,比如,現在需要使用一個廢棄連接池,它肯定不符合JavaBean
規范,Spring照樣能管理。
使用XML格式配置元數據 定義bean的class
,如下所示:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
如何為構造函數指定參數?如何在對象實力話之后設置其屬性?請參看Injecting Dependencies
<h5 id='beans-factory-class-static-factory-method'>使用靜態工廠方法實例化</h5>
定義使用使用靜態工廠方法創建的bean時,得指定工廠方法類的作為class
屬性值,并且還得指定工廠方法類中用于創建bean的方法名稱,作為factory-method
屬性值。工廠方法可以有參數,調用該方法即可返回對象實例,就像通過構造函數創建對象實例一樣。此種bean定義是為了兼容遺留系統中的靜態工廠
下面的bean定義,是使用工廠方法創建bean的方式。定義中,無需指定返回對象的類型(class),而是指定工廠方法類的class
。下例中,createInstance()
方法必須是一個static
靜態方法。
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
繼續
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'>使用實例工廠方法實例化</h5>
和靜態工廠方法類似的還有實例工廠方法,使用實例工廠方法的方式實例化,是調用容器中已存在的bean的一個非靜態方法來創建一個bean。用法是,1、class
屬性置空設置。 2、設置factory-bean
屬性,其值為當前容器(或者父容器)中bean的名字,該bean包含可供調用的創建對象的實例方法。3、設置factory-method
屬性,其值為工廠方法名。
<!-- 工廠類, 包含一個方法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;
}
}
工廠類可以有多個工廠方法:
<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是指一個Spring配置的bean,該bean能通過實例或者靜態工廠方法創建對象。對比之下,
FactoryBean
(注意大寫)是指Spring術語FactoryBean
。這段沒太理解,解釋factory bean和FactoryBean
。
<h3 id='beans-dependencies'>依賴</h3>
企業應用絕不會只有1個簡單對象(或者說Spring bean)。哪怕是最簡單的應用,也會包含許多對象協同工作。下一章節講述,如何為真正的應用定義大量的、獨立的bean,并讓這些對象一起合作。
<h4 id="beans-factory-collaborators">依賴注入</h4>
依賴注入(DI),是一個有對象定義依賴的手法,也就是,如何與其他對象合作,通過構造參數、工廠方法參數、或是在對象實例化之后設置對象屬性,實例化既可以構造也可以是使用工廠方法。容器在它創建bean之后注入依賴。這個過程從根本上發生了反轉,因此又名控制反轉(Ioc),因為Spring bean自己控制依賴類的實例化或者定位 ,Spring bean中就有依賴類的定義,容器使用依賴類構造器創建依賴類實例,使用Service Locator模式定位依賴類。
DI機制使代碼簡潔,對象提供它們的依賴,解耦更高效。對象無需自己查找依賴。同樣的,類更容易測試,尤其當依賴接口或者抽象類時,測試允許在單元測試中使用stub
或者mock
(模擬技術)實現。
DI有2種主要方式,構造注入 和 setter注入
構造注入,容器調用構造函數并傳參數,每個參數都是依賴。調用靜態工廠方法并傳參數方式構造bean和構造注入差不多,這里是指構造注入處理參數和靜態工廠方法處理參數像類似。下例中展示了一個只能使用構造注入的類。注意,此類無任何特別之處,并未依賴容器指定的接口、基類、注解,就是一個POJO
public class SimpleMovieLister {
// the SimpleMovieLister 依賴 a MovieFinder
private MovieFinder movieFinder;
//Spring容器能注入MovieFinder的構造函數
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// 實際如何使用MovieFinder的業務邏輯省略了
}
<h5 id='beans-factory-ctor-arguments-resolution'>構造函數參數解決方案</h5>
構造參數解決方案,會匹配所使用的參數類型。如果在bean的定義中,構造參數不存在歧義,那么,在bean定義中定義的構造參數的次序,在bean實例化時,就是提供給適合的構造參數的次序??催@個類:
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
}
}
不存在歧義,假設Bar
和Baz
類沒有集成關系,那么下面的配置是合法的,而且,不需要在<constructor-arg/>
元素里指定構造參數的明確的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>
若需要引用另一個bean,類型已知,構造函數就可以匹配參數類型(像上面的示例)。使用簡單類型時, 想<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
屬性明確指定構造參數的類型,容器就可以使用類型匹配。比如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
使用index
屬性明確指定構造參數的次序。比如
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>