Spring framework核心

節日快樂發表於2020-12-09

這一部分涵蓋了Spring框架絕對不可或缺的所有技術。

1、IOC容器

1.1Spring IoC容器和beans介紹

org.springframework.beans和org.springframework.context包是springframework的IoC容器的基礎。

BeanFactory介面提供了一種高階配置機制,能夠管理任何型別的物件。

ApplicationContext是BeanFactory的一個子介面。它增加了與Spring的AOP特性更容易的整合;訊息資源處理(用於國際化)、事件釋出;和應用程式層特定的上下文,例如用於網路應用程式的WebApplicationContext 

簡而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext新增了更多特定於企業的功能。ApplicationContext是BeanFactory的一個完整超集,在本章中專門用於描述Spring的IoC容器。

在Spring中,構成應用程式主幹並由Spring IoC容器管理的物件稱為beans。bean是由Spring IoC容器例項化、組裝和管理的物件。否則,bean只是應用程式中許多物件中的一個。Beans以及它們之間的依賴關係反映在容器使用的配置後設資料中。

1.2.容器概述

介面org . Spring framework . context . application context代表Spring IoC容器,負責例項化、配置和組裝上述beans。容器通過讀取配置後設資料來獲得關於例項化、配置和組裝什麼物件的指令。配置後設資料用XML、Java註釋或Java程式碼表示。它允許您表達組成應用程式的物件以及這些物件之間豐富的相互依賴關係。


Spring提供了幾個現成的應用程式上下文介面實現。在獨立的應用程式中,通常建立ClassPathXmlApplicationContext 或FileSystemXmlApplicationContext的例項。雖然XML是定義配置後設資料的傳統格式,但是您可以通過提供少量的XML配置來宣告性地支援這些附加的後設資料格式,從而指示容器使用Java註釋或程式碼作為後設資料格式。

在大多數應用程式場景中,不需要顯式使用者程式碼來例項化Spring IoC容器的一個或多個例項。例如,在一個web應用程式場景中,應用程式的web.xml檔案中簡單的八行:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
您的應用程式類與配置後設資料相結合,這樣在建立和初始化應用程式上下文之後,您就擁有了一個完全配置和可執行的系統或應用程式。

 

 

1.2.1配置後設資料

Spring IoC容器消耗了一種形式的配置後設資料;這種配置後設資料代表了作為應用程式開發人員,您如何告訴Spring容器在您的應用程式中例項化、配置和組裝物件。

配置後設資料傳統上以簡單直觀的XML格式提供,這是本章的大部分內容用來傳達Spring IoC容器的關鍵概念和功能。

基於XML的後設資料不是唯一允許的配置後設資料形式。Spring IoC容器本身完全脫離了實際編寫配置後設資料的格式。如今,許多開發人員為他們的Spring應用程式選擇基於Java的配置。

 

Spring 2.5引入了對基於註釋的配置後設資料的支援。

從Spring 3.0開始,Spring JavaConfig專案提供的許多特性成為了核心Spring框架的一部分。因此,您可以通過使用Java而不是XML檔案來定義應用程式類外部的beans。要使用這些新功能,請參見@Configuration, @Bean, @Import and @DependsOn 註釋。

 

Spring配置由容器必須管理的至少一個(通常不止一個)bean定義組成。基於XML的配置後設資料將這些bean配置為頂層< beans/>元素中的< bean/>元素。Java配置通常在@Configuration類中使用@Bean註釋方法。

這些bean定義對應於組成應用程式的實際物件。通常,您定義服務層物件、資料訪問物件(DAOs)、表示物件(如Struts操作例項)、基礎結構物件(如Hibernate會話工廠、JMS佇列等)。通常不在容器中配置細粒度的域物件,因為建立和載入域物件通常是Dao和業務邏輯的責任。但是,您可以使用Spring與AspectJ的整合來配置在IoC容器控制之外建立的物件。

以下示例顯示了基於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 -->
    </bean>

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

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

</beans>

id屬性是一個字串,用於標識單個bean定義。class屬性定義bean的型別,並使用完全限定的類名。id屬性的值指的是協作物件。

