Spring官方文檔通讀-部分一

Spring 通讀官方文檔

這部分參考文檔涵蓋了Spring Framework絕對不可或缺的全部技術。php

其中最重要的是Spring Framework的控制反轉(IoC)容器。Spring框架的IoC容器的全面處理緊隨其後,全面覆蓋了Spring的面向方面編程(AOP)技術。Spring Framework有本身的AOP框架,它在概念上易於理解,而且成功地解決了Java企業編程中AOP要求的80%最佳點。html

還提供了Spring與AspectJ集成的覆蓋範圍(目前最豐富的 - 在功能方面 - 固然也是Java企業領域中最成熟的AOP實現)。java

1. IoC容器

先從Spring的控制反轉(IoC)容器開始。mysql

1.1 Spring IoC容器和Bean簡介

本章介紹了控制反轉(IoC)原理的Spring Framework實現。IoC也稱爲依賴注入(DI)。這是一個過程,經過這個過程,對象只能經過構造函數參數,工廠方法的參數或在構造或從工廠方法返回後在對象實例上設置的屬性來定義它們的依賴關係(即,它們使用的其餘對象)。 。而後容器在建立bean時注入這些依賴項。此過程基本上是bean自己的逆(所以名稱,控制反轉),經過使用類的直接構造或諸如服務定位器模式的機制來控制其依賴關係的實例化或位置。web

org.springframework.beansorg.springframework.context包是Spring框架的IoC容器的基礎。該 BeanFactory 接口提供了一種可以管理任何類型對象的高級配置機制。 ApplicationContext 是一個子界面BeanFactory。它補充說:spring

  • 更容易與Spring的AOP功能集成
  • 消息資源處理(用於國際化)
  • 活動出版
  • 特定WebApplicationContext 於應用程序層的上下文,例如在Web應用程序中使用的上下文。

簡而言之,它BeanFactory提供了配置框架和基本功能,並ApplicationContext添加了更多特定於企業的功能。它ApplicationContext是完整的超集,BeanFactory在本章中僅用於Spring的IoC容器的描述。有關使用BeanFactory而不是ApplicationContext,看到 的BeanFactory更多信息。sql

在Spring中,構成應用程序主幹並由Spring IoC容器管理的對象稱爲bean。bean是一個由Spring IoC容器實例化,組裝和管理的對象。不然,bean只是應用程序中許多對象之一。Bean及其之間的依賴關係反映在容器使用的配置元數據中。數據庫

1.2 集裝箱概覽

org.springframework.context.ApplicationContext接口表明Spring IoC容器,負責實例化,配置和組裝bean。容器經過讀取配置元數據獲取有關要實例化,配置和組裝的對象的指令。配置元數據以XML,Java註釋或Java代碼表示。它容許您表達組成應用程序的對象以及這些對象之間豐富的相互依賴性。apache

ApplicationContextSpring提供了幾種接口實現。在獨立應用程序中,一般會建立一個ClassPathXmlApplicationContext 或的實例 FileSystemXmlApplicationContext。雖然XML是定義配置元數據的傳統格式,但您能夠經過提供少許XML配置來聲明容器使用Java註釋或代碼做爲元數據格式,以聲明方式啓用對這些其餘元數據格式的支持。編程

在大多數應用程序方案中,不須要顯式用戶代碼來實例化Spring IoC容器的一個或多個實例。例如,在Web應用程序場景中,應用程序文件中的簡單八行(左右)樣板Web描述符XML web.xml一般就足夠了(請參閱Web應用程序的便捷ApplicationContext實例)。若是您使用 Spring Tool Suite(基於Eclipse的開發環境),只需點擊幾下鼠標或按鍵便可輕鬆建立此樣板配置。

下圖顯示了Spring如何工做的高級視圖。您的應用程序類與配置元數據相結合,以便在ApplicationContext建立和初始化以後,您擁有徹底配置且可執行的系統或應用程序。

Spring IoC容器-圖1

1.2.1 配置元數據

如上圖所示,Spring IoC容器使用一種配置元數據。此配置元數據表示您做爲應用程序開發人員如何告訴Spring容器在應用程序中實例化,配置和組裝對象。

傳統上,配置元數據以簡單直觀的XML格式提供,本章的大部份內容用於傳達Spring IoC容器的關鍵概念和功能。

基於XML的元數據不是惟一容許的配置元數據形式。Spring IoC容器自己徹底與實際編寫此配置元數據的格式分離。目前,許多開發人員爲其Spring應用程序選擇 基於Java的配置

有關在Spring容器中使用其餘形式的元數據的信息,請參閱:

  • 基於註釋的配置:Spring 2.5引入了對基於註釋的配置元數據的支持。(我這裏不細寫)
  • 基於Java的配置:從Spring 3.0開始,Spring JavaConfig項目提供的許多功能成爲核心Spring Framework的一部分。所以,您可使用Java而不是XML文件在應用程序類外部定義bean。要使用這些新功能,請參閱 @Configuration, @Bean, @Import,和@DependsOn註釋。

Spring配置包含容器必須管理的至少一個且一般不止一個bean定義。基於XML的配置元數據將這些bean配置爲頂級元素內的元素。Java配置一般@Bean在@Configuration類中使用註釋方法。

這些bean定義對應於構成應用程序的實際對象。一般,您定義服務層對象,數據訪問對象(DAO),表示對象(如Struts Action實例),基礎結構對象(如Hibernate SessionFactories,JMS Queues等)。一般,不會在容器中配置細粒度域對象,由於DAO和業務邏輯一般負責建立和加載域對象。可是,您可使用Spring與AspectJ的集成來配置在IoC容器控制以外建立的對象。請參閱使用AspectJ使用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 https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">   
        <!-- collaborators and configuration for this bean go here id屬性是一個標識單個bean定義的字符串 該class屬性定義bean的類型並使用徹底限定的類名 -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>
複製代碼

1.2.2 實例化容器

提供給ApplicationContext構造函數的位置路徑是資源字符串,它容許容器從各類外部資源(如本地文件系統,Java等)加載配置元數據CLASSPATH。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
複製代碼

在瞭解了Spring的IoC容器以後,您可能想要了解有關Spring Resource抽象的更多信息 (如參考資料中所述),它提供了一種從URI語法中定義的位置讀取InputStream的便捷機制。特別是, Resource路徑用於構建應用程序上下文,如應用程序上下文和資源路徑中所述。

