1

<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組裝)。同時,為測試弄了專門的單獨章節,希望你能領悟這一點

<h2 id="beans">IoC容器</h2>
<h3 id="beans-introduction">springIOC容器和beans簡介</h3>

本章講解spring的控制反轉(IoC)的spring 框架實現 [1] 原理. IoC 又名 依賴注入 (DI). 它是一個由對象定義依賴的處理手法,也就是如何與其他對象協同工作, 可以通過以下途徑定義依賴:構造參數注入、工廠方法的參數注入、屬性注入(是指對象實例化后或者從工廠方法返回一個實例后設置其屬性)。容器創建bean時候, 注入 依賴。 這個控制倒轉了, 因此得名控制反轉 (IoC)。反轉了哪些控制,不再是由bean自己控制依賴類的實例化和定位, 而是使用了類似于 服務定位 模式的機制來控制。

org.springframework.beansorg.springframework.context 這兩個包是spring IOC容器的基礎包. BeanFactory 接口 提供了各種配置,用于管理任何對象. ApplicationContextBeanFactory的子接口. 它提供以下功能,使集成Spring’s AOP功能 更容易; 消息資源處理(用于國際化);事件發布;應用層指定上下文環境,像用于web應用的WebApplicationContext.

簡而言之, BeanFactory 提供了配置框架和基礎功能, ApplicationContext 增加了更多的企業應用用能. ApplicationContextBeanFactory的超集, 是本章的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

spring 工作原理示意圖

<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對象JpaAccountDaoJpaItemDao(基于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.xmlthemeSource.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的實現DefaultListableBeanFactoryDefaultListableBeanFactory類支持這種注冊,通過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相同拋出異常,停止實例化

idname屬性不是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) {
        // ...
    }

}

不存在歧義,假設BarBaz類沒有集成關系,那么下面的配置是合法的,而且,不需要在<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>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容

  • 參考W3C Spring教程 Spring致力于J2EE應用的各種解決方案,而不僅僅專注于某一層解決方案??梢哉fS...
    王偵閱讀 1,183評論 0 6
  • 開始使用Spring Framework 5.0和設計模式 介紹Spring框架 Spring簡化了應用程序開發,...
    一顆懶能閱讀 2,897評論 3 21
  • 2.1 我們的理念是:讓別人為你服務 IoC是隨著近年來輕量級容器(Lightweight Container)的...
    好好學習Sun閱讀 2,730評論 0 11
  • 今天又單獨把周重林的書拿出來看了一遍,看了三分之一。我發現我確實是書要看2遍才有印象,每次看的時候收獲都不一樣,然...
    茶女子張怡閱讀 156評論 0 0
  • 等許桑再見到蘇軒時已經是一個月以后了?;@球在籃球框邊懸搖轉動,蘇軒騰空的身子落地?!鞍““ 彪S著女生瘋狂的尖叫...
    憶夕笑雪閱讀 301評論 0 0