1.2.2.例項化容器
 例項化Spring IoC容器很簡單。提供給ApplicationContext建構函式的一個或多個位置路徑實際上是資源字串,它們允許容器從各種外部資源載入配置後設資料,如本地檔案系統、Java類路徑等。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

 以下示例顯示了服務層物件(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物件/關係對映標準)的資料訪問物件組成。屬性名稱元素引用JavaBean屬性的名稱,ref元素引用另一個Bean定義的名稱。id和ref元素之間的這種聯絡表達了協作物件之間的依賴關係。

編寫基於XML的配置後設資料
讓bean定義跨越多個XML檔案可能很有用。通常,每個單獨的XML配置檔案代表您的體系結構中的一個邏輯層或模組。
您可以使用應用程式上下文建構函式從所有這些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。所有位置路徑都是相對於執行匯入的定義檔案的,因此services.xml必須與執行匯入的檔案位於同一目錄或類路徑位置,而messageSource.xml和themeSource.xml必須位於匯入檔案位置下方的資源位置。如您所見,前導斜槓被忽略,但鑑於這些路徑是相對的,最好不要使用斜槓。根據Spring模式,被匯入檔案的內容,包括頂層的< bean/>元素,必須是有效的XML bean定義。

 

可以使用相對引用父目錄中的檔案,但不建議這樣做"../"路徑。這樣做會建立對當前應用程式之外的檔案的依賴。特別是,對於“類classpath:”網址(例如,“類classpath:../services.xml),執行時解析過程選擇“最近的”類路徑根,然後檢視其父目錄。類路徑配置更改可能會導致選擇不同的不正確目錄。

您可以始終使用完全限定的資源位置,而不是相對路徑:例如,“檔案:C:/config/services.xml”或“classpath:/config/services.xml”。但是,請注意,您正在將應用程式的配置耦合到特定的絕對位置。對於這種絕對位置,通常最好保持間接性,例如,通過執行時根據JVM系統屬性解析的“$ {……}”佔位符。

1.2.3.使用容器

ApplicationContext是高階工廠的介面,能夠維護不同beans及其依賴項的登錄檔。使用 getBean(String name, Class<T> requiredType) 方法可以檢索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();

最靈活的變體是與讀取器委託相結合的GenericApplicationContext,例如,對於XML檔案,使用XmlBeanDefinitionReader:  

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

如果需要,這樣的讀取器委託可以在同一個應用程式上下文中混合和匹配,從不同的配置源中讀取bean定義。  

然後,您可以使用getBean來檢索Bean的例項。ApplicationContext介面有一些其他的方法來檢索beans,但是理想情況下,您的應用程式程式碼不應該使用它們。事實上,您的應用程式程式碼應該根本沒有對getBean()方法的呼叫,因此根本不依賴於Spring APIs。例如,Spring與網路框架的整合為各種網路框架元件(如控制器和JSF管理的bean)提供了依賴注入,允許您通過後設資料(如自動連線註釋)宣告對特定bean的依賴。

1.3.Bean概述

 Spring IoC容器管理一個或多個beans。這些beans是用您提供給容器的配置後設資料建立的,例如,以XML <bean/>定義的形式。

在容器本身中,這些bean定義被表示為BeanDefinition物件,這些物件包含(除其他資訊外)以下後設資料:

   包限定的類名:通常是被定義的bean的實際實現類。

   Bean行為配置元素,宣告bean在容器中的行為(範圍、生命週期回撥等等)。

   對bean完成工作所需的其他bean的引用;這些引用也被稱為協作者或依賴者。

   在新建立的物件中設定的其他配置設定,例如,在管理連線池的bean中使用的連線數,或者連線池的大小限制。

 這些後設資料轉化為一組屬性,組成了每個bean定義。

 

 

 除了包含如何建立特定bean的資訊的bean定義之外,ApplicationContext實現還允許註冊使用者在容器外建立的現有物件。這是通過方法getBeanFactory()訪問應用程式上下文的BeanFactory來完成的,該方法返回BeanFactory實現DefaultListableBeanFactory。DefaultListableBeanFactory通過方法registerSingleton(..)和registerBeanDefinition(..).然而,典型的應用程式只使用通過後設資料bean定義定義的bean。

 Bean後設資料和手動提供的單例例項需要儘早註冊,以便容器在自動連線和其他自省步驟中能夠正確地推理它們。雖然在某種程度上支援覆蓋現有的後設資料和現有的單例例項,但是官方不支援在執行時註冊新的bean(同時對工廠進行實時訪問),這可能會導致併發訪問異常和/或bean容器中的狀態不一致.

 1.3.1.命名Bean

 每個bean都有一個或多個識別符號。這些識別符號在承載bean的容器中必須是唯一的。一個bean通常只有一個識別符號,但是如果它需要多個識別符號,多餘的識別符號可以被認為是別名。

