spring-framework-reference(5.1.1.RELEASE)中文版——Core部分


前言

最近在學習Spring框架,在學習的同時,借助有道翻譯,整理翻譯了部分文檔,由於尚在學習當中,所以該篇文章將會定時更新,目標在一個月左右時間翻譯完全部版本。

雖然大部分內容為翻譯,但是其中可能會添加一些我對Spring的理解,最近也在計划建立一個wiki版本,希望同樣在學習的你,可以參與進來,我們共同完成該版本文檔的翻譯與評注。感興趣的,可以私信聯系,方便共同學習。

以下是正式內容,標題未翻譯,為了方便與官方文檔相對應。

為了防止篇幅過大,該篇主要翻譯Core核心部分.

Introduction to the Spring IoC Container and Beans

本章介紹了基於控制反轉(IoC)原理的Spring框架實現。IoC也稱為依賴注入(dependency injection, DI)。在這個過程中,對象只通過構造函數參數、工廠方法的參數或對象實例在構造或從工廠方法返回后設置的屬性來定義它們的依賴關系(也就是說,它們使用的其他對象)。然后容器在創建bean時注入這些依賴項。這個過程從根本上說是bean本身的翻轉(因此稱為控制反轉),它通過使用類或服務定位器模式等機制的直接構造來控制依賴項的實例化或位置。

這里補充IoC的理解框圖

傳統應用程序示意圖

從這里我們就可以看出,為什么叫做翻轉。因為本來客戶端負責創建用戶類,而現在只需要獲取用戶類。

org.spring.framework.beansorg.springframework.context是Spring Framework IoC容器的基礎。BeanFactory接口提供了一種高級配置機制,能夠管理任何類型的對象。ApplicationContextBeanFactory的子接口。它增加了:

  • 更容易與Spring的AOP特性集成
  • 消息資源處理
  • 事件發布
  • 應用程序層特定的context,例如用於web應用程序的WebApplicationContext。

簡而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多企業特定的功能。ApplicationContext是BeanFactory的一個完整超集,在本章中專門用於描述Spring的IoC容器。有關使用BeanFactory而不是ApplicationContext的更多信息,請參閱BeanFactory。

在Spring中,構成應用程序主干並由Spring IoC容器管理的對象稱為bean。bean是由Spring IoC容器實例化、組裝和以其他方式管理的對象。另外,bean只是應用程序中的眾多對象之一。bean及其之間的依賴關系反映在容器使用的配置元數據中。

Container Overview

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

Spring提供了ApplicationContext接口的幾個實現。在獨立應用程序中,通常創建ClassPathXmlApplicationContextFileSystemXmlApplicationContext實例。雖然XML一直是定義配置元數據的傳統格式,但您可以通過提供少量XML配置來聲明支持這些額外的元數據格式,指示容器使用Java注釋或代碼作為元數據格式。

在大多數應用程序場景中,不需要顯式用戶代碼來實例化Spring IoC容器的一個或多個實例。例如,在web應用程序場景中,應用程序的web.xml文件中的8行(或大約8行)樣板web描述符XML通常就足夠了.

如果您使用Spring工具套件(一個eclipse驅動的開發環境),您可以很容易地通過單擊鼠標或擊鍵創建這個樣板配置。

下圖顯示了Spring工作原理的高級視圖。您的應用程序類與配置元數據相結合,這樣在創建和初始化ApplicationContext之后,您就有了一個完整配置的可執行系統或應用程序。

The Spring IoC container

Configuration Metadata

如上面的圖表所示,Spring IoC容器使用了一種配置元數據的形式。這個配置元數據表示您作為應用程序開發人員如何告訴Spring容器實例化、配置和組裝應用程序中的對象。

配置元數據傳統上以簡單直觀的XML格式提供,這是本章的主要內容,用於傳遞Spring IoC容器的關鍵概念和特性。

基於xml的元數據不是配置元數據的唯一允許形式。Spring IoC容器本身與實際編寫配置元數據的格式完全分離。現在,許多開發人員為他們的Spring應用程序選擇基於java的配置。

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

  • Annotation-based configuration: Spring 2.5引入了對基於注釋的配置元數據的支持。
  • Java-based configuration: 從Spring 3.0開始,Spring JavaConfig項目提供的許多特性成為核心Spring框架的一部分。因此,您可以通過使用Java而不是XML文件來定義應用程序類外部的bean。要使用這些新特性,請參閱@Configuration、@Bean、@Import和@DependsOn注釋。