如下示例顯示了服務層對象(services.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 https://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"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>
複製代碼

如下示例顯示了數據訪問對象daos.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 https://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>
複製代碼

在前面的示例中,服務層由的PetStoreServiceImpl類和類型的兩個數據訪問對象JpaAccountDao和JpaItemDao(基於JPA對象關係映射標準)。該property name元素是指JavaBean屬性的名稱,以及ref元素指的是另外一個bean定義的名稱。元素id和ref元素之間的這種聯繫表達了協做對象之間的依賴關係。有關配置對象的依賴關係的詳細信息,請參閱 依賴關係。

編寫基於XML的配置元數據 讓bean定義跨越多個XML文件會頗有用。一般,每一個單獨的XML配置文件都表明架構中的邏輯層或模塊。

您可使用應用程序上下文構造函數從全部這些XML片斷加載bean定義。此構造函數採用多個Resource位置,如上一節中所示 。或者,使用一個或多個元素來從另外一個或多個文件加載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>
複製代碼

在前面的例子中,外部豆定義是從三個文件加載: services.xml,messageSource.xml,和themeSource.xml。全部位置路徑都與執行導入的定義文件相關,所以services.xml必須與執行導入的文件位於相同的目錄或類路徑位置, messageSource.xml並且themeSource.xml必須位於resources導入文件位置下方的位置。如您所見,忽略前導斜槓。可是,鑑於這些路徑是相對的,最好不要使用斜槓。根據Spring Schema,正在導入的文件的內容(包括頂級元素)必須是有效的XML bean定義。

能夠(但不建議)使用相對「../」路徑引用父目錄中的文件。這樣作會對當前應用程序以外的文件建立依賴關係。特別是,不建議對classpath:URL(例如,classpath:../services.xml)使用此引用,其中運行時解析過程選擇「最近的」類路徑根,而後查看其父目錄。類路徑配置更改可能致使選擇不一樣的,不正確的目錄。

您始終可使用徹底限定的資源位置而不是相對路徑:例如,file:C:/config/services.xml或classpath:/config/services.xml。可是,請注意您將應用程序的配置與特定的絕對位置耦合。一般最好爲這些絕對位置保持間接 - 例如,經過在運行時針對JVM系統屬性解析的「$ {...}」佔位符。

命名空間自己提供了導入指令功能。Spring提供的一系列XML命名空間中提供了除普通bean定義以外的其餘配置功能 - 例如,context和util名稱空間。

Groovy Bean定義DSL 做爲外化配置元數據的另外一個示例,bean定義也能夠在Spring的Groovy Bean定義DSL中表示,如Grails框架中所知。一般,此類配置位於「.groovy」文件中,其結構以下例所示:

beans {
    dataSource(BasicDataSource) {
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
        dataSource = dataSource
    }
    myService(MyService) {
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}
複製代碼

1.2.3 使用容器

它ApplicationContext是高級工廠的接口,可以維護不一樣bean及其依賴項的註冊表。經過使用該方法T getBean(String name, Class requiredType),您能夠檢索Bean的實例。

將ApplicationContext讓你讀bean定義和訪問它們,以下例所示:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);

// use configured instance
List<String> userList = service.getUsernameList();
複製代碼

使用Groovy配置,bootstrapping看起來很是類似。它有一個不一樣的上下文實現類,它是Groovy-aware(但也理解XML bean定義)。如下示例顯示了Groovy配置:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
複製代碼

最靈活的變體GenericApplicationContext與讀者委託相結合 - 例如,XmlBeanDefinitionReader對於XML文件,如如下示例所示

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
複製代碼

您還可使用GroovyBeanDefinitionReaderfor Groovy文件,如如下示例所示:

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
複製代碼

您能夠ApplicationContext在不一樣的配置源中讀取和匹配此類讀取器委託,讀取bean定義。

而後,您可使用它getBean來檢索Bean的實例。該ApplicationContext 接口還有一些其餘方法可用於檢索bean,但理想狀況下,應用程序代碼永遠不該使用它們。實際上,您的應用程序代碼根本不該該調用該 getBean()方法,所以根本不依賴於Spring API。例如,Spring與Web框架的集成爲各類Web框架組件(如控制器和JSF託管bean)提供依賴注入,容許您經過元數據(例如自動裝配註釋)聲明對特定bean的依賴性。

1.3 Bean概述

Spring IoC容器管理一個或多個bean。這些bean是使用您提供給容器的配置元數據建立的(例如,以XML 定義的形式 )。

在容器自己內,這些bean定義表示爲BeanDefinition 對象,其中包含(以及其餘信息)如下元數據:

  • 包限定的類名:一般是正在定義的bean的實際實現類。
  • Bean行爲配置元素,說明bean在容器中的行爲方式(範圍,生命週期回調等)。
  • 引用bean執行其工做所需的其餘bean。這些引用也稱爲協做者或依賴項。
  • 要在新建立的對象中設置的其餘配置設置 - 例如,池的大小限制或在管理鏈接池的Bean中使用的鏈接數。

此元數據轉換爲構成每一個bean定義的一組屬性。下表描述了這些屬性:

屬性 解釋在......
實例化bean​​類
名稱 命名bean
範圍 Bean範圍
構造函數參數 依賴注入
屬性 依賴注入
自動裝配模式 自動化協做者
延遲初始化模式 懶惰初始化的豆類
初始化方法 初始化回調
破壞方法 毀滅回調

除了包含有關如何建立特定bean的信息的bean定義以外,這些ApplicationContext實現還容許註冊在容器外部(由用戶)建立的現有對象。這是經過方法訪問ApplicationContext的BeanFactory來完成的getBeanFactory(),該方法返回BeanFactory DefaultListableBeanFactory實現。DefaultListableBeanFactory 經過registerSingleton(..)registerBeanDefinition(..)方法支持此註冊。可是,典型的應用程序僅使用經過常規bean定義元數據定義的bean。

須要儘早註冊Bean元數據和手動提供的單例實例,以便容器在自動裝配和其餘內省步驟期間正確推理它們。雖然在某種程度上支持覆蓋現有元數據和現有單例實例,可是在運行時註冊新bean(與對工廠的實時訪問同時)並未獲得官方支持,而且可能致使併發訪問異常,bean容器中的狀態不一致,或者都。

1.3.1 命名bean

每一個bean都有一個或多個標識符。這些標識符在託管bean的容器中必須是惟一的。bean一般只有一個標識符。可是,若是它須要多個,則額外的能夠被視爲別名。

在基於XML的配置元數據中,您可使用id屬性,name屬性或二者來指定bean標識符。該id屬性容許您指定一個id。一般,這些名稱是字母數字('myBean','someService'等),但它們也能夠包含特殊字符。若是要爲bean引入其餘別名,還能夠在name 屬性中指定它們,用逗號(,),分號(;)或空格分隔。做爲歷史記錄,在Spring 3.1以前的版本中,該id屬性被定義爲一種xsd:ID類型,它約束了可能的字符。從3.1開始,它被定義爲一種xsd:string類型。請注意,id容器仍然強制實施bean 惟一性,但再也不是XML解析器。

您不須要提供bean name或idbean。若是您不提供 name或id顯式提供,則容器會爲該bean生成惟一的名稱。可是,若是要按名稱引用該bean,則經過使用ref元素或 Service Locator樣式查找,必須提供名稱。不提供名稱的動機與使用內部bean和自動裝配協做者有關。

Bean命名約定 慣例是在命名bean時使用標準Java約定做爲實例字段名稱。也就是說,bean名稱以小寫字母開頭,並從那裏開始駝峯。這樣的名字的例子包括accountManager, accountService,userDao,loginController,等等。

命名bean始終使您的配置更易於閱讀和理解。此外,若是您使用Spring AOP,那麼在將建議應用於與名稱相關的一組bean時,它會有很大幫助。

經過類路徑中的組件掃描,Spring按照前面描述的規則爲未命名的組件生成bean名稱:實質上,採用簡單的類名並將其初始字符轉換爲小寫。可是,在(不常見的)特殊狀況下,當有多個字符且第一個和第二個字符都是大寫字母時,原始外殼將被保留。這些規則與java.beans.Introspector.decapitalize(Spring在此處使用)定義的規則相同。

在Bean定義以外別名Bean 在bean定義自己中,您能夠爲bean提供多個名稱,方法是使用id屬性指定的最多一個名稱和屬性中的任意數量的其餘名稱name。這些名稱能夠是同一個bean的等效別名,對某些狀況頗有用,例如讓應用程序中的每一個組件經過使用特定於該組件自己的bean名稱來引用公共依賴項。

可是,指定實際定義bean的全部別名並不老是足夠的。有時須要爲其餘地方定義的bean引入別名。在大型系統中一般就是這種狀況,其中配置在每一個子系統之間分配,每一個子系統具備其本身的一組對象定義。在基於XML的配置元數據中,您可使用該元素來完成此任務。如下示例顯示瞭如何執行此操做:

<alias name="fromName" alias="toName"/>
複製代碼

Java的配置 若是使用Javaconfiguration,則@Bean可使用註釋來提供別名。有關詳細信息,請參閱使用@Bean註釋

1.3.2 實例化bean​​類

bean定義本質上是用於建立一個或多個對象的配方。容器在被詢問時查看命名bean的配方,並使用由該bean定義封裝的配置元數據來建立(或獲取)實際對象。

若是使用基於XML的配置元數據,則指定要在元素的class屬性中實例化的對象的類型(或類)。此 class屬性(在內部,是 實例Class上的屬性BeanDefinition)一般是必需的。(有關例外,請參閱 使用實例工廠方法和Bean定義繼承進行實例化。)您能夠經過如下Class兩種方式之一使用該屬性:

  • 一般,在容器自己經過反向調用其構造函數直接建立bean的狀況下指定要構造的bean類,稍微等同於使用new運算符的Java代碼。
  • 要指定包含static爲建立對象而調用的工廠方法的實際類,在不太常見的狀況下,容器static在類上調用 工廠方法來建立bean。從調用static工廠方法返回的對象類型能夠徹底是同一個類或另外一個類。

內部類名 若是要爲static嵌套類配置bean定義,則必須使用嵌套類的二進制名稱。

例如,若是您SomeThing在com.example包中調用了一個類,而且此類 SomeThing具備一個static被調用的嵌套類OtherThing,則class bean定義中的屬性值將爲com.example.SomeThing$OtherThing。

請注意,使用$名稱中的字符將嵌套類名與外部類名分開。

使用構造函數實例化 當您經過構造方法建立bean時,全部普通類均可以使用並與Spring兼容。也就是說,正在開發的類不須要實現任何特定接口或以特定方式編碼。簡單地指定bean類就足夠了。可是,根據您爲該特定bean使用的IoC類型,您可能須要一個默認(空)構造函數。

Spring IoC容器幾乎能夠管理您但願它管理的任何類。它不只限於管理真正的JavaBeans。大多數Spring用戶更喜歡實際的JavaBeans,只有一個默認(無參數)構造函數,而且在容器中的屬性以後建模了適當的setter和getter。您還能夠在容器中擁有更多異國情調的非bean樣式類。例如,若是您須要使用絕對不符合JavaBean規範的舊鏈接池,那麼Spring也能夠對其進行管理。

使用基於XML的配置元數據,您能夠按以下方式指定bean類:

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

複製代碼

有關爲構造函數提供參數的機制(若是須要)以及在構造對象後設置對象實例屬性的詳細信息,請參閱 注入依賴項。

使用靜態工廠方法實例化

定義使用靜態工廠方法建立的bean時,請使用該class 屬性指定包含static工廠方法的類,並使用factory-method名稱的屬性指定工廠方法自己的名稱。您應該可以調用此方法(使用可選參數,如稍後所述)並返回一個活動對象,隨後將其視爲經過構造函數建立的對象。這種bean定義的一個用途是static在遺留代碼中調用工廠。

如下bean定義指定經過調用工廠方法來建立bean。該定義未指定返回對象的類型(類),僅指定包含工廠方法的類。在此示例中,該createInstance() 方法必須是靜態方法。如下示例顯示如何指定工廠方法:

<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
複製代碼

如下示例顯示了一個可使用前面的bean定義的類: public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {}

public static ClientService createInstance() {
        return clientService;
    }
}
複製代碼

使用實例工廠方法實例化

與經過靜態工廠方法實例化相似,使用實例工廠方法進行實例化會從容器調用現有bean的非靜態方法來建立新bean。要使用此機制,請將該class屬性保留爲空,並在factory-bean屬性中指定當前(或父或祖先)容器中bean的名稱,該容器包含要調用以建立對象的實例方法。使用factory-method屬性設置工廠方法自己的名稱。如下示例顯示如何配置此類bean:

<!-- the factory bean, which contains a method called 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"/>
複製代碼

如下示例顯示了相應的Java類:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    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"/>
複製代碼

如下示例顯示了相應的Java類: public class DefaultServiceLocator {

private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}
複製代碼

這種方法代表工廠bean自己能夠經過依賴注入(DI)進行管理和配置。請參閱詳細信息中的依賴關係和配置。

在Spring文檔中,「工廠bean」是指在Spring容器中配置並經過實例或 靜態工廠方法建立對象的bean 。相比之下, FactoryBean(注意大寫)指的是特定於Spring的 FactoryBean。

1.4 依賴

典型的企業應用程序不包含單個對象(或Spring用法中的bean)。即便是最簡單的應用程序也有一些對象能夠協同工做,以呈現最終用戶所看到的連貫應用程序。下一節將介紹如何定義多個獨立的bean定義,以及對象協做實現目標的徹底實現的應用程序。

1.4.1 依賴注入

依賴注入(DI)是一個過程,經過這個過程,對象只能經過構造函數參數,工廠方法的參數或在構造對象實例後在對象實例上設置的屬性來定義它們的依賴關係(即,它們使用的其餘對象)。從工廠方法返回。而後容器在建立bean時注入這些依賴項。這個過程基本上是bean自己的反向(所以名稱,控制反轉),它經過使用類的直接構造或服務定位器模式來控制其依賴項的實例化或位置。

使用DI原則的代碼更清晰,當對象提供其依賴項時,解耦更有效。該對象不查找其依賴項,也不知道依賴項的位置或類。所以,您的類變得更容易測試,特別是當依賴關係在接口或抽象基類上時,這容許在單元測試中使用存根或模擬實現。

DI存在兩個主要變體:基於構造函數的依賴注入和基於Setter的依賴注入。

基於構造函數的依賴注入

基於構造函數的DI由容器調用具備多個參數的構造函數來完成,每一個參數表示一個依賴項。調用static具備特定參數的工廠方法來構造bean幾乎是等效的,本討論一樣處理構造函數和static工廠方法的參數。如下示例顯示了一個只能經過構造函數注入進行依賴注入的類: public class SimpleMovieLister {

// the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}
複製代碼

請注意,這個類沒有什麼特別之處。它是一個POJO,它不依賴於容器特定的接口,基類或註釋。

####構造函數參數解析 經過使用參數的類型進行構造函數參數解析匹配。若是bean定義的構造函數參數中不存在潛在的歧義,那麼在bean定義中定義構造函數參數的順序是在實例化bean時將這些參數提供給適當的構造函數的順序。考慮如下課程:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}
複製代碼

假設ThingTwo而且ThingThree類與繼承無關,則不存在潛在的歧義。所以,如下配置工做正常,您不須要在 元素中顯式指定構造函數參數索引或類型。

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>
複製代碼

當引用另外一個bean時,類型是已知的,而且能夠發生匹配(與前面的示例同樣)。當使用簡單類型時,例如 true,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 id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>
複製代碼

請記住,爲了使這項工做開箱即用,必須在啓用調試標誌的狀況下編譯代碼,以便Spring能夠從構造函數中查找參數名稱。若是您不能或不想使用debug標誌編譯代碼,則可使用 @ConstructorProperties JDK批註顯式命名構造函數參數。而後,示例類必須以下所示:

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
複製代碼

基於Setter的依賴注入 在調用無參數構造函數或無參數static工廠方法來實例化bean以後,基於setter的DI由bean上的容器調用setter方法完成。

如下示例顯示了一個只能經過使用純setter注入進行依賴注入的類。這個類是傳統的Java。它是一個POJO,它不依賴於容器特定的接口,基類或註釋。

public class SimpleMovieLister {

// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;

// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
}

// business logic that actually uses the injected MovieFinder is omitted...
複製代碼

} 它ApplicationContext支持它管理的bean的基於構造函數和基於setter的DI。在經過構造函數方法注入了一些依賴項以後,它還支持基於setter的DI。您能夠以a的形式配置依賴項,並將BeanDefinition其與PropertyEditor實例結合使用,以將屬性從一種格式轉換爲另外一種格式。然而,大多數Spring用戶不直接與這些類(即,編程),而是用XML bean 定義註釋的組件(也就是帶註釋類@Component, @Controller等),或者@Bean方法在基於Java的@Configuration類。而後,這些源在內部轉換爲實例BeanDefinition並用於加載整個Spring IoC容器實例。