在基於XML的配置後設資料中,您使用id和/name屬性來指定bean識別符號。id屬性允許您只指定一個id。傳統上,這些名稱是字母數字(‘MyBean’、‘FooServiCe’等)。),但也可能包含特殊字元。如果您想為bean引入其他別名,也可以在name屬性中指定它們,用逗號(,,分號(;),或者空白。作為一個歷史記錄,在Spring 3.1之前的版本中,id屬性被定義為xsd:ID型別,它約束可能的字元。從3.1開始,它被定義為xsd:string型別。請注意,bean id的唯一性仍然由容器強制執行,儘管不再由XML解析器強制執行。

您不需要為bean提供名稱或id。如果沒有顯式提供名稱或id,容器將為該bean生成一個唯一的名稱。但是,如果您想通過名稱引用該bean,通過使用引用元素或服務定位器樣式查詢,您必須提供一個名稱。不提供名稱的動機與使用內部beans和自動連線協作者有關。

 

Bean命名約定

約定是在命名beans時使用標準的Java約定作為例項欄位名稱。也就是說,bean名稱以小寫字母開頭,並且從那時起是駱駝大小寫。這類名稱的例子有(不帶引號)'accountManager', 'accountService', 'userDao', 'loginController'等等

 

在Bean定義之外別名Bean

在bean定義本身中,您可以為bean提供多個名稱,方法是使用由id屬性指定的最多一個名稱和name屬性中任意數量的其他名稱的組合。這些名稱可以是同一個bean的等效別名,在某些情況下很有用,例如允許應用程式中的每個元件通過使用特定於該元件本身的bean名稱來引用公共依賴關係。

然而,指定實際定義bean的所有別名並不總是足夠的。有時需要為在別處定義的bean引入一個別名。這在大型系統中是常見的情況,在大型系統中,配置被分割到每個子系統中,每個子系統都有自己的一組物件定義。在基於XML的配置後設資料中,您可以使用< alias/>元素來實現這一點。

<alias name="fromName" alias="toName"/>

在這種情況下,一個名為fromName的bean(在同一個容器中)在使用這個別名定義後,也可以被稱為toName。

例如,子系統A的配置後設資料可以通過subsystemA-dataSource的名稱引用資料來源。子系統B的配置後設資料可以通過subsystemB-dataSource的名稱引用資料來源。當組成使用這兩個子系統的主應用程式時,主應用程式以MyApp-資料來源的名稱引用資料來源。要使所有三個名稱都引用同一個物件,可以將以下別名定義新增到配置後設資料中:

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

現在,每個元件和主應用程式都可以通過一個唯一的名稱來引用資料來源,並保證不與任何其他定義衝突(有效地建立了一個名稱空間),但它們引用的是同一個bean。  

如果您使用的是Java配置,那麼@Bean註釋可以用來提供別名。有關詳細資訊,請參見使用@Bean註釋。

1.3.2.例項化beans

 bean定義本質上來說是建立一個或多個物件的方法。當問及一個命名bean時,容器會檢視這個方法並使用bean定義中封裝的配置後設資料建立(或取得)一個實際的物件。

如果使用基於XML的配置後設資料,則需要在< bean/>元素的class屬性中指定要例項化的物件的型別(或類)。這個類屬性在內部是BeanDefinition例項上的一個Class屬性,通常是強制性的。(有關異常,請參見使用例項工廠方法和Bean定義繼承的例項化。)您可以通過兩種方式之一使用Class屬性:

通常,在容器本身通過反射呼叫其建構函式直接建立bean的情況下,指定要構造的bean類,這在某種程度上等同於使用new操作符的Java程式碼。
要指定包含為建立物件而呼叫的靜態工廠方法的實際類,在容器呼叫類上的靜態工廠方法來建立bean的情況下就不太常見了。從靜態工廠方法呼叫返回的物件型別可以是相同的類,也可以完全是另一個類。

內部類名

如果要為靜態巢狀類配置bean定義,必須使用巢狀類的二進位制名稱。

例如,如果您在com.example包中有一個名為Foo的類,並且這個Foo類有一個名為Bar的靜態巢狀類,那麼bean定義上的“類”屬性值將是…com.example.Foo$Bar

請注意,名稱中使用了$字元來分隔巢狀類名和外部類名。

用建構函式例項化 

Spring IoC容器幾乎可以管理任何您希望它管理的類;它不限於管理真正的JavaBeans。大多數Spring使用者更喜歡實際的JavaBeans,它只有一個預設的(無引數)建構函式和適當的設定器和獲取器,它們是根據容器中的屬性建模的。您的容器中還可以有更多外來的非bean風格的類。例如,如果您需要使用一個完全不符合JavaBean規範的遺留連線池,Spring也可以管理它。

使用基於XML的配置後設資料,您可以按如下方式指定bean類:

 

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

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

有關向建構函式提供引數(如果需要)以及在構造物件後設定物件例項屬性的機制的詳細資訊,請參見注入依賴項。 

用靜態工廠方法例項化

定義使用靜態工廠方法建立的bean時,可以使用class 屬性指定包含靜態工廠方法的類,並使用名為factory-method 的屬性指定工廠方法本身的名稱。您應該能夠呼叫此方法(帶有後面描述的可選引數)並返回一個活動物件,該物件隨後被視為是通過建構函式建立的。這種bean定義的一個用途是在遺留程式碼中呼叫靜態工廠

以下bean定義指定將通過呼叫factory-method來建立bean。定義沒有指定返回物件的型別(類),只指定包含工廠方法的類。在本例中,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;
    }
}