Spring配置包含至少一個bean定義,通常是容器必須管理的多個bean定義。基於xml的配置元數據將這些bean配置為頂層元素中的元素。基於xml的配置元數據將這些bean配置為頂層 元素中的 元素。Java配置通常在@Configuration類中使用@ bean注釋的方法。

這些bean定義對應於組成應用程序的實際對象。通常,您可以定義服務層對象、數據訪問對象(DAOs)、表示對象(如Struts動作實例)、基礎設施對象(如Hibernate SessionFactories)、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
        http://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:i是標識單個bean定義的字符串。
  • class:定義bean的類型並使用完全限定的classname。

id屬性的值引用協作對象。本例中沒有顯示引用協作對象的XML。有關更多信息,請參見依賴關系。

Instantiating a Container

提供給ApplicationContext構造函數的位置路徑或路徑是資源字符串,允許容器裝載來自各種外部資源(如本地文件系統、Java類路徑等)的配置元數據。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

注意:在了解了Spring的IoC容器之后,您可能想了解更多關於Spring的資源抽象(如參考資料中所述)的內容,它提供了從URI語法中定義的位置讀取InputStream的方便機制。特別是,資源路徑用於構造應用程序上下文,正如在應用程序上下文和資源路徑中描述的那樣。

下面的示例顯示了服務層對象(service .xml)配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

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

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

在前面的示例中,服務層包含PetStoreServiceImpl類和JpaAccountDao和JpaItemDao類型的兩個數據訪問對象(基於JPA對象關系映射標准)。

屬性名稱元素引用JavaBean屬性的名稱,ref元素引用另一個bean定義的名稱。id和ref元素之間的鏈接表示協作對象之間的依賴關系。有關配置對象依賴項的詳細信息,請參閱依賴項。

Composing XML-based Configuration Metadata

讓bean定義跨越多個XML文件可能很有用。通常,每個XML配置文件都代表體系結構中的邏輯層或模塊。

您可以使用Application context構造函數從所有這些XML片段加載bean定義。此構造函數接受多個資源位置,如前一節所示。或者,使用一個或多個元素來從另一個或多個文件加載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定義從三個文件加載:service.xml,messageSource.xml和themeSource.xml。

所有位置路徑都相對於執行導入操作的定義文件,因此是service.xml必須與執行導入的文件位於相同的目錄或classpath位置,而messageSource.xml和themeSource.xml必須位於導入文件位置下方的resources位置。如您所見,前導斜杠被忽略。但是,考慮到這些路徑是相對的,最好不要使用斜杠。根據Spring模式,導入的文件的內容,包括頂層元素,必須是有效的XML bean定義。

可以,但不推薦,引用文件在父目錄使用一個相對"../”路徑。這樣做會對當前應用程序之外的文件創建依賴關系。特別是,不建議將此引用用於classpath: URLs(例如,classpath:../services.xml),其中運行時解析過程選擇“最近的”classpath根目錄,然后查看它的父目錄。類路徑配置更改可能導致選擇不同的、不正確的目錄。

您總是可以使用完全限定的資源位置而不是相對路徑:例如,文件:C:/config/services。xml或者類路徑:/配置/ services . xml。但是,請注意,您正在將應用程序的配置耦合到特定的絕對位置。對於這樣的絕對位置,通常最好保持間接——例如,通過在運行時針對JVM系統屬性解析的“${…}”占位符。

命名空間本身支持import directive特性。在Spring提供的XML名稱空間(例如context和util名稱空間)中,除了普通bean定義之外,還有其他配置特性。

The Groovy Bean Definition 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
        }
    }
}

這種配置樣式基本上與XML bean定義等價,甚至支持Spring的XML配置名稱空間。它還允許通過importBeans指令導入XML bean定義文件。

Using the Container

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看起來非常相似。它有一個不同的context實現類,它支持groovy-aware(但也理解XML bean定義)。下面的示例展示了Groovy配置:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

最靈活的變體是GenericApplicationContext和reader delegate相結合——例如,XmlBeanDefinitionReader用於XML文件,如下示例所示:

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

您還可以為Groovy文件使用GroovyBeanDefinitionReader,如下例所示:

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的依賴。