####基於構造函數或基於setter的DI? 因爲您能夠混合基於構造函數和基於setter的DI,所以將構造函數用於強制依賴項和setter方法或可選依賴項的配置方法是一個很好的經驗法則。請注意, 在setter方法上使用@Required註釋可用於使屬性成爲必需的依賴項; 可是,最好使用編程驗證參數的構造函數注入。

Spring團隊一般提倡構造函數注入,由於它容許您將應用程序組件實現爲不可變對象,並確保所需的依賴項不是null。此外,構造函數注入的組件始終以徹底初始化的狀態返回到客戶端(調用)代碼。做爲旁註,大量的構造函數參數是一個糟糕的代碼氣味,暗示該類可能有太多的責任,應該重構以更好地解決關注點的正確分離。

Setter注入應主要僅用於可在類中指定合理默認值的可選依賴項。不然,必須在代碼使用依賴項的任何位置執行非空檢查。setter注入的一個好處是setter方法使該類的對象能夠在之後從新配置或從新注入。所以,經過JMX MBean進行管理是二次注入的一個引人注目的用例。

使用對特定類最有意義的DI樣式。有時,在處理您沒有源的第三方類時,會選擇您。例如,若是第三方類沒有公開任何setter方法,那麼構造函數注入多是惟一可用的DI形式。

####依賴性解決過程 容器執行bean依賴性解析,以下所示:

使用ApplicationContext描述全部bean的配置元數據建立和初始化。配置元數據能夠由XML,Java代碼或註釋指定。

對於每一個bean,它的依賴關係以屬性,構造函數參數或static-factory方法的參數的形式表示(若是使用它而不是普通的構造函數)。實際建立bean時,會將這些依賴項提供給bean。

每一個屬性或構造函數參數都是要設置的值的實際定義,或者是對容器中另外一個bean的引用。

做爲值的每一個屬性或構造函數參數都從其指定的格式轉換爲該屬性或構造函數參數的實際類型。默認狀況下,Spring可以轉換成字符串格式提供給全部內置類型的值,例如int, long,String,boolean,等等。

Spring容器在建立容器時驗證每一個bean的配置。可是,在實際建立bean以前,不會設置bean屬性自己。建立容器時會建立單例做用域並設置爲預先實例化(默認值)的Bean。範圍在Bean範圍中定義。不然,僅在請求時才建立bean。建立bean可能會致使建立bean的圖形,由於bean的依賴關係及其依賴關係(依此類推)被建立和分配。請注意,這些依賴項之間的解決方案不匹配可能會顯示較晚 - 也就是說,首次建立受影響的bean時。

####循環依賴 若是您主要使用構造函數注入,則能夠建立沒法解析的循環依賴關係場景。

例如:類A經過構造函數注入須要類B的實例,而類B經過構造函數注入須要類A的實例。若是將A類和B類的bean配置爲相互注入,則Spring IoC容器會在運行時檢測到此循環引用,並拋出a BeanCurrentlyInCreationException。

一種可能的解決方案是編輯由setter而不是構造函數配置的某些類的源代碼。或者,避免構造函數注入並僅使用setter注入。換句話說,儘管不推薦使用,但您可使用setter注入配置循環依賴項。

與典型狀況(沒有循環依賴)不一樣,bean A和bean B之間的循環依賴強制其中一個bean在徹底初始化以前被注入另外一個bean(一個經典的雞與蛋的場景)。

你一般能夠相信Spring作正確的事。它在容器加載時檢測配置問題,例如對不存在的bean和循環依賴關係的引用。當實際建立bean時,Spring會盡量晚地設置屬性並解析依賴關係。這意味着,若是在建立該對象或其中一個依賴項時出現問題,則在請求對象時,正確加載的Spring容器能夠在之後生成異常 - 例如,bean因缺失或無效而拋出異常屬性。這可能會延遲一些配置問題的可見性ApplicationContext默認狀況下實現預實例化單例bean。以實際須要以前建立這些bean的一些前期時間和內存爲代價,您ApplicationContext會在建立時發現配置問題,而不是更晚。您仍然能夠覆蓋此默認行爲,以便單例bean能夠懶惰地初始化,而不是預先實例化。

若是不存在循環依賴關係,當一個或多個協做bean被注入依賴bean時,每一個協做bean在被注入依賴bean以前徹底配置。這意味着,若是bean A依賴於bean B,則Spring IoC容器在調用bean A上的setter方法以前徹底配置bean B.換句話說,bean被實例化(若是它不是預先實例化的單例),設置其依賴項,並調用相關的生命週期方法(如配置的init方法 或InitializingBean回調方法)。

依賴注入的示例 如下示例將基於XML的配置元數據用於基於setter的DI。Spring XML配置文件的一小部分指定了一些bean定義,以下所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
複製代碼

如下示例顯示了相應的ExampleBean類:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public void setBeanOne(AnotherBean beanOne) {
        this.beanOne = beanOne;
    }

    public void setBeanTwo(YetAnotherBean beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setIntegerProperty(int i) {
        this.i = i;
    }
}
複製代碼

在前面的示例中,聲明setter與XML文件中指定的屬性匹配。如下示例使用基於構造函數的DI:

<bean id="exampleBean" class="examples.ExampleBean">
    <!-- constructor injection using the nested ref element -->
    <constructor-arg>
        <ref bean="anotherExampleBean"/>
    </constructor-arg>

    <!-- constructor injection using the neater ref attribute -->
    <constructor-arg ref="yetAnotherBean"/>

    <constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
複製代碼

如下示例顯示了相應的ExampleBean類:

public class ExampleBean {

    private AnotherBean beanOne;

    private YetAnotherBean beanTwo;

    private int i;

    public ExampleBean(
        AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
        this.beanOne = anotherBean;
        this.beanTwo = yetAnotherBean;
        this.i = i;
    }
}
複製代碼

bean定義中指定的構造函數參數用做構造函數的參數ExampleBean。

如今考慮這個示例的變體,其中,不使用構造函數,而是告訴Spring調用static工廠方法來返回對象的實例:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>
    <constructor-arg ref="yetAnotherBean"/>
    <constructor-arg value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
複製代碼

如下示例顯示了相應的ExampleBean類:

public class ExampleBean {

// a private constructor
private ExampleBean(...) {
    ...
}

// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
    AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {

    ExampleBean eb = new ExampleBean (...);
    // some other operations...
    return eb;
}
複製代碼

} static工廠方法的參數由元素提供,與實際使用的構造函數徹底相同。工廠方法返回的類的類型沒必要與包含static工廠方法的類相同(儘管在本例中,它是)。實例(非靜態)工廠方法能夠以基本相同的方式使用(除了使用factory-bean屬性而不是class屬性),所以咱們不在此討論這些細節。

1.4.2 詳細信息的依賴關係和配置

如上一節所述,您能夠將bean屬性和構造函數參數定義爲對其餘託管bean(協做者)的引用,也能夠將其定義爲內聯定義的值。Spring的基於XML的配置元數據爲此目的支持其元素和元素中的子元素類型。

直值(基元,字符串等) 在value所述的屬性元素指定屬性或構造器參數的人類可讀的字符串表示。Spring的 轉換服務用於將這些值從a轉換String爲屬性或參數的實際類型。如下示例顯示了要設置的各類值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>
複製代碼

如下示例使用p命名空間進行更簡潔的XML配置:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/mydb"
        p:username="root"
        p:password="masterkaoli"/>

</beans>
複製代碼

前面的XML更簡潔。可是,除非您在建立bean定義時使用支持自動屬性完成的IDE(例如IntelliJ IDEA或Spring Tool Suite),不然會在運行時而不是設計時發現拼寫錯誤。強烈建議使用此類IDE幫助。

您還能夠配置java.util.Properties實例,以下所示:

<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
            jdbc.driver.className=com.mysql.jdbc.Driver
            jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>
複製代碼

Spring容器經過使用JavaBeans 機制將元素內的文本轉換爲 java.util.Properties實例PropertyEditor。這是一個很好的快捷方式,也是Spring團隊支持在value屬性樣式上使用嵌套元素的少數幾個地方之一。

該idref元素 該idref元素只是一種防錯方法,能夠將id容器中另外一個bean 的(字符串值 - 而不是引用)傳遞給or或 element。如下示例顯示瞭如何使用它:

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>
複製代碼

前面的bean定義代碼段與如下代碼段徹底等效(在運行時):

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>
複製代碼

第一種形式優於第二種形式,由於使用idref標記容許容器在部署時驗證引用的命名bean實際存在。在第二個變體中,不對傳遞給bean 的targetName屬性的值執行驗證client。只有在client實際實例化bean 時纔會發現錯別字(極可能是致命的結果)。若是client bean是原型 bean,則只能在部署容器後很長時間才能發現此錯誤和產生的異常。

4.0 beans XSD再也不支持local該idref元素 的屬性,由於它再也不提供常規bean引用的值。升級到4.0架構時,將現有idref local引用更改idref bean爲。

其中一個共同的地方(至少在早期比Spring 2.0版本)元素帶來的值在配置AOP攔截在 ProxyFactoryBeanbean定義。指定攔截器名稱時使用元素可防止拼寫錯誤的攔截器ID。

####參考其餘bean類(合做者) 所述ref元件是內部的最終元件或 定義元素。在這裏,您將bean的指定屬性的值設置爲對容器管理的另外一個bean(協做者)的引用。引用的bean是要設置其屬性的bean的依賴項,而且在設置該屬性以前根據須要對其進行初始化。(若是協做者是單例bean,它可能已經被容器初始化。)全部引用最終都是對另外一個對象的引用。劃定範圍和有效性取決因而否經過指定其餘對象的ID或名稱bean,local,或parent屬性。

經過標記的bean屬性指定目標bean 是最經常使用的形式,並容許建立對同一容器或父容器中的任何bean的引用,而無論它是否在同一XML文件中。bean屬性的值 能夠id與目標bean 的屬性相同,或者與目標bean的name屬性中的值之一相同。如下示例顯示如何使用ref元素:

<ref bean="someBean"/>
複製代碼

經過該parent屬性指定目標bean 會建立對當前容器的父容器中的bean的引用。parent 屬性的值能夠id與目標bean 的屬性或目標bean的name屬性中的值之一相同。目標bean必須位於當前bean的父容器中。您應該使用此bean引用變體,主要是當您有容器層次結構而且但願將現有bean包裝在父容器中時,該容器具備與父bean同名的代理。如下一對列表顯示瞭如何使用該parent屬性:

<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target">
        <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
    </property>
    <!-- insert other configuration and dependencies as required here -->
</bean>
複製代碼

4.0 beans XSD再也不支持local該ref元素 的屬性,由於它再也不提供常規bean引用的值。升級到4.0架構時,將現有ref local引用更改ref bean爲。

####內bean 內部的元件或元件限定內部豆,以下面的示例所示:

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>
複製代碼

內部bean定義不須要定義的ID或名稱。若是指定,則容器不使用此類值做爲標識符。容器還會scope在建立時忽略標誌,由於內部bean始終是匿名的,而且始終使用外部bean建立。不可能獨立訪問內部bean或將它們注入協做bean而不是封閉bean。