有關為工廠方法提供(可選)引數以及在物件從工廠返回後設定物件例項屬性的機制的詳細資訊,請參見詳細的依賴關係和配置。  

使用例項工廠方法的例項化  

例項工廠方法的例項化從容器呼叫現有bean的非靜態方法來建立新bean。要使用這種機制,將class 屬性保留為空,並在factory-bean屬性中,指定當前(或父/祖先)容器中的bean的名稱,該容器包含要呼叫來建立物件的例項方法。用factory-method屬性設定工廠方法本身的名稱。

<!-- 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"/>
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"/>

  

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文件中,factory bean是指在Spring容器中配置的bean,它將通過例項或靜態工廠方法建立物件。相比之下,FactoryBean(注意大寫)指的是Spring特定的FactoryBean。   

1.4.依賴性  

1.4.1.依賴注入

依賴注入(DI)是一個過程,通過這個過程,物件定義它們的依賴關係,也就是說,它們使用的其他物件,只通過建構函式引數、工廠方法的引數,或者在物件例項被構造或從工廠方法返回後在物件例項上設定的屬性。然後,容器在建立bean時注入這些依賴項。這個過程從根本上說是控制反轉(IoC)的逆過程,即bean本身通過使用類的直接構造或服務定位器模式來控制其依賴關係的例項化或位置。

DI有兩種主要的變體,基於建構函式的依賴注入和基於Setter的依賴注入

基於建構函式的依賴注入

基於建構函式的DI是通過容器呼叫一個帶有多個引數的建構函式來實現的,每個引數代表一個依賴關係。呼叫帶有特定引數的靜態工廠方法來構造bean幾乎是等價的,並且本討論將引數類似地對待建構函式和靜態工廠方法。下面的示例顯示了一個只能通過建構函式注入進行依賴注入的類。請注意,這個類沒有什麼特別之處,它是一個POJO,不依賴於容器特定的介面、基類或註釋。 

 

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...
}

建構函式引數解析  

使用引數的型別進行建構函式引數解析匹配。如果bean定義的建構函式引數中不存在潛在的模糊性,那麼在bean定義中定義建構函式引數的順序就是在例項化bean時將這些引數提供給適當的建構函式的順序。

 

package x.y;

public class Foo {

    public Foo(Bar bar, Baz baz) {
        // ...
    }
}