Bean Overview

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

在容器本身中,這些bean定義被表示為BeanDefinition對象,其中包含以下元數據(以及其他信息):

  • 包限定類名:通常是定義的bean的實際實現類。
  • Bean行為配置元素,它說明Bean在容器中的行為(范圍、生命周期回調等等)。
  • 對bean執行其工作所需的其他bean的引用。這些引用也稱為協作者或依賴關系。
  • 在新創建的對象中要設置的其他配置設置——例如,池的大小限制或管理連接池的bean中要使用的連接數。

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

Property Explained in...
Class 實例化Bean
Name 命名Beans
Scope Bean生成期
Contructor arguments 依賴注入
Properties 依賴注入
Autowiring mode 自動裝配合作者
Lazy initialization mdoe 初始化回調
Destruction method 毀壞回調

除了包含關於如何創建特定bean的信息的bean定義之外,ApplicationContext實現還允許注冊容器之外創建的現有對象(由用戶創建)。
通過getBeanFactory()方法訪問ApplicationContext的BeanFactory,該方法返回BeanFactory DefaultListableBeanFactory實現。DefaultListableBeanFactory通過registerSingleton(..)和registerBeanDefinition(..)方法支持此注冊。但是,典型的應用程序只使用通過元數據bean定義定義的bean。

Bean元數據和手動提供的單例實例需要盡早注冊,以便容器在自動連接和其他自省步驟期間正確地推斷它們。雖然在一定程度上支持覆蓋現有的元數據和現有的單例實例,但是在運行時注冊新bean(與對工廠的實時訪問同時進行)並沒有得到正式支持,並且可能導致並發訪問異常、bean容器中的不一致狀態,或者兩者都有。

Naming Beans

每個bean都有一個或多個標識符。這些標識符在承載bean的容器中必須是唯一的。bean通常只有一個標識符。但是,如果需要一個以上的別名,則可以將額外的別名看作別名。

在基於xml的配置元數據中,可以使用id屬性、name屬性或兩者來指定bean標識符。id屬性允許您指定一個id。按照慣例,這些名稱是字母數字(“myBean”、“someService”等),但它們也可以包含特殊字符。如果希望為bean引入其他別名,還可以在name屬性中指定它們,用逗號(、)、分號(;)或空格分隔。作為歷史記錄,在Spring 3.1之前的版本中,id屬性被定義為xsd: id類型,這限制了可能的字符。在3.1中,它被定義為xsd:string類型。請注意,bean id惟一性仍然由容器強制執行,但不再由XML解析器強制執行。

您不需要為bean提供名稱或id。如果沒有顯式地提供名稱或id,容器將為該bean生成唯一的名稱。但是,如果希望通過引用ref元素或服務定位器樣式查找來引用bean的名稱,則必須提供名稱。不提供名稱的動機與使用內部bean和自動連接合作者有關。

Bean 命名約定

約定是在命名bean時使用標准Java約定作為實例字段名。也就是說,bean名稱以小寫字母開頭,然后以駝色大小寫字母開頭。這些名字的例子包括accountManager,accountService, userDao, loginController, 等等。

一致地命名bean使您的配置更容易閱讀和理解。另外,如果您使用Spring AOP,那么在向一組名稱相關的bean應用建議時,它會有很大幫助。


注:通過類路徑中的組件掃描,Spring為未命名的組件生成bean名稱,遵循前面描述的規則:本質上,使用簡單的類名並將其初始字符轉換為小寫。然而,在(不尋常的)特殊情況下,當有一個以上的字符,並且第一和第二字符都是大寫字母時,原始的大小寫保留。這些規則與
java.beans.Introspector.decapitalize(此處使用Spring)定義的規則相同。

Aliasing a Bean outside the Bean Definition

在bean定義本身中,通過使用id屬性指定的最多一個名稱和name屬性中任意數量的其他名稱的組合,可以為bean提供多個名稱。這些名稱可以等效於同一bean的別名,並且在某些情況下非常有用,例如讓應用程序中的每個組件通過使用特定於該組件本身的bean名稱引用公共依賴項。

然而,指定bean實際定義的所有別名並不總是足夠的。
有時需要為其他地方定義的bean引入別名。在大型系統中,配置通常在每個子系統之間進行分配,每個子系統都有自己的一組對象定義。在基於xml的配置元數據中,可以使用元素來完成此任務。下面的例子展示了如何做到這一點:

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