內部bean定義不須要定義的ID或名稱。若是指定,則容器不使用此類值做爲標識符。容器還會scope在建立時忽略標誌,由於內部bean始終是匿名的,而且始終使用外部bean建立。不可能獨立訪問內部bean或將它們注入協做bean而不是封閉bean。

做爲一個極端狀況,能夠從自定義範圍接收銷燬回調 - 例如,對於包含在單例bean中的請求範圍內部bean。內部bean實例的建立與其包含bean相關聯,可是銷燬回調容許它參與請求範圍的生命週期。這不是常見的狀況。內部bean一般只是共享其包含bean的範圍。

####集合

集合的,,,和元件設置Java的屬性和參數Collection類型List,Set,Map,和Properties,分別。如下示例顯示瞭如何使用它們:

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>
複製代碼

映射鍵或值的值或設置值也能夠是如下任何元素:

bean | ref | idref | list | set | map | props | value | null
複製代碼

#####集合合併 Spring容器還支持合併集合。應用程序開發人員能夠定義父,,或元素,並有孩子,,或元素繼承和父集合覆蓋值。也就是說,子集合的值是合併父集合和子集合的元素的結果,子集合的元素覆蓋父集合中指定的值。

關於合併的這一部分討論了父子bean機制。不熟悉父母和子bean定義的讀者可能但願在繼續以前閱讀 相關部分。

如下示例演示了集合合併:

<beans>
    <bean id="parent" abstract="true" class="example.ComplexObject">
        <property name="adminEmails">
            <props>
                <prop key="administrator">administrator@example.com</prop>
                <prop key="support">support@example.com</prop>
            </props>
        </property>
    </bean>
    <bean id="child" parent="parent">
        <property name="adminEmails">
            <!-- the merge is specified on the child collection definition -->
            <props merge="true">
                <prop key="sales">sales@example.com</prop>
                <prop key="support">support@example.co.uk</prop>
            </props>
        </property>
    </bean>
<beans>
複製代碼

注意使用的merge=true上屬性的元素 adminEmails的財產childbean定義。當child容器解析並實例化bean時,生成的實例有一個adminEmails Properties集合,其中包含將子集合adminEmails與父adminEmails集合合併的結果 。如下清單顯示告終果:

administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk 孩子Properties集合的值設置繼承父全部屬性元素,和孩子的爲值support值將覆蓋父集合的價值。

這一合併行爲一樣適用於,和 集合類型。在元素的特定狀況下,保持與List集合類型(即,ordered 值集合的概念)相關聯的語義。父級的值位於全部子級列表的值以前。在的狀況下Map,Set和Properties集合類型,沒有順序存在。所以,沒有排序的語義在背後的關聯的集合類型的效果Map,Set以及Properties該容器內部使用實現類型。

收集合並的侷限性

您沒法合併不一樣的集合類型(例如a Map和a List)。若是您嘗試這樣作,Exception則會引起相應的操做。merge必須在較低的繼承子定義上指定該屬性。merge在父集合定義上指定屬性是多餘的,而且不會致使所需的合併。

強烈的收藏品

經過在Java 5中引入泛型類型,您可使用強類型集合。也就是說,能夠聲明一種Collection類型,使得它只能包含(例如)String元素。若是使用Spring將強類型依賴注入Collection到bean中,則能夠利用Spring的類型轉換支持,以便強類型Collection 實例的元素在添加到以前轉換爲適當的類型Collection。如下Java類和bean定義顯示瞭如何執行此操做:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="something" class="x.y.SomeClass">
        <property name="accounts">
            <map>
                <entry key="one" value="9.99"/>
                <entry key="two" value="2.75"/>
                <entry key="six" value="3.99"/>
            </map>
        </property>
    </bean>
</beans>
複製代碼

當爲注入準備bean 的accounts屬性時,經過反射可得到something關於強類型的元素類型的泛型信息Map<String, Float>。所以,Spring的類型轉換基礎結構將各類值元素識別爲類型Float,並將字符串值(9.99, 2.75,和 3.99)轉換爲實際Float類型。

空字符串值和空字符串值 Spring將屬性等的空參數視爲空Strings。如下基於XML的配置元數據片斷將email屬性設置爲空 String值(「」)。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
複製代碼

上面的示例等效於如下Java代碼:

exampleBean.setEmail("");
複製代碼

該元素處理null值。如下清單顯示了一個示例:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>
複製代碼

上述配置等同於如下Java代碼:

exampleBean.setEmail(null);
複製代碼

####帶有p命名空間的XML快捷方式 p-namespace容許您使用bean元素的屬性(而不是嵌套 元素)來描述屬性值協做bean,或二者。

Spring支持具備命名空間的可擴展配置格式,這些命名空間基於XML Schema定義。beans本章中討論的配置格式在XML Schema文檔中定義。可是,p-namespace未在XSD文件中定義,僅存在於Spring的核心中。

如下示例顯示了兩個XML片斷(第一個使用標準XML格式,第二個使用p命名空間)解析爲相同的結果:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="classic" class="com.example.ExampleBean">
        <property name="email" value="someone@somewhere.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/>
</beans>
複製代碼

該示例顯示email了bean定義中調用的p命名空間中的屬性。這告訴Spring包含一個屬性聲明。如前所述,p命名空間沒有架構定義,所以您能夠將屬性的名稱設置爲屬性名稱。

下一個示例包括另外兩個bean定義,它們都引用了另外一個bean:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="john-classic" class="com.example.Person">
        <property name="name" value="John Doe"/>
        <property name="spouse" ref="jane"/>
    </bean>

    <bean name="john-modern"
        class="com.example.Person"
        p:name="John Doe"
        p:spouse-ref="jane"/>

    <bean name="jane" class="com.example.Person">
        <property name="name" value="Jane Doe"/>
    </bean>
</beans>
複製代碼

此示例不只包含使用p命名空間的屬性值,還使用特殊格式來聲明屬性引用。第一個bean定義用於建立從bean john到bean 的引用 jane,而第二個bean定義p:spouse-ref="jane"用做屬性來執行徹底相同的操做。在這種狀況下,spouse是屬性名稱,而該-ref部分表示這不是直接值,而是對另外一個bean的引用。

p命名空間不如標準XML格式靈活。例如,聲明屬性引用的格式與最終的屬性衝突Ref,而標準XML格式則否則。咱們建議您仔細選擇您的方法並將其傳達給您的團隊成員,以免生成同時使用全部三種方法的XML文檔。

####帶有c命名空間的XML快捷方式 與帶有p-namespace的XML Shortcut相似,Spring 3.1中引入的c-namespace容許使用內聯屬性來配置構造函數參數,而不是嵌套constructor-arg元素。

如下示例使用c:命名空間執行與 基於構造函數的依賴注入相同的操做:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>

    <!-- traditional declaration with optional argument names -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>

    <!-- c-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>
複製代碼

該c:命名空間使用相同的約定做爲p:一個(尾部-ref的bean引用),供他們的名字設置構造函數的參數。相似地,它須要在XML文件中聲明,即便它沒有在XSD模式中定義(它存在於Spring核心內部)。

對於構造函數參數名稱不可用的罕見狀況(一般在沒有調試信息的狀況下編譯字節碼),您可使用回退到參數索引,以下所示:

<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
    c:_2="something@somewhere.com"/>
複製代碼

因爲XML語法,索引表示法要求存在前導_,由於XML屬性名稱不能以數字開頭(即便某些IDE容許)。對於元素也可使用相應的索引符號,但不經常使用,由於一般的聲明順序一般就足夠了。

實際上,構造函數解析 機制在匹配參數方面很是有效,所以除非您確實須要,不然咱們建議在整個配置中使用名稱表示法。

複合屬性名稱 設置bean屬性時,可使用複合或嵌套屬性名稱,只要除最終屬性名稱以外的路徑的全部組件都不是null。考慮如下bean定義:

<bean id="something" class="things.ThingOne">
    <property name="fred.bob.sammy" value="123" />
</bean>
複製代碼

該somethingbean具備一個fred屬性,該屬性具備屬性,該bob屬性具備sammy 屬性,而且最終sammy屬性的值設置爲123。爲了使其工做,在構造bean以後,fred屬性something和bob屬性fred不得爲null。不然,NullPointerException拋出一個。

1.4.3運用depends-on

若是bean是另外一個bean的依賴項,那一般意味着將一個bean設置爲另外一個bean的屬性。一般,您可使用基於XML的配置元數據中的 元素來完成此操做。可是,有時bean之間的依賴關係不那麼直接。例如,須要觸發類中的靜態初始化程序,例如數據庫驅動程序註冊。depends-on在初始化使用此元素的bean以前,該屬性能夠顯式強制初始化一個或多個bean。如下示例使用該depends-on屬性表示對單個bean的依賴關係:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
複製代碼

要表示對多個bean的依賴關係,請提供bean名稱列表做爲depends-on屬性的值(逗號,空格和分號是有效的分隔符):

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>

<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
複製代碼

該depends-on屬性既能夠指定初始化時間依賴性,也能夠指定單獨的 bean,相應的銷燬時間依賴性。depends-on在給定的bean自己被銷燬以前,首先銷燬定義與給定bean 的關係的從屬bean 。這樣,depends-on也能夠控制關​​機順序。

###1.4.4 懶惰初始化的bean類 默認狀況下,ApplicationContext實現會急切地建立和配置全部 單例 bean,做爲初始化過程的一部分。一般,這種預先實例化是可取的,由於配置或周圍環境中的錯誤是當即發現的,而不是幾小時甚至幾天後。當不但願出現這種狀況時,能夠經過將bean定義標記爲延遲初始化來阻止單例bean的預實例化。延遲初始化的bean告訴IoC容器在第一次請求時建立bean實例,而不是在啓動時。

在XML中,此行爲由 元素lazy-init上的屬性控制,如如下示例所示:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
 <bean name="not.lazy" class="com.something.AnotherBean"/>
複製代碼

當前面的配置被a使用時ApplicationContext,lazybean在ApplicationContext啓動時不會急切地預先實例化,而not.lazybean被急切地預先實例化。

可是,當延遲初始化的bean是未進行延遲初始化的單例bean的依賴項時,ApplicationContext會在啓動時建立延遲初始化的bean,由於它必須知足單例的依賴關係。惰性初始化的bean被注入到其餘地方的單獨的bean中,而這個bean並非惰性初始化的。

您還能夠經過使用元素default-lazy-init上的屬性來控制容器級別的延遲初始化,

<beans/>如下示例顯示:
 <beans default-lazy-init="true">
 <!-- no beans will be pre-instantiated... -->
 </beans>
複製代碼

1.4.5 自動化協做者

Spring容器能夠自動鏈接協做bean之間的關係。您可讓Spring經過檢查bean的內容自動爲您的bean解析協做者(其餘bean)ApplicationContext。自動裝配具備如下優勢:

  • 自動裝配能夠顯着減小指定屬性或構造函數參數的須要。(在本章其餘地方討論的其餘機制,如bean模板 ,在這方面也頗有價值。)
  • 自動裝配能夠隨着對象的發展更新配置。例如,若是須要向類添加依賴項,則能夠自動知足該依賴項,而無需修改配置。所以,自動裝配在開發期間尤爲有用,而不會在代碼庫變得更穩定時否認切換到顯式佈線的選項。

使用基於XML的配置元數據(請參閱依賴注入)時,可使用元素的autowire屬性爲 bean定義指定autowire模式。自動裝配功能有四種模式。您指定每一個bean的自動裝配,所以能夠選擇要自動裝配的那些。下表描述了四種自動裝配模式:

自動裝配模式

模式 說明

no

(默認)無自動裝配。Bean引用必須由ref元素定義。不建議對較大的部署更改默認設置,由於明確指定協做者能夠提供更好的控制和清晰度。在某種程度上,它記錄了系統的結構。

byName

按屬性名稱自動裝配。Spring查找與須要自動裝配的屬性同名的bean。例如,若是bean定義按名稱設置爲autowire而且它包含一個master屬性(即,它有一個 setMaster(..)方法),則Spring會查找名爲bean的定義master並使用它來設置屬性。

byType

若是容器中只存在一個屬性類型的bean,則容許屬性自動裝配。若是存在多個,則拋出致命異常,這表示您可能不會byType對該bean 使用自動裝配。若是沒有匹配的bean,則不會發生任何事情(該屬性未設置)。

constructor

相似byType但適用於構造函數參數。若是容器中沒有構造函數參數類型的一個bean,則會引起致命錯誤。

使用byType或constructor自動裝配模式,您能夠鏈接陣列和鍵入的集合。在這種狀況下,提供容器內與預期類型匹配的全部autowire候選者以知足依賴性。Map若是預期的鍵類型是,則能夠自動裝配強類型實例String。自動裝配Map 實例的值由與預期類型匹配的全部bean實例組成, Map實例的鍵包含相應的bean名稱。

自動裝配的侷限和缺點

當在整個項目中一致地使用自動裝配時,自動裝配效果最佳。若是通常不使用自動裝配,那麼開發人員使用它來鏈接一個或兩個bean定義可能會讓人感到困惑。

考慮自動裝配的侷限和缺點:
  • 顯式依賴項property和constructor-arg設置始終覆蓋自動裝配。您不能自動裝配簡單屬性,例如基元 Strings,和Classes(以及此類簡單屬性的數組)。這種限制是按設計的。
  • 自動裝配不如顯式佈線精確。雖然如前面的表中所述,但Spring會謹慎地避免在可能產生意外結果的模糊性的狀況下進行猜想。您再也不明確記錄Spring管理對象之間的關係。
  • 可能沒法爲可能從Spring容器生成文檔的工具提供接線信息。

容器中的多個bean定義能夠匹配setter方法或構造函數參數指定的類型以進行自動裝配。對於數組,集合或 Map實例,這不必定是個問題。可是,對於指望單個值的依賴關係,這種模糊性不是任意解決的。若是沒有可用的惟一bean定義,則拋出異常。

在後一種狀況下,您有幾種選擇:

  • 放棄自動裝配,支持顯式佈線。
  • 經過將其autowire-candidate屬性設置爲bean,能夠避免對bean定義進行自動裝配false,以下一節所述。
  • 經過將primary其元素的屬性設置爲,將單個bean定義指定爲主要候選者 true。
  • 實現基於註釋的配置可用的更細粒度的控件,如基於註釋的容器配置中所述

從自動裝配中排除Bean 在每一個bean的基礎上,您能夠從自動裝配中排除bean。在Spring的XML格式中,將元素的autowire-candidate屬性設置爲false。容器使特定的bean定義對自動裝配基礎結構不可用(包括註釋樣式配置等@Autowired)。

該autowire-candidate屬性旨在僅影響基於類型的自動裝配。它不會影響名稱的顯式引用,即便指定的bean未標記爲autowire候選,也會解析它。所以,若是名稱匹配,則按名稱自動裝配會注入bean。

您還能夠根據與bean名稱的模式匹配來限制autowire候選者。頂級元素在其default-autowire-candidates屬性中接受一個或多個模式 。例如,要將autowire候選狀態限制爲名稱以其結尾的任何bean Repository,請提供值*Repository。要提供多個模式,請在逗號分隔的列表中定義它們。bean定義的屬性的顯式值 true或優先級始終優先。對於此類bean,模式匹配規則不適用。falseautowire-candidate

這些技術對於您永遠不但願經過自動裝配注入其餘bean的bean很是有用。這並不意味着排除的bean自己不能使用自動裝配進行配置。相反,bean自己不是自動裝配其餘bean的候選者。

1.4.6 方法注入

在大多數應用程序場景中,容器中的大多數bean都是 單例。當單例bean須要與另外一個單例bean協做或非單例bean須要與另外一個非單例bean協做時,一般經過將一個bean定義爲另外一個bean的屬性來處理依賴關係。當bean生命週期不一樣時會出現問題。假設單例bean A須要使用非單例(原型)bean B,多是在A上的每一個方法調用上。容器只建立一次單例bean A,所以只有一次機會來設置屬性。每次須要時,容器都不能爲bean A提供bean B的新實例。

解決方案是放棄一些控制反轉。你能夠作一個豆意識到容器經過實現ApplicationContextAware接口,並經過製做getBean("B")到容器調用請求(典型新)bean B實例的實例每次豆A須要它。如下示例顯示了此方法:

// 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) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
複製代碼

前面的內容是不可取的,由於業務代碼知道並耦合到Spring Framework。方法注入是Spring IoC容器的一個高級功能,可讓您乾淨地處理這個用例。

您能夠在此博客條目中閱讀有關Method Injection的動機的更多信息 。

前面的內容是不可取的,由於業務代碼知道並耦合到Spring Framework。方法注入是Spring IoC容器的一個高級功能,可讓您乾淨地處理這個用例。

您能夠在此博客條目中閱讀有關Method Injection的動機的更多信息 。

查找方法注入

查找方法注入是容器覆蓋容器管理的bean上的方法並返回容器中另外一個命名bean的查找結果的能力。查找一般涉及原型bean,如上一節中描述的場景。Spring Framework經過使用CGLIB庫中的字節碼生成來動態生成覆蓋該方法的子類來實現此方法注入。

  • 要使這個動態子類工做,Spring bean容器子類不能成爲的類final,以及要重寫的方法也不能final。
  • 對具備abstract方法的類進行單元測試須要您本身對類進行子類化並提供該abstract方法的存根實現。
  • 組件掃描也須要具體的方法,這須要具體的類來獲取。
  • 另外一個關鍵限制是查找方法不適用於工廠方法,特別是@Bean配置類中的方法,由於在這種狀況下,容器不負責建立實例,所以沒法建立運行時生成的子類蒼蠅

對於CommandManager前面代碼片斷中的類,Spring容器動態地覆蓋createCommand() 方法的實現。該CommandManager班沒有任何Spring的依賴,由於返工例所示:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}
複製代碼

在包含要注入的方法的客戶端類中(CommandManager在本例中),要注入的方法須要如下形式的簽名:

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

複製代碼

若是方法是abstract,則動態生成的子類實現該方法。不然,動態生成的子類將覆蓋原始類中定義的具體方法。請考慮如下示例:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

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

複製代碼

只要須要bean 的新實例,標識爲bean的bean 就會commandManager調用本身的createCommand()方法myCommand。myCommand若是實際須要,您必須當心將bean 部署爲原型。若是它是單例,myCommand 則每次返回相同的bean 實例。 或者,在基於註釋的組件模型中,您能夠經過@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();
}

複製代碼

或者,更具慣用性,您能夠依賴於針對查找方法的聲明返回類型解析目標bean:

public abstract class CommandManager {

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

    @Lookup
    protected abstract MyCommand createCommand();
}

複製代碼

請注意,您一般應該使用具體的存根實現來聲明這種帶註釋的查找方法,以使它們與Spring的組件掃描規則兼容,其中默認狀況下抽象類被忽略。此限制不適用於顯式註冊或顯式導入的bean類。

訪問不一樣範圍的目標bean的另外一種方法是ObjectFactory/ Provider注入點。請參閱Scoped Beans做爲依賴關係。

您可能還會發現ServiceLocatorFactoryBean(在 org.springframework.beans.factory.config包中)有用。

任意方法替換

與查找方法注入相比,一種不太有用的方法注入形式是可以使用另外一個方法實現替換託管bean中的任意方法。您能夠安全地跳過本節的其他部分,直到您確實須要此功能。

使用基於XML的配置元數據,您可使用該replaced-method元素將已存在的方法實現替換爲已部署的bean。考慮如下類,它有一個computeValue咱們想要覆蓋的方法:

public class MyValueCalculator {

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

    // some other methods...
}

複製代碼

實現org.springframework.beans.factory.support.MethodReplacer接口的類提供新的方法定義,如如下示例所示:

/**
 * 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">
    <!-- arbitrary method replacement -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

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

複製代碼

您可使用元素中的一個或多個元素 來指示被覆蓋的方法的方法簽名。僅當方法重載且類中存在多個變體時,才須要參數的簽名。爲方便起見,參數的類型字符串能夠是徹底限定類型名稱的子字符串。例如,如下全部匹配 java.lang.String:

java.lang.String
String
Str

複製代碼

由於參數的數量一般足以區分每一個可能的選擇,因此經過讓您只鍵入與參數類型匹配的最短字符串,此快捷方式能夠節省大量的輸入。

1.5 Bean範圍

建立bean定義時,能夠建立用於建立由該bean定義定義的類的實際實例的配方。bean定義是一個配方的想法很重要,由於它意味着,與一個類同樣,您能夠從一個配方建立許多對象實例。

您不只能夠控制要插入到從特定bean定義建立的對象中的各類依賴項和配置值,還能夠控制從特定bean定義建立的對象的範圍。這種方法功能強大且靈活,由於您能夠選擇經過配置建立的對象的範圍,而沒必要在Java類級別烘焙對象的範圍。能夠將Bean定義爲部署在多個範圍之一中。Spring Framework支持六個範圍,其中四個範圍僅在您使用Web感知時纔可用ApplicationContext。您還能夠建立 自定義範圍。

Bean範圍描述

  • 單例 singleton

    (默認)將單個bean定

    義範圍限定爲每一個Spring IoC容器的單個對象實例。

  • 原型prototype 將單個bean定義範圍限定爲任意數量的對象實例。

  • 請求request 將單個bean定義範圍限定爲單個HTTP請求的生命週期。也就是說,每一個HTTP請求都有本身的bean實例,它是在單個bean定義的後面建立的。僅在具備Web感知功能的Spring環境中有效ApplicationContext。

  • 會話 session將單個bean定義範圍限定爲HTTP的生命週期Session。僅在具備Web感知功能的Spring環境中有效ApplicationContext。

  • 應用application將單個bean定義範圍限定爲a的生命週期ServletContext。僅在具備Web感知功能的Spring環境中有效ApplicationContext。

  • WebSocket 將單個bean定義範圍限定爲a的生命週期WebSocket。僅在具備Web感知功能的Spring環境中有效ApplicationContext。

從Spring 3.0開始,線程範圍可用,但默認狀況下未註冊:請參閱SimpleThreadScope。從Spring 4.2開始,還有一個事務範圍: SimpleTransactionScope。有關如何註冊這些或任何其餘自定義做用域的說明,請參閱 使用自定義做用域。

1.5.1單例 singleton

只管理單個bean的一個共享實例,而且對具備與該bean定義匹配的ID或ID的bean的全部請求都會致使Spring容器返回一個特定的bean實例。

換句話說,當您定義bean定義並將其做爲單一做用域時,Spring IoC容器只建立該bean定義定義的對象的一個​​實例。此單個實例存儲在此類單例bean的緩存中,而且該命名Bean的全部後續請求和引用都將返回緩存對象。下圖顯示了單例範圍的工做原理:

Spring的單例bean概念不一樣於Gang of Four(GoF)模式書中定義的單例模式。GoF單例對一個對象的範圍進行硬編碼,使得每一個ClassLoader建立一個且只有一個特定類的實例。Spring單例的範圍最好描述爲每容器和每一個bean。這意味着,若是在單個Spring容器中爲特定類定義一個bean,則Spring容器將建立該bean定義所定義的類的一個且僅一個實例。單例範圍是Spring中的默認範圍。要將bean定義爲XML中的單例,您能夠定義一個bean,如如下示例所示:

<bean id="accountService" class="com.something.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

複製代碼

1.5.2 原型prototype

bean部署的非單例原型範圍致使每次發出對該特定bean的請求時都建立新的bean實例。也就是說,bean被注入另外一個bean,或者經過getBean()對容器的方法調用來請求它。一般,您應該對全部有狀態bean使用原型範圍,對無狀態bean使用單例範圍。

下圖說明了Spring原型範圍:

(數據訪問對象(DAO)一般不配置爲原型,由於典型的DAO不會保持任何會話狀態。咱們更容易重用單例圖的核心。)

如下示例將bean定義爲XML中的原型

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

複製代碼

與其餘範圍相比,Spring無論理原型bean的完整生命週期。容器實例化,配置和組裝原型對象並將其交給客戶端,而沒有該原型實例的進一步記錄。所以,儘管不管範圍如何都在全部對象上調用初始化生命週期回調方法,但在原型的狀況下,不會調用已配置的銷燬生命週期回調。客戶端代碼必須清理原型範圍的對象並釋放原型bean所擁有的昂貴資源。要使Spring容器釋放原型範圍的bean所擁有的資源,請嘗試使用自定義bean後處理器,它包含對須要清理的bean的引用。

在某些方面,Spring容器關於原型範圍bean的角色是Java new運算符的替代品。超過該點的全部生命週期管理必須由客戶端處理。(有關Spring容器中bean的生命週期的詳細信息,請參閱Lifecycle Callbacks。)

1.5.3 具備原型bean依賴關係的單例Bean

當您使用具備依賴於原型bean的單例做用域bean時,請注意在實例化時解析依賴項。所以,若是依賴項將原型範圍的bean注入到單例範圍的bean中,則會實例化一個新的原型bean,而後將依賴注入到單例bean中。原型實例是惟一提供給單例範圍bean的實例。

可是,假設您但願單例範圍的bean在運行時重複獲取原型範圍的bean的新實例。您不能將原型範圍的bean依賴注入到您的單例bean中,由於當Spring容器實例化單例bean並解析並注入其依賴項時,該注入只發生一次。若是您須要在運行時屢次使用原型bean的新實例,請參閱方法注入

1.5.4 請求,會話,應用程序和WebSocket範圍

在request,session,application,和websocket範圍只有當你使用一個基於web的Spring可ApplicationContext實現(例如 XmlWebApplicationContext)。若是將這些範圍與常規的Spring IoC容器一塊兒使用,例如ClassPathXmlApplicationContext,IllegalStateException則會引起抱怨未知Bean範圍的問題。

初始Web配置

爲了支持豆的範圍界定在request,session,application,和 websocket(即具備web做用域bean),須要作少許的初始配置定義你的豆以前。(標準範圍不須要此初始設置:singleton和prototype。)

如何完成此初始設置取決於您的特定Servlet環境。

若是您在Spring Web MVC中訪問scoped bean,其實是在Spring處理的請求中,則DispatcherServlet無需進行特殊設置。 DispatcherServlet已暴露全部相關國家。

若是您使用Servlet 2.5 Web容器,而且在Spring以外處理請求 DispatcherServlet(例如,使用JSF或Struts時),則須要註冊 org.springframework.web.context.request.RequestContextListener ServletRequestListener。對於Servlet 3.0+,可使用該WebApplicationInitializer 接口以編程方式完成。或者,或者對於舊容器,將如下聲明添加到Web應用程序的web.xml文件中:

<web-app>
 ...
 <listener>
 <listener-class>
 org.springframework.web.context.request.RequestContextListener
 </listener-class>
 </listener>
 ...
 </web-app>
複製代碼

或者,若是您的偵聽器設置存在問題,請考慮使用Spring RequestContextFilter。過濾器映射取決於周圍的Web應用程序配置,所以您必須根據須要進行更改。如下清單顯示了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>
複製代碼

DispatcherServlet,RequestContextListener和RequestContextFilter全部作一樣的事情,即將HTTP請求對象綁定到Thread爲該請求提供服務的對象。這使得請求和會話範圍的bean能夠在調用鏈的下游進一步使用。

DispatcherServlet,RequestContextListenerRequestContextFilter全部作一樣的事情,即將HTTP請求對象綁定到Thread爲該請求提供服務的對象。這使得請求和會話範圍的bean能夠在調用鏈的下游進一步使用。

請求範圍

考慮bean定義的如下XML配置:

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
複製代碼

Spring容器LoginAction經過loginAction對每一個HTTP請求使用bean定義來建立bean 的新實例。也就是說, loginActionbean的範圍是HTTP請求級別。您能夠根據須要更改建立的實例的內部狀態,由於從同一loginActionbean定義建立的其餘實例在狀態中看不到這些更改。它們特別針對我的要求。當請求完成處理時,將放棄做用於請求的bean。

使用註釋驅動的組件或Java配置時,@RequestScope註釋可用於將組件分配給request範圍。如下示例顯示瞭如何執行此操做:

@RequestScope
@Component
public class LoginAction {
    // ...
}
複製代碼
會話範圍

考慮bean定義的如下XML配置:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
複製代碼

Spring容器UserPreferences經過在userPreferences單個HTTP的生存期內使用bean定義來建立bean 的新實例Session。換句話說,userPreferencesbean在HTTP Session級別上有效地做用域。與請求範圍的bean同樣,您能夠根據須要更改建立的實例的內部狀態,由於知道Session一樣使用從同一userPreferencesbean定義建立的實例的其餘HTTP 實例在狀態中看不到這些更改,由於它們特定於單個HTTP Session。當Session最終丟棄HTTP時Session,也將丟棄做用於該特定HTTP的bean 。

使用註釋驅動的組件或Java配置時,可使用 @SessionScope註釋將組件分配給session範圍。

@SessionScope
@Component
public class UserPreferences {
    // ...
}
複製代碼

適用範圍 考慮bean定義的如下XML配置:

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
複製代碼

Spring容器AppPreferences經過appPreferences對整個Web應用程序使用一次bean定義來建立bean 的新實例。也就是說, appPreferencesbean在該ServletContext級別做用域並存儲爲常規 ServletContext屬性。這有點相似於Spring單例bean,但在兩個重要方面有所不一樣:它是一個單獨的ServletContext,不是每一個Spring的'ApplicationContext'(在任何給定的Web應用程序中可能有幾個),它其實是暴露的,所以是可見的做爲一個ServletContext屬性。

使用註釋驅動的組件或Java配置時,可使用 @ApplicationScope註釋將組件分配給application範圍。如下示例顯示瞭如何執行此操做:

@ApplicationScope
@Component
public class AppPreferences {
    // ...
}
複製代碼

做爲依賴關係的Scoped Bean

Spring IoC容器不只管理對象(bean)的實例化,還管理協做者(或依賴關係)的鏈接。若是要將(例如)HTTP請求範圍的bean注入到壽命較長範圍的另外一個bean中,您能夠選擇注入AOP代理來代替範圍內的bean。也就是說,您須要注入一個代理對象,該對象公開與範圍對象相同的公共接口,但也能夠從相關範圍(例如HTTP請求)中檢索真實目標對象,並將方法調用委託給真實對象。

您還能夠aop:scoped-proxy/在做用域的bean之間使用singleton,而後經過引用而後經過可序列化的中間代理,從而可以在反序列化時從新獲取目標單例bean。

當聲明aop:scoped-proxy/範圍的bean時prototype,共享代理上的每一個方法調用都會致使建立一個新的目標實例,而後轉發該調用。

此外,範圍代理不是以生命週期安全的方式從較短範圍訪問bean的惟一方法。您還能夠將注入點(即構造函數或setter參數或autowired字段)聲明爲ObjectFactory容許getObject()調用,以便在每次須要時按需檢索當前實例 - 無需保留實例或單獨存儲它。

做爲擴展變體,您能夠聲明ObjectProvider,它提供了幾個額外的訪問變體,包括getIfAvailable和getIfUnique。

調用它的JSR-330變體,Provider並與每次檢索嘗試的Provider<MyTargetBean聲明和相應get()調用一塊兒使用。有關JSR-330總體的更多詳細信息,請參見此處。

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <bean id="userPreferences" class="com.something.UserPreferences" scope="session">
        <!-- instructs the container to proxy the surrounding bean 定義代理的行。 -->
        <aop:scoped-proxy/> 
    </bean>

    <!-- a singleton-scoped bean injected with a proxy to the above bean -->
    <bean id="userService" class="com.something.SimpleUserService">
        <!-- a reference to the proxied userPreferences bean -->
        <property name="userPreferences" ref="userPreferences"/>
    </bean>
</beans>
複製代碼

要建立此類代理,請將子aop:scoped-proxy/元素插入到做用域bean定義中(請參閱選擇要建立的代理類型和 基於XML架構的配置)。豆類的定義爲什麼做用域的request,session和自定義範圍水平要求aop:scoped-proxy/元素?考慮如下單例bean定義,並將其與您須要爲上述範圍定義的內容進行對比(請注意,如下 userPreferencesbean定義不完整):

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

<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
複製代碼

在前面的示例中,singleton bean(userManager)注入了對HTTP Session-scoped bean(userPreferences)的引用。這裏的重點是 userManagerbean是一個單例:它每一個容器只實例化一次,它的依賴關係(在這種狀況下只有一個,userPreferencesbean)也只注入一次。這意味着userManagerbean只在徹底相同的userPreferences對象(即最初注入它的對象)上運行。

當將一個壽命較短的scoped bean注入一個壽命較長的scoped bean時,這不是你想要的行爲(例如,將一個HTTP Session-scoped協做bean做爲依賴注入到singleton bean中)。相反,您須要一個userManager 對象,而且,在HTTP的生命週期中Session,您須要一個userPreferences特定於HTTP 的對象Session。所以,容器建立一個對象,該對象公開與UserPreferences該類徹底相同的公共接口(理想狀況下是一個UserPreferences實例的對象),該UserPreferences對象能夠從做用域機制(HTTP請求Session等)中獲取真實 對象。容器將此代理對象注入到userManagerbean中,該bean不知道此UserPreferences引用是代理。在這個例子中,當一個 UserManager實例在依賴注入的UserPreferences 對象上調用一個方法,它其實是在代理上調用一個方法。而後,代理 UserPreferences從(在這種狀況下)HTTP中Session獲取真實UserPreferences對象,並將方法調用委託給檢索到的真實對象。

所以,在將bean request-和session-scopedbean注入協做對象時,您須要如下(正確和完整)配置 ,如如下示例所示:

<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
 <aop:scoped-proxy/>
 </bean>
複製代碼
<bean id="userManager" class="com.something.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
複製代碼

選擇要建立的代理類型 默認狀況下,當Spring容器爲使用該aop:scoped-proxy/元素標記的bean建立代理時,將建立基於CGLIB的類代理。

CGLIB代理只攔截公共方法調用!不要在這樣的代理上調用非公共方法。它們不會委託給實際的做用域目標對象。

或者,您能夠經過指定元素屬性false的值,將Spring容器配置爲爲此類做用域bean建立基於JDK接口的標準代理。使用基於JDK接口的代理意味着您不須要在應用程序類路徑中使用其餘庫來影響此類代理。可是,這也意味着做用域bean的類必須至少實現一個接口,而且注入了做用域bean的全部協做者必須經過其中一個接口引用bean。如下示例顯示了基於接口的代理:proxy-target-classaop:scoped-proxy/

<!-- DefaultUserPreferences implements the UserPreferences interface -->
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
    <aop:scoped-proxy proxy-target-class="false"/>
</bean>

<bean id="userManager" class="com.stuff.UserManager">
    <property name="userPreferences" ref="userPreferences"/>
</bean>
複製代碼

有關選擇基於類或基於接口的代理的更多詳細信息,請參閱代理機制

1.5.5 自定義範圍

bean範圍機制是可擴展的。您能夠定義本身的範圍,甚至能夠從新定義現有範圍,儘管後者被認爲是很差的作法,您沒法覆蓋內置singleton和prototype範圍。

#####建立自定義範圍 要將自定義做用域集成到Spring容器中,須要實現org.springframework.beans.factory.config.Scope本節中描述的 接口。有關如何實現本身的做用域的想法,請參閱Scope Spring Framework自己和Scopejavadoc 提供的實現 ,它們解釋了您須要更詳細地實現的方法。

該Scope接口有四種方法能夠從做用域中獲取對象,將其從做用域中刪除,而後將其銷燬。

例如,會話範圍實現返回會話範圍的bean(若是它不存在,則該方法在將其綁定到會話以供未來參考以後返回該bean的新實例)。如下方法從基礎範圍返回對象: Object get(String name, ObjectFactory objectFactory)

例如,會話範圍實現從基礎會話中刪除會話範圍的bean。應返回該對象,但若是找不到具備指定名稱的對象,則能夠返回null。如下方法從基礎範圍中刪除對象:

Object remove(String name)
複製代碼

如下方法記錄範圍在銷燬時或範圍中指定對象被銷燬時應執行的回調:

void registerDestructionCallback(String name, Runnable destructionCallback)
複製代碼

有關 銷燬回調的更多信息,請參閱javadoc或Spring做用域實現。

如下方法獲取基礎範圍的對話標識符:

String getConversationId()
複製代碼

每一個範圍的標識符都不一樣。對於會話範圍的實現,該標識符能夠是會話標識符。

使用自定義範圍

在編寫並測試一個或多個自定義Scope實現以後,您須要讓Spring容器知道您的新範圍。如下方法是Scope使用Spring容器註冊new的核心方法:

void registerScope(String scopeName, Scope scope);
複製代碼

此方法在ConfigurableBeanFactory接口上聲明,該接口可經過 Spring隨附的BeanFactory大多數具體ApplicationContext實現的屬性得到。

  • 該registerScope(..)方法的第一個參數是與範圍關聯的惟一名稱。Spring容器自己中的這些名稱的示例是singleton和 prototype。
  • 該registerScope(..)方法的第二個參數是Scope您但願註冊和使用的自定義實現的實際實例。

假設您編寫自定義Scope實現,而後註冊它,以下一個示例所示。

下一個示例使用SimpleThreadScope,它包含在Spring中,但默認狀況下未註冊。您本身的自定義Scope 實現的說明是相同的。

Scope threadScope = new SimpleThreadScope();
beanFactory.registerScope("thread", threadScope);
複製代碼

而後,您能夠建立符合自定義的做用域規則的bean定義, Scope以下所示:

<bean id="..." class="..." scope="thread">
複製代碼

使用自定義Scope實現,您不只限於範圍的編程註冊。您還能夠Scope使用CustomScopeConfigurer該類以聲明方式進行註冊 ,如如下示例所示:

<?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 https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="thread">
                    <bean class="org.springframework.context.support.SimpleThreadScope"/>
                </entry>
            </map>
        </property>
    </bean>

    <bean id="thing2" class="x.y.Thing2" scope="thread">
        <property name="name" value="Rick"/>
        <aop:scoped-proxy/>
    </bean>

    <bean id="thing1" class="x.y.Thing1">
        <property name="thing2" ref="thing2"/>
    </bean>

</beans>
複製代碼

放置aop:scoped-proxy/在FactoryBean實現中時,工廠bean自己是做用域的,而不是從中返回的對象getObject()。

1.6 定製Bean的本質

Spring Framework提供了許多可用於自定義bean特性的接口。本節將它們分組以下:

  • 生命週期回調
  • ApplicationContextAwareBeanNameAware
  • 其餘Aware接口

1.6.1 生命週期回調(Lifecycle Callbacks)

要與容器的bean生命週期管理進行交互,能夠實現Spring InitializingBean和DisposableBean接口。容器調用 afterPropertiesSet()前者,destroy()後者讓bean在初始化和銷燬​​bean時執行某些操做。

JSR-250 @PostConstruct和@PreDestroy註釋一般被認爲是在現代Spring應用程序中接收生命週期回調的最佳實踐。使用這些註釋意味着您的bean不會耦合到特定於Spring的接口。有關詳細信息,請參閱使用@PostConstruct和@PreDestroy。

若是您不想使用JSR-250註釋但仍想刪除耦合,請考慮init-method和destroy-methodbean定義元數據。

在內部,Spring Framework使用BeanPostProcessor實現來處理它能夠找到的任何回調接口並調用適當的方法。若是您須要自定義功能或其餘生命週期行爲Spring默認不提供,您能夠BeanPostProcessor本身實現。有關更多信息,請參閱 容器擴展點。

除了初始化和銷燬​​回調以外,Spring管理的對象還能夠實現Lifecycle接口,以便這些對象能夠參與啓動和關閉過程,這是由容器自身的生命週期驅動的。

本節描述了生命週期回調接口。

初始化回調

org.springframework.beans.factory.InitializingBean接口容許在容器上設置bean的全部必要屬性後,一個bean進行初始化工做。的InitializingBean接口規定了一個方法:

void afterPropertiesSet() throws Exception;
複製代碼

咱們建議您不要使用該InitializingBean接口,由於它會沒必要要地將代碼耦合到Spring。或者,咱們建議使用@PostConstruct註釋或指定POJO初始化方法。對於基於XML的配置元數據,您可使用該init-method屬性指定具備void無參數簽名的方法的名稱。使用Java配置,您可使用。的initMethod屬性 @Bean。請參閱接收生命週期回調。請考慮如下示例:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
複製代碼


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

前面的示例與如下示例幾乎徹底相同(包含兩個列表):

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

public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }
}
複製代碼

可是,前面兩個示例中的第一個沒有將代碼耦合到Spring。

毀滅回調

實現org.springframework.beans.factory.DisposableBean接口容許bean在包含它的容器被銷燬時得到回調。的 DisposableBean接口規定了一個方法:

void destroy() throws Exception;
複製代碼

咱們建議您不要使用DisposableBean回調接口,由於它會沒必要要地將代碼耦合到Spring。或者,咱們建議使用@PreDestroy註釋或指定bean定義支持的泛型方法。使用基於XML的配置元數據,您可使用該destroy-method屬性<bean/>。使用Java配置,您可使用。的destroyMethod屬性@Bean。請參閱 接收生命週期回調。考慮如下定義:

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

public class ExampleBean {

    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}
複製代碼

前面的定義與如下定義幾乎徹底相同:

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

public class AnotherExampleBean implements DisposableBean {

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
複製代碼

可是,前面兩個定義中的第一個沒有將代碼耦合到Spring。

您能夠destroy-method爲元素的屬性指定一個特殊 (inferred)值,該值指示Spring自動檢測特定bean類的公共close或 shutdown方法。(任何實現 java.lang.AutoCloseable或java.io.Closeable所以匹配的類。)您還能夠(inferred)在元素的default-destroy-method屬性 上設置此特殊值,以將此行爲應用於整組bean(請參閱 默認初始化和銷燬​​方法)。請注意,這是Java配置的默認行爲。

默認初始化和銷燬​​方法

當你寫的初始化和銷燬不使用Spring的具體方法回調InitializingBean和DisposableBean回調接口,你一般寫有名字,如方法init(),initialize(),dispose(),等等。理想狀況下,此類生命週期回調方法的名稱在項目中是標準化的,以便全部開發人員使用相同的方法名稱並確保一致性。

您能夠將Spring容器配置爲「查找」命名初始化並銷燬每一個bean上的回調方法名稱。這意味着,做爲應用程序開發人員,您能夠編寫應用程序類並使用調用的初始化回調 init(),而無需爲init-method="init"每一個bean定義配置屬性。Spring IoC容器在建立bean時調用該方法(而且符合前面描述的標準生命週期回調協定)。此功能還強制執行初始化和銷燬​​方法回調的一致命名約定。

假設您的初始化回調方法已命名,init()而且您的destroy回調方法已命名destroy()。而後,您的類相似於如下示例中的類:

public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
複製代碼

而後,您能夠在相似於如下內容的bean中使用該類:

<bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>
複製代碼

default-init-method頂級<beans/>元素屬性上存在屬性會致使Spring IoC容器init將bean類上調用的方法識別爲初始化方法回調。當bean被建立和組裝時,若是bean類具備這樣的方法,則在適當的時候調用它。

您能夠經過使用default-destroy-method頂級<beans/>元素上的屬性來相似地配置destroy方法回調(在XML中) 。

若是現有的bean類已經具備與約定一致的回調方法,則能夠經過使用 自身的init-method和destroy-method屬性指定(在XML中,即方法名稱)來覆蓋默認值。

Spring容器保證在爲bean提供全部依賴項後當即調用已配置的初始化回調。所以,在原始bean引用上調用初始化回調,這意味着AOP攔截器等還沒有應用於bean。首先徹底建立目標bean,而後應用帶有攔截器鏈的AOP代理(例如)。若是目標bean和代理是分開定義的,那麼您的代碼甚至能夠繞過代理與原始目標bean交互。所以,將攔截器應用於init方法是不一致的,由於這樣作會將目標bean的生命週期耦合到其代理或攔截器,並在代碼直接與原始目標bean交互時留下奇怪的語義。

結合生命週期機制

從Spring 2.5開始,您有三個控制bean生命週期行爲的選項:

  • 在InitializingBean和 DisposableBean回調接口
  • 定製init()和destroy()方法
  • 在@PostConstruct和@PreDestroy 註釋。您能夠組合這些機制來控制給定的bean。

若是爲bean配置了多個生命週期機制,而且每一個機制都配置了不一樣的方法名稱,則每一個配置的方法都按照此註釋後列出的順序執行。可是,若是init()爲多個這些生命週期機制配置了相同的方法名稱(例如, 對於初始化方法),則該方法將執行一次,如上 一節中所述。

爲同一個bean配置的多個生命週期機制具備不一樣的初始化方法,以下所示:

  • 用註釋方法註釋 @PostConstruct
  • mafterPropertiesSet()由InitializingBean回調接口定義
  • 自定義配置的init()方法

Destroy方法以相同的順序調用:

  • 用註釋方法註釋 @PreDestroy
  • destroy()由DisposableBean回調接口定義
  • 自定義配置的destroy()方法
啓動和關閉回調

Lifecycle接口爲任何具備本身的生命週期要求的對象(例如啓動和中止某些後臺進程)定義了基本方法:

public interface Lifecycle {

    void start();

    void stop();

    boolean isRunning();
}
複製代碼
Default Initialization and Destroy Methods

當你寫的初始化和銷燬不使用Spring的具體方法回調InitializingBean和DisposableBean回調接口,你一般寫有名字,如方法init(),initialize(),dispose(),等等。理想狀況下,此類生命週期回調方法的名稱在項目中是標準化的,以便全部開發人員使用相同的方法名稱並確保一致性。

您能夠將Spring容器配置爲「查找」命名初始化並銷燬每一個bean上的回調方法名稱。這意味着,做爲應用程序開發人員,您能夠編寫應用程序類並使用調用的初始化回調 init(),而無需爲init-method="init"每一個bean定義配置屬性。Spring IoC容器在建立bean時調用該方法(而且符合前面描述的標準生命週期回調協定)。此功能還強制執行初始化和銷燬​​方法回調的一致命名約定。

假設您的初始化回調方法已命名,init()而且您的destroy回調方法已命名destroy()。而後,您的類相似於如下示例中的類:

public class DefaultBlogService implements BlogService {

    private BlogDao blogDao;

    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }

    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}
複製代碼

而後,您能夠在相似於如下內容的bean中使用該類:

<beans default-init-method="init">

    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>

</beans>
複製代碼

default-init-method頂級元素屬性上存在屬性會致使Spring IoC容器init將bean類上調用的方法識別爲初始化方法回調。當bean被建立和組裝時,若是bean類具備這樣的方法,則在適當的時候調用它。

您能夠經過使用default-destroy-method頂級元素上的屬性來相似地配置destroy方法回調(在XML中) 。

若是現有的bean類已經具備與約定一致的回調方法,則能夠經過使用 自身的init-method和destroy-method屬性指定(在XML中,即方法名稱)來覆蓋默認值。

Spring容器保證在爲bean提供全部依賴項後當即調用已配置的初始化回調。所以,在原始bean引用上調用初始化回調,這意味着AOP攔截器等還沒有應用於bean。首先徹底建立目標bean,而後應用帶有攔截器鏈的AOP代理(例如)。若是目標bean和代理是分開定義的,那麼您的代碼甚至能夠繞過代理與原始目標bean交互。所以,將攔截器應用於init方法是不一致的,由於這樣作會將目標bean的生命週期耦合到其代理或攔截器,並在代碼直接與原始目標bean交互時留下奇怪的語義。

結合生命週期機制 從Spring 2.5開始,您有三個控制bean生命週期行爲的選項:

  • 在InitializingBean和 DisposableBean回調接口
  • 定製init()和destroy()方法
  • 在@PostConstruct和@PreDestroy 註釋。您能夠組合這些機制來控制給定的bean。

若是爲bean配置了多個生命週期機制,而且每一個機制都配置了不一樣的方法名稱,則每一個配置的方法都按照此註釋後列出的順序執行。可是,若是init()爲多個這些生命週期機制配置了相同的方法名稱(例如, 對於初始化方法),則該方法將執行一次,如上 一節中所述。

爲同一個bean配置的多個生命週期機制具備不一樣的初始化方法,以下所示:

  • 用註釋方法註釋 @PostConstruct
  • afterPropertiesSet()由InitializingBean回調接口定義
  • 自定義配置的init()方法

Destroy方法以相同的順序調用:

  • 用註釋方法註釋 @PreDestroy
  • destroy()由DisposableBean回調接口定義
  • 自定義配置的destroy()方法
啓動和關閉回調

該Lifecycle接口爲任何具備本身的生命週期要求的對象(例如啓動和中止某些後臺進程)定義了基本方法:

public interface LifecycleProcessor extends Lifecycle {

    void onRefresh();

    void onClose();
}
複製代碼

請注意,LifecycleProcessor它自己是Lifecycle 接口的擴展。它還添加了另外兩種方法來響應刷新和關閉的上下文。

請注意,常規org.springframework.context.Lifecycle接口是顯式啓動和中止通知的簡單合約,並不意味着在上下文刷新時自動啓動。要對特定bean的自動啓動(包括啓動階段)進行細粒度控制,請考慮實現org.springframework.context.SmartLifecycle。

此外,請注意,在銷燬以前不保證中止通知。在常規關閉時,全部Lifecyclebean在傳播通常銷燬回調以前首先收到中止通知。可是,在上下文生命週期中的熱刷新或停止刷新嘗試時,僅調用destroy方法。

啓動和關閉調用的順序很是重要。若是任何兩個對象之間存在「依賴」關係,則依賴方在其依賴以後開始,而且在其依賴以前中止。可是,有時,直接依賴性是未知的。您可能只知道某種類型的對象應該在另外一種類型的對象以前開始。在這些狀況下,SmartLifecycle接口定義了另外一個選項,即getPhase()在其超級接口上定義的方法 Phased。如下清單顯示了Phased界面的定義:

public interface Phased {

    int getPhase();
}
複製代碼

下面的列表顯示了SmartLifecycle接口的定義:

public interface SmartLifecycle extends Lifecycle, Phased {

    boolean isAutoStartup();

    void stop(Runnable callback);
}
複製代碼

啓動時,具備最低相位的對象首先啓動。停車時,按相反順序行駛。所以,實現SmartLifecycle且其getPhase()方法返回integer.min_值的對象將是第一個開始和最後一箇中止的對象。在頻譜的另外一端,integer.max_值的相位值將指示對象應在最後啓動並首先中止(多是由於它取決於要運行的其餘進程)。在考慮階段值時,還必須知道不實現SmartLifecycle的任何「正常」生命週期對象的默認階段是0。所以,任何負相位值都表示一個對象應該在這些標準組件以前開始(並在它們以後中止)。對於任何正相位值,反向爲真。

定義的stop方法SmartLifecycle接受回調。任何實現必須run()在該實現的關閉過程完成後調用該回調的方法。這樣就能夠在必要時啓用異步關閉,由於LifecycleProcessor接口 的默認實現DefaultLifecycleProcessor等待每一個階段內的對象組的超時值來調用該回調。默認的每階段超時爲30秒。您能夠經過定義lifecycleProcessor在上下文中命名的bean來覆蓋缺省生命週期處理器實例 。若是您只想修改超時,則定義如下內容就足夠了:

<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
    <!-- timeout value in milliseconds -->
    <property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
複製代碼

如前所述,該LifecycleProcessor接口還定義了用於刷新和關閉上下文的回調方法。後者驅動關閉過程就好像stop()已經顯式調用同樣,但它在上下文關閉時發生。另外一方面,'refresh'回調啓用了SmartLifecyclebean的另外一個功能 。刷新上下文時(在實例化並初始化全部對象以後),將調用該回調。此時,默認生命週期處理器檢查每一個SmartLifecycle對象的isAutoStartup()方法返回的布爾值 。若是true,那個對象是在那個點開始的,而不是等待顯式調用上下文或它本身的對象start()方法(與上下文刷新不一樣,上下文啓動不會自動發生在標準上下文實現中)。該phase值與任何「依賴式」的關係肯定爲前面所述的啓動順序。

在非Web應用程序中優雅地關閉Spring IoC容器

本節僅適用於非Web應用程序。Spring的基於Web的 ApplicationContext實現已經具備代碼,能夠在相關Web應用程序關閉時正常關閉Spring IoC容器。

若是在非Web應用程序環境中使用Spring的IoC容器(例如,在富客戶機桌面環境中),請使用JVM註冊關閉掛鉤。這樣作可確保正常關閉並在單例bean上調用相關的destroy方法,以便釋放全部資源。您仍然必須正確配置和實現這些destroy回調。

要註冊關閉掛鉤,請調用接口registerShutdownHook()上聲明的方法ConfigurableApplicationContext,如如下示例所示:

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");

        // add a shutdown hook for the above context...
        ctx.registerShutdownHook();

        // app runs here...

        // main method exits, hook is called prior to the app shutting down...
    }
}
複製代碼

1.6.2 ApplicationContextAware和BeanNameAware

當ApplicationContext建立實現org.springframework.context.ApplicationContextAware接口的對象實例時,將 爲該實例提供對該實例的引用ApplicationContext。如下清單顯示了ApplicationContextAware界面的定義:

public interface ApplicationContextAware {

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
複製代碼

所以,bean能夠ApplicationContext經過ApplicationContext接口或經過將引用轉換爲此接口的已知子類(例如ConfigurableApplicationContext,公開其餘功能)以編程方式操縱建立它們的方法。一種用途是對其餘bean進行編程檢索。有時這種能力頗有用。可是,通常狀況下,您應該避免使用它,由於它將代碼耦合到Spring而且不遵循Inversion of Control樣式,其中協做者做爲屬性提供給bean。其餘方法 ApplicationContext提供對文件資源的訪問,發佈應用程序事件和訪問MessageSource。這些附加功能在附加ApplicationContext功能中描述 。

從Spring 2.5開始,自動裝配是另外一種獲取參考的方法 ApplicationContext。「傳統」 constructor和byType自動裝配模式(如自動裝配協做者中所述)能夠分別爲ApplicationContext構造函數參數或setter方法參數提供類型的依賴性 。爲了得到更大的靈活性,包括自動裝配字段和多參數方法的能力,請使用基於註釋的新自動裝配功能。若是這樣作,ApplicationContext則自動裝入一個字段,構造函數參數或方法參數,ApplicationContext若是相關的字段,構造函數或方法帶有@Autowired註釋,則該參數須要該類型。有關更多信息,請參閱 使用@Autowired。

當applicationContext建立一個實現org.springframework.beans.factory.beannameaware接口的類時,將向該類提供對其關聯對象定義中定義的名稱的引用。下面的列表顯示了beannameaware接口的定義:

public interface BeanNameAware {

    void setBeanName(String name) throws BeansException;
}
複製代碼

在填充普通bean屬性以後,但在初始化回調(如initializingbean、afterpropertiesset或自定義init方法)以前調用回調。

1.6.3 其餘Aware接口

除了ApplicationContextAware和BeanNameAware討論(早期),Spring提供了普遍的Aware,讓豆子指示,他們須要必定的基礎設施的依賴容器回調接口。做爲通常規則,名稱表示依賴關係類型。下表總結了最重要的Aware接口:

名稱 注入依賴 解釋在......
ApplicationContextAware 宣佈ApplicationContext ApplicationContextAwareBeanNameAware
ApplicationEventPublisherAware 封閉的事件發佈者ApplicationContext 附加功能 ApplicationContext
BeanClassLoaderAware 用於加載bean類的類加載器。 實例化豆​​類
BeanFactoryAware 宣佈BeanFactory ApplicationContextAwareBeanNameAware
BeanNameAware 聲明bean的名稱。 ApplicationContextAwareBeanNameAware
BootstrapContextAware BootstrapContext容器運行的資源適配器。一般僅在JCA感知ApplicationContext實例中可用。 JCA CCI
LoadTimeWeaverAware 定義的weaver用於在加載時處理類定義。 在Spring框架中使用AspectJ進行加載時編織
MessageSourceAware 用於解析消息的已配置策略(支持參數化和國際化)。 附加功能 ApplicationContext
NotificationPublisherAware Spring JMX通知發佈者。 通知
ResourceLoaderAware 配置的加載程序,用於對資源進行低級訪問。 資源
ServletConfigAware 當前ServletConfig容器運行。僅在Web感知彈簧中有效 ApplicationContext Spring MVC
ServletContextAware 當前ServletContext容器運行。僅在Web感知彈簧中有效 ApplicationContext Spring MVC

請再次注意,使用這些接口會將您的代碼綁定到Spring API,而不會遵循Inversion of Control樣式。所以,咱們建議將它們用於須要以編程方式訪問容器的基礎架構bean。

相關文章
相關標籤/搜索