不存在潛在的歧義,假設Bar和Baz類沒有繼承關係。因此,以下配置工作正常,並且您不需要在< constructor-arg/>元素中顯式指定建構函式引數indexes/type。  

 

<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</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>

除了解決多個簡單值的不確定性之外,指定索引還解決了建構函式有兩個相同型別引數的不確定性。請注意,索引是基於0的。

建構函式引數名  

 您還可以使用建構函式引數名稱來消除值的歧義:

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

    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

請記住,要使這一點開箱即用,您的程式碼必須在啟用除錯標誌的情況下編譯,以便Spring可以從建構函式中查詢引數名稱。如果不能用除錯標誌編譯程式碼(或者不想),可以使用@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的依賴注入  

基於Setter的DI是通過容器呼叫bean上的setter方法,然後呼叫無引數建構函式無引數靜態工廠方法來例項化bean來實現的。

下面的示例顯示了一個只能使用純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...
}

因為您可以混合基於建構函式和基於設定函式的DI,所以對於強制依賴項使用建構函式,對於可選依賴項使用設定方法或配置方法是一個很好的經驗法則。請注意,在setter方法上使用@Required註釋可以使屬性成為必需的依賴項。  

Spring團隊通常提倡建構函式注入,因為它使人們能夠將應用程式元件實現為不可變的物件,並確保所需的依賴關係不為空。此外,建構函式注入的元件總是以完全初始化的狀態返回給客戶端(呼叫)程式碼。

Setter注入應該主要用於可選的依賴項,這些依賴項可以在類中被賦予合理的預設值。否則,必須在程式碼使用依賴關係的任何地方執行非空檢查。setter注入的一個好處是,setter方法使該類的物件可以在以後重新配置或重新注入。

使用對特定類最有意義的DI樣式。有時候,當處理第三方類時,你沒有原始碼,選擇是為你做的。例如,如果第三方類沒有公開任何setter方法,那麼建構函式注入可能是DI唯一可用的形式。

 

 依賴性解決過程

 容器執行bean依賴關係解析,如下所示:

  ApplicationContext是用描述所有beans的配置後設資料建立和初始化的。配置後設資料可以通過XML、Java程式碼或註釋來指定。

   對於每個bean,它的依賴關係以屬性、建構函式引數或靜態工廠方法的引數的形式表示,如果您使用的是靜態工廠方法而不是普通的建構函式。這些依賴關係是在實際建立bean時提供給bean的。

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

  作為值的每個屬性或建構函式引數都從其指定格式轉換為該屬性或建構函式引數的實際型別。預設情況下,Spring可以將字串格式的值轉換為所有內建型別,如int、long、string、boolean等。

 

如果主要使用建構函式注入,就有可能建立一個無法解析的迴圈依賴場景。

比如:A類通過建構函式注入需要B類的一個例項,B類通過建構函式注入需要A類的一個例項。如果您將類A和類B的beans配置為相互注入,Spring IoC容器會在執行時檢測到此迴圈引用,並丟擲一個BeanCurrentLincreationException。 

一個可能的解決方案是編輯一些類的原始碼,由設定者而不是構造者來配置。或者,避免建構函式注入,只使用setter注入。

與典型情況(沒有迴圈依賴)不同,bean A和bean B之間的迴圈依賴迫使其中一個bean在完全初始化之前被注入到另一個bean中(典型的先有雞還是先有蛋的場景)。 

一般可以相信Spring做的是對的。它在容器載入時檢測配置問題,例如對不存在的beans和迴圈依賴的引用。在實際建立bean時,Spring儘可能晚地設定屬性和解決依賴關係。

這意味著,如果在建立一個物件或其依賴項時出現問題,正確載入的Spring容器可以在您請求該物件時生成異常。例如,由於缺少屬性或屬性無效,bean會引發異常。這種對某些配置問題的潛在延遲可見性是應用程式上下文實現預設預例項化單例beans的原因。在實際需要之前建立這些beans需要一些前期時間和記憶體,在建立應用程式上下文時,而不是稍後,您會發現配置問題。您仍然可以覆蓋這個預設行為,這樣單例beans將會延遲初始化,而不是預先例項化。

如果不存在迴圈依賴,當一個或多個協作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"/>

 

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;
    }
}

 在前面的示例中,設定器被宣告為與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"/>

  

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被告知呼叫一個靜態工廠方法來返回物件的一個例項,而不是使用一個建構函式:

<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"/>
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;
    }
}
1.4.2.詳細的依賴關係和配置

您可以將bean屬性和建構函式引數定義為對其他託管bean(協作者)的引用,或者定義為內聯定義的值。Spring基於XML的配置後設資料支援其< property/>和< constructor-arg/>元素中的子元素型別。

直接值(原語、字串等)  

< property/>元素的value屬性將屬性或建構函式引數指定為人類可讀的字串表示形式。Spring的轉換服務用於將這些值從字串轉換為屬性或引數的實際型別。

<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>

 您還可以將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屬性編輯器機制將< value/>元素中的文字轉換為java.util.Properties例項。這是一個很好的快捷方式,並且是Spring團隊支援使用巢狀的< value/>元素而不是value屬性樣式的少數地方之一。  

idref元素 

idref元素只是將容器中另一個bean的id(字串值,而不是引用)傳遞給< constructor-arg/>或< property/>元素的一種防錯方式。

<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屬性的值不執行驗證。錯別字只有在客戶端bean實際例項化時才會被發現(最有可能導致致命的結果)。如果客戶端bean是一個原型bean,這個錯別字和產生的異常可能只有在部署容器之後很久才會被發現。  

< idref/>元素帶來價值的一個常見之處(至少在Spring 2.0之前的版本中)是在一個ProxyFactoryBean定義中配置AOP攔截器。當您指定攔截器名稱時,使用< idref/>元素可以防止您拼錯攔截器id。  

 

對其他beans(協作者)的引用

ref元素是<constructor-arg/>或<property/>定義元素中的最後一個元素。在這裡,您將一個bean的指定屬性的值設定為對由容器管理的另一個bean(協作者)的引用。被引用的bean是其屬性將被設定的bean的依賴項,並且在設定屬性之前根據需要對其進行初始化。(如果協作者是單例bean,它可能已經被容器初始化了。)所有引用最終都是對另一個物件的引用。作用域和驗證取決於您是否通過bbeanlocal,  parent屬性指定了另一個物件的id/name。

 

通過< ref/>bean屬性的值可以與目標bean的id屬性相同,也可以是目標bean的name屬性中的一個值。

<ref bean="someBean"/>

通過parent 屬性指定目標bean會建立對當前容器的父容器中的bean的引用。parent 屬性的值可以與目標bean的id屬性相同,也可以是目標bean的name屬性中的一個值,並且目標bean必須位於當前容器的父容器中。當您有一個容器層次結構,並且您想用一個與父bean同名的代理來包裝父容器中的現有bean時,您主要使用這個bean引用變體。

<!-- in the parent context -->
<bean id="accountService" class="com.foo.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 
< property/>或< constructor-arg/>元素中的< 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或名稱;如果指定,容器不使用這樣的值作為識別符號。容器還忽略了建立時的範圍標誌:內部bean總是匿名的,並且它們總是與外部bean一起建立。除了封閉bean之外,不可能將內部bean注入到協作bean中,也不可能獨立訪問它們。  

 

集合

在<list/>, <set/>, <map/>,和<props/>元素中,分別設定了Java集合型別ListSetMap, 和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>

 map key或value或set value的值也可以是以下任何元素: 

bean | ref | idref | list | set | map | props | value | null

 集合合併

子屬性集合的值集從父<props />繼承所有屬性元素,子支援值的值覆蓋父集合中的值。  

集合合併的侷限性 

 您不能合併不同的集合型別(如對映和列表),如果您嘗試這樣做,則會引發適當的異常。必須在較低的繼承子定義上指定merge屬性;在父集合定義上指定merge屬性是多餘的,並且不會導致所需的合併。

強型別集合

public class Foo {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<beans>
    <bean id="foo" class="x.y.Foo">
        <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>

Null 和 “”字串

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>
exampleBean.setEmail("");
<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>
exampleBean.setEmail(null);

帶有p名稱空間的XML快捷方式  

p-namespace使您能夠使用bean元素的屬性而不是巢狀的< property/>元素來描述您的屬性值和/或協作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="classic" class="com.example.ExampleBean">
        <property name="email" value="foo@bar.com"/>
    </bean>