在這種情況下,命名為fromName的bean(在同一個容器中)也可以在使用這個別名定義之后稱為toName。
例如,子系統A的配置元數據可以通過subsystemA-dataSource的名稱引用數據源。子系統B的配置元數據可以通過subsystemB-dataSource的名稱引用數據源。在組合使用這兩個子系統的主應用程序時,主應用程序以myApp-dataSource的名稱引用數據源。要使所有三個名稱都指向同一個對象,可以向配置元數據添加以下別名定義:

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

現在,每個組件和主應用程序都可以通過唯一的名稱引用數據源,並且保證不會與任何其他定義沖突(有效地創建名稱空間),但是它們引用的是同一個bean。

Java-Configuration

如果使用Javaconfiguration,那么可以使用@Bean注釋來提供別名。有關詳細信息,請參閱使用@Bean注釋。

Instantiating Beans

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

如果使用基於xml的配置元數據,則指定要在元素 的class屬性中實例化的對象的類型(或類)。這個類屬性(在內部是BeanDefinition實例上的類屬性)通常是強制的。(對於異常,請參閱使用實例工廠方法和Bean定義繼承進行實例化。)您可以通過以下兩種方式之一來使用類屬性:

  • 通常,在容器本身通過反射性地調用其構造函數直接創建bean的情況下,指定要構造的bean類,這在某種程度上類似於使用new操作符的Java代碼。
  • 在不太常見的情況下,在容器調用類上的靜態工廠方法來創建bean時,指定包含用於創建對象的靜態工廠方法的實際類。從靜態工廠方法調用返回的對象類型可能完全是同一個類或另一個類。

Inner class names
如果要為靜態嵌套類配置bean定義,則必須使用嵌套類的二進制名稱。

例如,如果在com.example中有一個名為SomeThing的類。這個SomeThing類有一個名為OtherThing的靜態嵌套類,bean定義上的class屬性的值應該是com.example.SomeThing$OtherThing。

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

Instantiation with a Constructor

當您使用構造函數方法創建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"/>

有關向構造函數提供參數(如果需要)和在構造對象之后設置對象實例屬性的機制的詳細信息,請參見注入依賴項。

Instantiation with a Static Factory Method

在定義使用靜態工廠方法創建的bean時,使用class屬性指定包含靜態工廠方法的類和名為factory-method的屬性來指定工廠方法本身的名稱。您應該能夠調用這個方法(使用可選參數,稍后將進行描述),並返回一個活動對象,該對象隨后將被視為通過構造函數創建的對象。這種bean定義的一種用法是在遺留代碼中調用靜態工廠。

下面的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;
    }
}

有關向工廠方法提供(可選的)參數以及在從工廠返回對象后設置對象實例屬性的機制的詳細信息,請參閱依賴項和配置的詳細信息。

Instantiation by Using an Instance Factory Method

與通過靜態工廠方法實例化類似,使用實例工廠方法實例化從容器調用現有bean的非靜態方法來創建新的bean。要使用這種機制,請保持類屬性為空,並在factory-bean屬性中指定當前(或父或祖先)容器中的bean的名稱,該容器包含要調用來創建對象的實例方法。使用工廠方法屬性設置工廠方法本身的名稱。下面的示例展示了如何配置這樣的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;
    }
}

這種方法表明,可以通過依賴注入(dependency injection)對工廠bean本身進行管理和配置。請參閱詳細的依賴關系和配置。

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

Dependencies

一個典型的企業應用程序不包含單個對象(或Spring術語中的bean)。即使是最簡單的應用程序,也有一些對象一起工作,以顯示最終用戶所看到的一致的應用程序。
下一節將解釋如何從定義獨立的許多bean定義過渡到一個完全實現的應用程序,在這個應用程序中,對象協作以實現目標。

Dependency Injection

依賴注入(Dependency injection)是一個過程,在這個過程中,對象只通過構造函數參數、工廠方法的參數或從工廠方法構造或返回的對象實例上設置的屬性來定義它們的依賴關系(也就是說,它們工作的其他對象)。然后容器在創建bean時注入這些依賴項。這個過程本質上是bean本身的翻轉(因此稱為控制翻轉),它通過使用類或服務定位器模式的直接構造來控制其依賴項的實例化或位置。