    <bean name="p-namespace" class="com.example.ExampleBean" p:email="foo@bar.com"/>
</beans>

該示例在bean定義中顯示了一個名為email的p-namespace屬性。這告訴Spring包含一個屬性宣告。如前所述,p-namespace沒有模式定義,因此您可以將屬性的名稱設定為屬性名稱。 

<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定義使用< property name= "spouse" ref="jane"/>來建立從bean john到bean jane的引用,而第二個bean定義使用p:spouse-ref="jane "作為屬性來做同樣的事情。在這種情況下,spouse是屬性名,而-ref部分表明這不是一個直接的值,而是對另一個bean的引用。  

帶有c-namespace的XML快捷方式  

與帶有p-namespace的XML快捷方式類似,Spring 3.1中新引入的c-namespace允許使用內聯屬性來配置建構函式引數,而不是巢狀的建構函式引數元素。  

<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="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>

    <!-- traditional declaration -->
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
        <constructor-arg value="foo@bar.com"/>
    </bean>

    <!-- c-namespace declaration -->
    <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/>

</beans>

對於建構函式引數名不可用的極少數情況(通常是在沒有除錯資訊的情況下編譯位元組碼),可以使用回退到引數索引:  

<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>

實際上,建構函式解析機制在匹配引數方面非常有效,所以除非真的需要,否則我們建議在整個配置中使用名稱符號。  

複合屬性名  

 設定bean屬性時,可以使用複合或巢狀屬性名,只要路徑的所有元件(除了最終屬性名)都不為空。

 

<bean id="foo" class="foo.Bar">
    <property name="fred.bob.sammy" value="123" />
</bean>

foo bean有一個fred屬性,它有一個bob屬性,它有一個sammy屬性,最終sammy屬性被設定為值123。為了實現這一點,foo的fred屬性和fred的bob屬性在構造bean後不能為null,否則會引發NullPointerException。  

1.4.3使用Using depends-on

 如果一個bean是另一個bean的依賴項,這通常意味著一個bean被設定為另一個bean的屬性。通常,您可以使用基於XML的配置後設資料中的< ref/>元素來實現這一點。但是,有時候beans之間的依賴關係不那麼直接;例如,類中的靜態初始化器需要被觸發,比如資料庫驅動註冊。

 在初始化使用此元素的bean之前,depends-on屬性可以顯式強制初始化一個或多個bean。以下示例使用depends-on屬性來表示對單個bean的依賴:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

要表示對多個bean的依賴,請提供一個bean名稱列表作為依賴屬性的值,用逗號、空格和分號作為有效分隔符:  

 

<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" />

bean定義中的dependent-on屬性既可以指定初始化時的依賴關係,初始化時,被依賴的bean是先被初始化的;也可以指定相應的銷燬時的依賴關係(僅限於單例bean)。depends-on適用於表面上看起來兩個bean之間沒有使用屬性之類的強連線的bean,但是兩個bean又確實存在前後依賴關係的情況,使用了depends-on的時候,依賴他人的bean是先於被依賴bean銷燬的,因此依賴也可以控制關機順序。 

depends-on 強制的說明在該Bean 初始化之前,那些Bean必須先初始化!

 ref通常用在一個Bean的屬性指向另外一個Bean,這個Bean必須先初始化。

1.4.4.惰性初始化的beans

預設情況下,作為初始化過程的一部分,應用上下文實現急切地建立和配置所有的單例beans。通常,這種預例項化是可取的,因為配置或周圍環境中的錯誤會立即被發現,而不是幾小時甚至幾天後。當這種行為不可取時,您可以通過將bean定義標記為惰性初始化來防止單例bean的預例項化。惰性初始化的bean告訴IoC容器在第一次被請求時建立一個bean例項,而不是在啟動時。

在XML中,這種行為由< bean/>元素上的lazy-init屬性控制;例如:

<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>

當前面的配置被應用程式上下文使用時,名為lazy的bean在應用程式上下文啟動時不會被提前例項化,而not.lazy bean會被提前例項化。  

然而,當延遲初始化的bean是未延遲初始化的單例bean的依賴項時,應用程式上下文會在啟動時建立延遲初始化的bean,因為它必須滿足單例bean的依賴項。惰性初始化的bean被注入到未惰性初始化的單例bean中。

您還可以通過在< beans/>元素上使用default-lazy-init屬性來控制容器級別的惰性初始化;例如:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>
1.4.5.Autowiring 協作者 

 Spring容器可以用autowire 協作beans之間的關係。您可以允許Spring通過檢查ApplicationContext的內容來為您的bean自動解析協作者(其他bean)。自動佈線有以下優點:

   自動連線可以大大減少指定屬性或建構函式引數的需要。

   自動佈線可以隨著物件的發展更新配置。例如,如果您需要向一個類新增一個依賴項,該依賴項可以自動得到滿足,而無需修改配置。因此,自動連線在開發過程中特別有用,當程式碼庫變得更加穩定時,不排除切換到顯式連線的選項。

 no  (預設)無自動佈線。Bean引用必須通過ref元素來定義。對於較大的部署,不建議更改預設設定,因為顯式指定協作者可以提供更好的控制和清晰度。在某種程度上,它記錄了一個系統的結構。

byName  按屬性名自動佈線。Spring尋找與需要自動連線的屬性同名的bean。例如,如果一個bean定義按名稱設定為autowire,並且它包含一個master屬性(也就是說,它有一個setMaster(..)方法),Spring尋找一個名為master的bean定義,並使用它來設定屬性。

byType  如果容器中恰好存在一個屬性型別的bean,則允許自動連線屬性。如果存在多個bean,則會引發致命異常,這表明您不能對該bean使用byType自動連線。

constructor  類似於byType,但適用於建構函式引數。如果容器中沒有恰好一個建構函式引數型別的bean,則會引發致命錯誤。

 

自動佈線autowiring的侷限性和缺點

當自動佈線在整個專案中一致使用時,效果最佳。如果通常不使用自動連線,開發人員可能會對使用它來連線一個或兩個bean定義感到困惑。

  1、property 和constructor-arg設定中的顯式依賴關係總是覆蓋自動連線。您不能自動連線所謂的簡單屬性,如原語、字串和類(以及此類簡單屬性的陣列)。這個限制是被設計出來的。

  2、自動佈線不如顯式佈線精確。儘管如上表所述,Spring小心翼翼地避免在可能產生意外結果的模糊情況下進行猜測,但是Spring管理的物件之間的關係不再被明確記錄。

  3、佈線資訊可能不適用於可能從Spring容器生成文件的工具。

  4、容器中的多個bean定義可能與要自動連線的setter方法或建構函式引數指定的型別相匹配。對於陣列、集合或對映,這不一定是個問題。然而,對於期望單個值的依賴項,這種模糊性並不是任意解決的。如果沒有唯一的bean定義可用,則會引發異常。

    1、放棄自動佈線,支援顯式佈線。

    2、通過將bean定義的autowire-candidate屬性設定為false來避免自動連線bean定義。

    3、通過將< bean/>元素的primary 屬性設定為true,將單個bean定義指定為主要候選物件。

    4、實現基於註釋的配置中可用的更細粒度的控制。

 

autowiring中排除一個bean

基於每個bean,您可以從自動連線中排除一個bean。在Spring的XML格式中,將< bean/>元素的autowire-candidate屬性設定為false該容器使特定的bean定義對自動連線基礎結構(包括註釋樣式配置,如@自動連線)不可用。

 

autowire-candidate屬性設計為僅影響基於型別的自動佈線。它不影響按名稱的顯式引用,即使指定的bean沒有被標記為autowire-candidate物件,該引用也會被解析。因此,如果名稱匹配,按名稱自動連線仍然會注入一個bean。

 

您還可以根據bean名稱的模式匹配來限制自動連線候選物件。頂層< beans>元素在其default-autowire-candidates屬性中接受一個或多個模式。例如,要將自動連線候選狀態限制為名稱以儲存庫結尾的任何bean,請提供*儲存庫的值。要提供多個模式,請在逗號分隔的列表中定義它們。

bean定義autowire-candidates屬性的顯式值true或false總是優先,對於這種bean,模式匹配規則不適用。

 

這些技術對於您永遠不想通過自動連線注入到其他bean中的bean非常有用。這並不意味著排除的bean本身不能使用自動連線進行配置。相反,bean本身並不是自動連線其他bean的候選物件。

 

相關文章