使用DI原則,代碼更簡潔,當對象具有依賴關系時,解耦更有效。對象不查找依賴項,也不知道依賴項的位置或類。因此,您的類變得更容易測試,特別是當依賴關系是在接口或抽象基類上時,這允許存根或模擬實現在單元測試中使用。

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

Constructor-based Dependency Injection

基於構造函數的DI由容器調用構造函數來完成,該構造函數有許多參數,每個參數都表示依賴關系。調用帶有特定參數的靜態工廠方法來構造bean幾乎是等價的,本文將把參數類似地處理給構造函數和靜態工廠方法。下面的例子展示了一個只能通過構造函數注入依賴注入的類:

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,不依賴於容器特定的接口、基類或注釋。

Constructor Argument Resolution

構造函數參數解析匹配通過使用參數的類型來實現。如果bean定義的構造函數參數中不存在潛在的歧義,那么在bean定義中定義構造函數參數的順序就是在實例化bean時將這些參數提供給適當的構造函數的順序。考慮以下類:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

假設ThingTwo和ThingThree類不通過繼承關聯,則不存在潛在的歧義。因此,下面的配置可以很好地工作,您不需要在元素<constructor-arg/>中顯式指定構造函數參數索引或類型。

<beans>
    <bean id="thingOne" class="x.y.ThingOne">
        <constructor-arg ref="thingTwo"/>
        <constructor-arg ref="thingThree"/>
    </bean>

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

    <bean id="thingThree" 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;
    }
}

Constructor argument type matching

在前面的場景中,如果使用type屬性顯式地指定構造函數參數的類型,容器可以使用與簡單類型匹配的類型。如下例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

Constructor argument index

可以使用index屬性顯式地指定構造函數參數的索引,如下例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

除了解決多個簡單值的模糊性之外,指定索引還可以解決構造函數具有相同類型的兩個參數的模糊性問題。

基於0的指數

Constructor argument name

您還可以使用構造函數參數名來消除值歧義,如下例所示:

<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-based Dependency Injection

在調用無參數構造函數或無參數靜態工廠方法實例化bean之后,容器調用bean上的setter方法就可以實現基於setter的DI。

下面的示例顯示了一個只能通過使用純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。
您可以以BeanDefinition的形式配置依賴關系,並將其與PropertyEditor實例一起使用,以將屬性從一種格式轉換為另一種格式。然而,大多數Spring用戶並不直接使用這些類(即通過編程方式),而是使用XML bean定義、帶注釋的組件(即用@Component、@Controller等注釋的類)或基於java的@Configuration類中的@Bean方法。然后,這些源在內部轉換為BeanDefinition實例,並用於加載整個Spring IoC容器實例。

Constructor-based or setter-based DI?

由於可以混合使用基於構造函數和基於setter的DI,因此使用構造函數處理強制依賴關系和setter方法或配置方法處理可選依賴關系是一個很好的經驗法則。注意,在setter方法上使用@Required注釋可以使屬性成為必需的依賴項。

Spring團隊通常提倡構造函數注入,因為它允許您將應用程序組件實現為不可變對象,並確保所需的依賴項不為空。此外,構造注入的組件總是以完全初始化的狀態返回給客戶端(調用)代碼。作為補充說明,大量的構造函數參數是一種糟糕的代碼味道,這意味着類可能有太多的職責,應該重構,以便更好地處理關注點的適當分離。

Setter注入主要應該只用於可選的依賴項,這些依賴項可以在類中分配合理的默認值。否則,在代碼使用依賴項的任何地方都必須執行非空檢查。setter注入的一個好處是,setter方法使該類的對象可以稍后重新配置或重新注入。因此,通過JMX MBean進行管理是setter注入的一個引人注目的用例。

用對特定類最有意義的DI樣式。有時候,在處理沒有源代碼的第三方類時,會為您做出選擇。例如,如果第三方類不公開任何setter方法,那么構造函數注入可能是惟一可用的DI形式。

Dependency Resolution Process

容器執行以下bean依賴項解析:

  • 使用描述所有bean的配置元數據創建和初始化ApplicationContext。配置元數據可以由XML、Java代碼或注釋指定。
  • 對於每個bean,其依賴關系以屬性、構造函數參數或靜態工廠方法參數的形式表示(如果使用靜態工廠方法而不是普通構造函數)。這些依賴項在bean實際創建時提供給bean。
  • 每個屬性或構造函數參數都是要設置的值的實際定義,或者是對容器中另一個bean的引用。
  • 值的每個屬性或構造函數參數都從其指定格式轉換為該屬性或構造函數參數的實際類型。默認情況下,Spring可以將字符串格式提供的值轉換為所有內置類型,如int、long、string、boolean等。

在創建容器時,Spring容器驗證每個bean的配置。然而,直到真正創建bean時,才會設置bean屬性本身。當創建容器時,將創建單實例作用域並設置為預實例化的bean(默認)。作用域在Bean作用域中定義。否則,只有在請求bean時才會創建它。創建bean可能會導致創建bean的圖,因為創建和分配了bean的依賴項及其依賴項(等等)。請注意,這些依賴項之間的解析不匹配可能出現得較晚——即在第一次創建受影響的bean時。

如果使用主構造函數注入,則可以創建不可解析的循環依賴場景。

Circular dependencies

例如:類A需要通過構造函數注入的類B實例,類B需要通過構造函數注入的類A實例。如果將類A和類B配置為相互注入的bean,那么Spring IoC容器將在運行時檢測到此循環引用,並拋出BeanCurrentlyInCreationException。

一種可能的解決方案是編輯由setter而不是構造器配置的一些類的源代碼。或者,避免構造函數注入,只使用setter注入。換句話說,盡管不建議使用setter注入配置循環依賴項。

與典型的情況(沒有循環依賴項)不同的是,Bean A和bean B之間的循環依賴項強制在完全初始化自身之前將一個bean注入另一個bean(典型的雞和蛋的場景)。

您通常可以相信Spring會做正確的事情。它在容器裝載時檢測配置問題,例如對不存在的bean和循環依賴項的引用。在實際創建bean時,Spring盡可能晚地設置屬性並解析依賴關系。這意味着,如果在創建該對象或其依賴項時出現問題,那么在以后請求對象時,正確加載的Spring容器可以生成異常——例如,bean由於丟失或無效屬性而拋出異常。某些配置問題可能會延遲可見性,這就是為什么ApplicationContext實現在默認情況下預實例化單例bean。
在實際需要這些bean之前先花一些時間和內存來創建它們,在創建ApplicationContext時(而不是稍后),您會發現配置問題。您仍然可以覆蓋這個默認行為,以便單例bean能夠惰性地初始化,而不是被預先實例化。[問題][1]

如果不存在循環依賴項,當一個或多個協作bean被注入到依賴bean中時,每個協作bean在被注入到依賴bean之前都被完全配置好了。這意味着,如果bean A依賴於Bean B,那么在調用bean A上的setter方法之前,Spring IoC容器已經完全配置了Bean B。換句話說,bean被實例化(如果它不是一個預先實例化的單例對象),它的依賴關系被設置,相關的生命周期方法(例如配置的init方法或InitializingBean回調方法)被調用。

Examples of Dependency Injection

下面的示例將基於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不是使用構造函數,而是調用靜態工廠方法來返回對象的實例:

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

靜態工廠方法的參數由元素提供,與實際使用的構造函數完全相同。工廠方法返回的類的類型不必與包含靜態工廠方法的類的類型相同(盡管在本例中是這樣)。實例(非靜態)工廠方法可以以一種基本相同的方式使用(除了使用factory-bean屬性而不是類屬性之外),因此我們在這里不討論這些細節。

在靜態工廠類中的靜態方法返回的類可以時任意類型的。

Dependencies and Configuration in Detail

如前一節所述,可以將bean屬性和構造函數參數定義為對其他托管bean(協作者)的引用或內聯定義的值。為此,Spring基於xml的配置元數據支持其 元素中的子元素類型。

Straight Values (Primitives, Strings, and so on)

元素的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-namespace來實現更簡潔的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
    http://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工具套件)。強烈建議提供這種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容器通過使用JavaBeansPropertyEditor機制將 元素轉化為java.util.Properties實例。這是一個很好的快捷方式,也是Spring團隊支持使用嵌套元素而不是value屬性樣式的少數幾個地方之一。

The idref element

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

<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是多例,那么這種類型和由此產生的異常可能在部署容器很久之后才會被發現。

4.0 bean XSD不再支持idref元素上的local屬性,因為它不再為常規bean引用提供值。升級到4.0架構時,將現有的idref local引用更改為idref bean。

元素帶來價值的一個常見地方(至少在Spring 2.0之前的版本中)是ProxyFactoryBean定義中的AOP攔截器配置。在指定攔截器名稱時使用元素可以防止對攔截器ID的拼寫錯誤。

References to Other Beans (Collaborators)

ref元素是<construct -arg/>或 定義元素中的最后一個元素。在這里,您將bean的指定屬性值設置為對容器管理的另一個bean(合作者)的引用。引用的bean是要設置其屬性的bean的依賴項,並且在設置屬性之前根據需要對其進行初始化(如果合作者是單例bean,則容器可能已經對其進行了初始化)。所有引用最終都是對另一個對象的引用。作用域和驗證取決於是否通過bean、本地屬性或父屬性指定其他對象的ID或名稱。

通過 標記的bean屬性指定目標bean是最通用的形式,允許創建對相同容器或父容器中的任何bean的引用,而不管它是否在相同的XML文件中。bean屬性的值可能與目標bean的id屬性相同,也可能與目標bean的name屬性中的值相同。下面的例子展示了如何使用ref元素:

<ref bean="someBean"/>

通過父屬性指定目標bean將創建對當前容器的父容器中的bean的引用。父屬性的值可能與目標bean的id屬性或目標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>

我們要注意如何引用的parent。

Inner Beans

元素在 元素中定義了一個內部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)中。

一種特例,可以從自定義作用域接收銷毀回調——例如,對於單例bean中包含的請求作用域的內部bean。內部bean實例的創建與其包含的bean綁定在一起,但是銷毀回調允許它參與請求范圍的生命周期。這不是一個常見的場景。內部bean通常只是共享其包含bean的作用域。

Collections

元素分別設置Java集合類型列表、集合、映射和屬性的屬性和參數。下面的例子展示了如何使用它們:

<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 

Collection Merging

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

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

下面的例子演示了集合合並:

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

注意,在子bean定義的adminemail屬性的元素上使用merge=true屬性。當容器解析並實例化子bean時,生成的實例具有adminEmail屬性集合,其中包含將子bean的adminemail集合與父組件的adminemail集合合並的結果。下面的清單顯示了結果:

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

子屬性集合的值集繼承了來自父元素 的所有屬性元素,而子元素的支持值覆蓋了父集合中的值。
這種合並行為同樣適用於 集合類型。在 元素的特定情況下,將維護與列表集合類型(即有序值集合的概念)關聯的語義。父列表的值在所有子列表的值之前.對於映射、集合和屬性集合類型,不存在排序。因此,對於位於容器內部使用的關聯映射、集合和屬性實現類型之下的集合類型,沒有任何排序語義。

Limitations of Collection Merging

不能合並不同的集合類型(例如映射和列表)。如果您確實試圖這樣做,則會拋出一個適當的異常。merge屬性必須在較低的繼承子定義上指定。在父集合定義上指定merge屬性是多余的,不會導致所需的合並。

Strongly-typed collection

隨着Java 5中泛型類型的引入,您可以使用強類型集合。也就是說,可以聲明一個集合類型,使其只能包含(例如)字符串元素。如果使用Spring依賴於將強類型集合注入bean,則可以利用Spring的類型轉換支持,以便在添加到集合之前將強類型集合實例的元素轉換為適當的類型。下面的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屬性時,強類型映射的元素類型的泛型信息可以通過反射得到。因此,Spring的類型轉換基礎結構將各種值元素識別為浮點類型,並將字符串值(9.99、2.75和3.99)轉換為實際的浮點類型。

Null and Empty String Values

Spring將屬性等的空參數視為空字符串。以下基於xml的配置元數據片段將email屬性設置為空字符串值("")。

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

上述示例相當於以下Java代碼:

exampleBean.setEmail("");

元素 處理空值。下面的清單顯示了一個示例:

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

上述示例相當於以下Java代碼:

exampleBean.setEmail(null);

使用p-namespace的XML快捷方式

[1]: 為什么要選擇用單例: https://docs.spring.io/spring/docs/5.1.1.RELEASE/spring-framework-reference/core.html#beans-dependency-resolution


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM