SpringCore筆記

1 IoC容器

1.1 SpringIOC容器和Bean介紹

對(duì)象僅通過構(gòu)造函數(shù)參數(shù)描滔、工廠方法的參數(shù)或在對(duì)象實(shí)例被構(gòu)造或從工廠方法返回后在對(duì)象實(shí)例上設(shè)置的屬性來定義它們的依賴項(xiàng)(即,它們使用的其他對(duì)象) . 然后容器在創(chuàng)建 bean 時(shí)注入這些依賴項(xiàng)宴偿。這個(gè)過程基本上是 bean 本身的逆過程(因此得名,控制反轉(zhuǎn))
org.springframework.beans和org.springframework.context包是Spring框架的IoC容器的基礎(chǔ)诀豁。BeanFactory接口提供了一種能夠管理任何類型對(duì)象的高級(jí)配置機(jī)制酪我。 ApplicationContext是BeanFactory的子接口。ApplicationContext的補(bǔ)充說明:

  • 更容易與 Spring 的 AOP 特性集成
  • 消息資源處理(用于國際化)
  • 事件發(fā)布
  • 應(yīng)用層特定的上下文且叁,例如WebApplicationContext 在 Web 應(yīng)用程序中使用都哭。

BeanFactory提供了配置框架和基本功能,ApplicationContext 添加了更多企業(yè)特定的功能逞带。ApplicationContext是BeanFactory的一個(gè)完整的超集欺矫,僅在本章描述Spring的IoC容器時(shí)使用。關(guān)于更多使用BeanFactory 的信息看BeanFactory 而不是ApplicationContext展氓。

1.2 容器概述

org.springframework.context.ApplicationContext接口代表 Spring IoC 容器穆趴,負(fù)責(zé)實(shí)例化、配置和組裝 bean遇汞。容器通過讀取配置元數(shù)據(jù)來獲取有關(guān)要實(shí)例化未妹、配置和組裝哪些對(duì)象的指令。配置元數(shù)據(jù)以 XML空入、Java 注解或 Java 代碼表示络它。它可以讓您組成應(yīng)用程序的表達(dá)對(duì)象以及這些對(duì)象之間豐富的相互依賴關(guān)系。
Springt提供了ApplicationContext幾個(gè)實(shí)現(xiàn)歪赢,通常創(chuàng)建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的實(shí)例化戳。雖然 XML 一直是定義配置元數(shù)據(jù)的傳統(tǒng)格式,但您可以通過提供少量 XML 配置來聲明性地啟用對(duì)這些附加元數(shù)據(jù)格式的支持埋凯,從而指示容器使用 Java 注釋或代碼作為元數(shù)據(jù)格式点楼。

在大多數(shù)應(yīng)用場(chǎng)景,不需要的顯式的代碼實(shí)例化一個(gè)或多個(gè)SpringIOC容器白对。比如說掠廓,在web應(yīng)用場(chǎng)景,應(yīng)用程序文件中的web.xml簡(jiǎn)單八行(左右)通常就足夠了(請(qǐng)參閱Web 應(yīng)用程序的便捷 ApplicationContext 實(shí)例化)甩恼。如果您使用 Spring Tools for Eclipse(一個(gè) Eclipse 驅(qū)動(dòng)的開發(fā)環(huán)境)蟀瞧,您可以通過點(diǎn)擊幾下鼠標(biāo)或按鍵輕松創(chuàng)建這個(gè)樣板配置狰域。

下圖顯示了 Spring 如何工作的高級(jí)視圖。您的應(yīng)用程序類與配置元數(shù)據(jù)相結(jié)合黄橘,在ApplicationContext創(chuàng)建和初始化后,您就有了一個(gè)完全配置且可執(zhí)行的系統(tǒng)或應(yīng)用程序屈溉。


image.png

圖 1. Spring IoC 容器

1.2.1. 配置元數(shù)據(jù)

如上圖所示塞关,Spring IoC 容器使用一種形式的配置元數(shù)據(jù)。此配置元數(shù)據(jù)表示您作為應(yīng)用程序開發(fā)人員如何告訴 Spring 容器實(shí)例化子巾、配置和組裝應(yīng)用程序中的對(duì)象帆赢。

配置元數(shù)據(jù)傳統(tǒng)上提供以簡(jiǎn)單直觀的 XML 格式,本章的大部分內(nèi)容使用這種格式來傳達(dá) Spring IoC 容器的關(guān)鍵概念和特性线梗。

基于 XML 的元數(shù)據(jù)并不是唯一允許的配置元數(shù)據(jù)形式椰于。Spring IoC 容器本身與實(shí)際寫入此配置元數(shù)據(jù)的格式完全分離。現(xiàn)在仪搔,許多開發(fā)人員為他們的 Spring 應(yīng)用程序選擇 基于 Java 的配置瘾婿。

有關(guān)在 Spring 容器中使用其他形式的元數(shù)據(jù)的信息,請(qǐng)參閱:

  • 基于注解的配置:Spring 2.5 引入了對(duì)基于注解的配置元數(shù)據(jù)的支持烤咧。
  • 基于 Java 的配置:從 Spring 3.0 開始偏陪,Spring JavaConfig 項(xiàng)目提供的許多特性成為 Spring Framework Core的一部分。因此煮嫌,您可以使用 Java 而不是 XML 文件來定義應(yīng)用程序類外部的 bean笛谦。要使用這些新功能,請(qǐng)參閱 @Configuration昌阿, @Bean饥脑, @Import,和@DependsOn注解懦冰。

Spring配置容器管理必須由至少一個(gè)(通常是多個(gè))bean定義組成灶轰。基于 XML 的配置元數(shù)據(jù)將這些 bean 配置為頂級(jí)<beans/>元素中的<bean/>元素刷钢。Java 配置通常在@Configuration類中使用@Bean注解的方法框往。

這些 bean 定義對(duì)應(yīng)于構(gòu)成應(yīng)用程序的實(shí)際對(duì)象。通常闯捎,您定義服務(wù)層對(duì)象椰弊、數(shù)據(jù)訪問對(duì)象 (DAO)、表示對(duì)象(例如 StrutsAction實(shí)例)瓤鼻、基礎(chǔ)設(shè)施對(duì)象(例如 Hibernate SessionFactories秉版、JMSQueues等)。通常茬祷,不會(huì)在容器中配置細(xì)粒度的域?qū)ο笄寤溃驗(yàn)閯?chuàng)建和加載域?qū)ο笸ǔJ?DAO 和業(yè)務(wù)邏輯的責(zé)任。但是,您可以使用 Spring 與 AspectJ 的集成來配置在 IoC 容器控制之外創(chuàng)建的對(duì)象秸妥。請(qǐng)參閱:使用 AspectJ 通過 Spring 依賴注入域?qū)ο蟆?/p>

以下示例顯示了基于 XML 的配置元數(shù)據(jù)的基本結(jié)構(gòu):

<?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屬性是一個(gè)字符串滚停,用于標(biāo)識(shí)單個(gè) bean 定義。
  • 該class屬性定義 bean 的類型并使用完全限定的類名粥惧。

該id屬性的值是指協(xié)作對(duì)象键畴。此示例中未顯示用于引用協(xié)作對(duì)象的 XML。有關(guān)更多信息突雪,請(qǐng)參閱依賴項(xiàng)起惕。

1.2.2. 實(shí)例化容器

提供給ApplicationContext構(gòu)造函數(shù)的一個(gè)或多個(gè)位置路徑是資源字符串,允許容器從各種外部資源(例如本地文件系統(tǒng)咏删、Java 等)加載配置元數(shù)據(jù)CLASSPATH惹想。

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

在了解了 Spring 的 IoC 容器之后,您可能想了解更多關(guān)于 Spring 的Resource抽象(如參考資料中所述)督函,它提供了一種從 URI 語法中定義的位置讀取 InputStream 的便捷機(jī)制嘀粱。特別是, Resource路徑用于構(gòu)造應(yīng)用程序上下文辰狡,如應(yīng)用程序上下文和資源路徑中所述草穆。
以下示例顯示了服務(wù)層對(duì)象(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>

以下示例顯示了數(shù)據(jù)訪問對(duì)象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>

在前面的示例中,服務(wù)層由PetStoreServiceImpl類和兩個(gè)類型JpaAccountDaoJpaItemDao(基于 JPA 對(duì)象-關(guān)系映射標(biāo)準(zhǔn))的數(shù)據(jù)訪問對(duì)象組成搓译。該property name元素是指JavaBean屬性的名稱悲柱,以及ref元素指的是另一個(gè)bean定義的名稱。idref元素之間的這種聯(lián)系表達(dá)了協(xié)作對(duì)象之間的依賴關(guān)系些己。有關(guān)配置對(duì)象依賴項(xiàng)的詳細(xì)信息豌鸡,請(qǐng)參閱 依賴項(xiàng)。

編寫基于 XML 的配置元數(shù)據(jù)

跨越多個(gè) XML 文件定義bean會(huì)很有用段标。通常涯冠,每個(gè)單獨(dú)的 XML 配置文件都代表您架構(gòu)中的一個(gè)邏輯層或模塊。

您可以使用應(yīng)用程序上下文構(gòu)造函數(shù)從這些 XML 片段加載所有bean 定義逼庞。該構(gòu)造函數(shù)采用多個(gè)Resource位置蛇更,如上一節(jié)所示 ∪悖或者派任,使用一個(gè)或多個(gè)<import/>元素從另一個(gè)文件或多個(gè)文件加載 bean 定義。以下示例顯示了如何執(zhí)行此操作:

<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定義是從三個(gè)文件加載: services.xml掌逛,messageSource.xml,和themeSource.xml司倚。所有位置路徑都相對(duì)于執(zhí)行導(dǎo)入的定義文件豆混,因此services.xml必須與執(zhí)行導(dǎo)入的文件位于同一目錄或類路徑位置篓像, messageSource.xml并且themeSource.xml必須位于resources導(dǎo)入文件所在位置下方的位置。如您所見皿伺,前導(dǎo)斜杠被忽略员辩。然而,鑒于這些路徑是相對(duì)的鸵鸥,最好根本不使用斜杠奠滑。根據(jù) Spring Schema,被導(dǎo)入文件的內(nèi)容包含在頂級(jí)元素<beans/>脂男,必須是有效的 XML bean 定義。

可以但不建議使用相對(duì)“../”路徑引用父目錄中的文件种呐。這樣做會(huì)創(chuàng)建對(duì)當(dāng)前應(yīng)用程序之外的文件的依賴關(guān)系宰翅。特別是,不建議將此引用用于classpath:URL(例如爽室,classpath:../services.xml)汁讼,其中運(yùn)行時(shí)解析過程選擇“最近的”類路徑根,然后查看其父目錄阔墩。類路徑配置更改可能會(huì)導(dǎo)致選擇不同的嘿架、不正確的目錄。

您始終可以使用完全限定的資源位置而不是相對(duì)路徑:例如啸箫,file:C:/config/services.xml或classpath:/config/services.xml耸彪。但是,請(qǐng)注意您將應(yīng)用程序的配置耦合到特定的絕對(duì)位置忘苛。通常最好為這樣的絕對(duì)位置保留一個(gè)間接地址——例如蝉娜,通過在運(yùn)行時(shí)根據(jù) JVM 系統(tǒng)屬性解析的“${… }”占位符。

命名空間本身提供了導(dǎo)入指令功能扎唾。Spring 提供的一系列 XML 命名空間中提供了超出普通 bean 定義的更多配置功能——例如召川,context和util命名空間。

Groovy Bean 定義 DSL

作為外部化配置元數(shù)據(jù)的另一個(gè)示例胸遇,bean 定義也可以在 Spring 的 Groovy Bean Definition DSL 中表達(dá)荧呐,正如 Grails 框架中所知。通常纸镊,此類配置位于“.groovy”文件中倍阐,其結(jié)構(gòu)如下例所示:

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

這種配置風(fēng)格在很大程度上等同于 XML bean 定義,甚至支持 Spring 的 XML 配置命名空間逗威。它還允許通過importBeans指令導(dǎo)入 XML bean 定義文件收捣。

1.2.3. 使用容器

這ApplicationContext是一個(gè)高級(jí)工廠的接口,能夠維護(hù)不同 bean 及其依賴項(xiàng)的注冊(cè)表庵楷。通過使用 方法 T getBean(String name, Class<T> requiredType)罢艾,您可以檢索 bean 的實(shí)例楣颠。

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 配置咐蚯,引導(dǎo)看起來非常相似童漩。它有一個(gè)不同的上下文實(shí)現(xiàn)類,它是 Groovy 感知的(但也理解 XML bean 定義)春锋。以下示例顯示了 Groovy 配置:

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

最靈活的變體是GenericApplicationContext結(jié)合讀者委托——例如矫膨,XmlBeanDefinitionReaderfor結(jié)合XML 文件,如下例所示:

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

您還可以使用GroovyBeanDefinitionReader結(jié)合 Groovy 文件期奔,如以下示例所示:

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

您可以在同一個(gè)ApplicationContext上混合和匹配此類讀取器委托侧馅,從不同的配置源讀取 bean 定義。

您可以使用它getBean來檢索 bean 的實(shí)例。該ApplicationContext 接口有一些其他方法來檢索 bean,但理想情況下输吏,您的應(yīng)用程序代碼應(yīng)該永遠(yuǎn)不使用它們筹误。實(shí)際上,您的應(yīng)用程序代碼根本不應(yīng)該調(diào)用該 getBean()方法,因此完全不依賴于 Spring API。例如,Spring 與 Web 框架的集成為各種 Web 框架組件(例如控制器和 JSF 管理的 bean)提供了依賴注入小渊,讓您可以通過元數(shù)據(jù)(例如自動(dòng)裝配注解)聲明對(duì)特定 bean 的依賴。

1.3. Bean概述

Spring IoC 容器管理一個(gè)或多個(gè) bean茫叭。這些 bean 是使用您提供給容器的配置元數(shù)據(jù)創(chuàng)建的(例如酬屉,以 XML<bean/>定義的形式 )。

在容器本身內(nèi)揍愁,這些 bean 定義表示為BeanDefinition 對(duì)象梆惯,其中包含(除其他信息外)以下元數(shù)據(jù):

  • 包限定的類名:通常是定義的 bean 的實(shí)際實(shí)現(xiàn)類。
  • Bean 行為配置元素吗垮,它說明 Bean 在容器中的行為方式(范圍垛吗、生命周期回調(diào)等)。
  • 對(duì) bean 執(zhí)行其工作所需的其他 bean 的引用烁登。這些引用也稱為協(xié)作者或依賴項(xiàng)怯屉。
  • 新創(chuàng)建的對(duì)象中設(shè)置的其他配置設(shè)置——例如,池的大小限制或在管理連接池的 bean 中使用的連接數(shù)饵沧。

此元數(shù)據(jù)轉(zhuǎn)換為組成每個(gè) bean 定義的一組屬性锨络。下表描述了這些屬性:

表 1. bean 定義

屬性 解釋
Class 實(shí)例化bean
Name bean的名字
Scope bean的作用域
Constructor arguments 依賴注入
Properties 依賴注入
Autowiring mode 自動(dòng)裝配合作者
Lazy initialization mode 延遲初始化的bean
Initialization method 初始化回調(diào)
Destruction method 銷毀回調(diào)

除了包含有關(guān)如何創(chuàng)建特定 bean 的信息的 bean 定義之外,ApplicationContext實(shí)現(xiàn)還允許注冊(cè)在容器外(由用戶)創(chuàng)建的現(xiàn)有對(duì)象狼牺。這是通過getBeanFactory()方法返回 BeanFactoryDefaultListableBeanFactory實(shí)現(xiàn)訪問 ApplicationContext 的 BeanFactory 來完成的羡儿。DefaultListableBeanFactory 通過registerSingleton(..)和 registerBeanDefinition(..)方法支持此注冊(cè)。但是是钥,通常應(yīng)用程序僅使用通過常規(guī) bean 定義元數(shù)據(jù)定義的 bean掠归。

Bean 元數(shù)據(jù)和手動(dòng)提供的單例實(shí)例需要盡早注冊(cè)缅叠,以便容器在自動(dòng)裝配和其他內(nèi)省步驟中正確推理它們。雖然在某種程度上支持覆蓋現(xiàn)有元數(shù)據(jù)和現(xiàn)有單例實(shí)例虏冻,但官方不支持在運(yùn)行時(shí)注冊(cè)新 bean(與實(shí)時(shí)訪問工廠同時(shí))肤粱,并可能導(dǎo)致并發(fā)訪問異常、bean 容器中的狀態(tài)不一致厨相,或兩個(gè)都领曼。

1.3.1. Bean命名

每個(gè) bean 都有一個(gè)或多個(gè)標(biāo)識(shí)符。這些標(biāo)識(shí)符在承載 bean 的容器中必須是唯一的蛮穿。一個(gè) bean 通常只有一個(gè)標(biāo)識(shí)符庶骄。但是,如果它需要多個(gè)践磅,則可以將多余的視為別名单刁。

在基于 XML 的配置元數(shù)據(jù)中,您可以使用id屬性音诈、name屬性或兩者來指定 bean 標(biāo)識(shí)符幻碱。通常绎狭,這些名稱是字母數(shù)字('myBean'细溅、'someService' 等),但它們也可以包含特殊字符儡嘶。如果要為 bean 引入其他別名喇聊,也可以在name 屬性中指定它們,用逗號(hào) ( ,)蹦狂、分號(hào) ( ;) 或空格分隔誓篱。作為歷史記錄,在 Spring 3.1 之前的版本中凯楔,id屬性被定義為一種xsd:ID類型窜骄,它限制了可能的字符。從 3.1 開始摆屯,它被定義為一種xsd:string類型邻遏。請(qǐng)注意,bean 的id唯一性仍然由容器強(qiáng)制執(zhí)行虐骑,但不再由 XML 解析器強(qiáng)制執(zhí)行准验。

您不需要為 bean提供 aname或 an id。如果您沒有明確提供 a name或id廷没,則容器會(huì)為該 bean 生成一個(gè)唯一的名稱糊饱。但是,如果您想通過名稱引用該 bean颠黎,通過使用ref元素或服務(wù)定位器樣式查找另锋,您必須提供名稱滞项。不提供名稱的動(dòng)機(jī)與使用內(nèi)部 bean和自動(dòng)裝配協(xié)作者有關(guān)。

Bean 命名約定
約定是在命名bean時(shí)使用標(biāo)準(zhǔn)Java約定對(duì)實(shí)例字段名稱砰蠢。bean 名稱以小寫字母開頭蓖扑,并從那里開始使用駝峰式大小寫。此類名稱的示例包括accountManager台舱、 accountService律杠、userDao、loginController等等竞惋。

始終如一地命名 bean 使您的配置更易于閱讀和理解柜去。此外,如果您使用 Spring AOP拆宛,則在將建議應(yīng)用于一組按名稱相關(guān)的 bean 時(shí)會(huì)很有幫助嗓奢。

通過類路徑中的組件掃描,Spring 為未命名的組件生成 bean 名稱浑厚,遵循前面描述的規(guī)則:本質(zhì)上股耽,采用簡(jiǎn)單的類名并將其初始字符轉(zhuǎn)換為小寫。但是钳幅,在有多個(gè)字符且第一個(gè)和第二個(gè)字符都是大寫的(不尋常的)特殊情況下物蝙,原始大小寫被保留。這些與定義的規(guī)則相同java.beans.Introspector.decapitalize(Spring 在這里使用)敢艰。

在 Bean 定義之外給 Bean 取別名

在 bean 定義本身中诬乞,您可以通過使用id屬性指定的最多一個(gè)名稱和屬性中任意數(shù)量的其他名稱的組合,為 bean 提供多個(gè)名稱name钠导。這些名稱等同于同一個(gè) bean 的別名震嫉,并且在某些情況下很有用,例如讓應(yīng)用程序中的每個(gè)組件通過使用特定于該組件本身的 bean 名稱來引用公共依賴項(xiàng)牡属。

然而票堵,在實(shí)際定義 bean 的地方指定所有別名并不總是足夠的。有時(shí)需要為在別處引入定義的 bean 別名逮栅。這在大型系統(tǒng)中很常見悴势,其中配置在每個(gè)子系統(tǒng)之間拆分,每個(gè)子系統(tǒng)都有自己的一組對(duì)象定義证芭。在基于 XML 的配置元數(shù)據(jù)中瞳浦,您可以使用<alias/>元素來完成此操作。以下示例顯示了如何執(zhí)行此操作:

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

在這種情況下废士,命名的 bean(在同一容器中)fromName也可以在使用此別名定義后稱為toName叫潦。

例如,子系統(tǒng) A 的配置元數(shù)據(jù)可能引用名為 的數(shù)據(jù)源subsystemA-dataSource官硝。子系統(tǒng) B 的配置元數(shù)據(jù)可以通過名稱來引用數(shù)據(jù)源subsystemB-dataSource矗蕊。在組合使用這兩個(gè)子系統(tǒng)的主應(yīng)用程序時(shí)短蜕,主應(yīng)用程序通過名稱引用 DataSource myApp-dataSource。要讓所有三個(gè)名稱都引用同一個(gè)對(duì)象傻咖,您可以將以下別名定義添加到配置元數(shù)據(jù)中:

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

現(xiàn)在朋魔,每個(gè)組件和主應(yīng)用程序都可以通過一個(gè)唯一的名稱來引用 dataSource,并且保證不會(huì)與任何其他定義發(fā)生沖突(有效地創(chuàng)建一個(gè)命名空間)卿操,但它們引用的是同一個(gè) bean警检。

java配置
如果您使用 Javaconfiguration,該@Bean注解可用于提供別名害淤。有關(guān)詳細(xì)信息扇雕,請(qǐng)參閱使用@Bean注釋。

1.3.2.實(shí)例化Bean

bean 定義本質(zhì)上是創(chuàng)建一個(gè)或多個(gè)對(duì)象的方法窥摄。當(dāng)被詢問時(shí)镶奉,容器會(huì)查看命名 bean 的方法,并使用該 bean 定義封裝的配置元數(shù)據(jù)來創(chuàng)建(或獲日阜拧)實(shí)際對(duì)象哨苛。

如果使用基于 XML 的配置元數(shù)據(jù),則指定要在元素的class屬性中實(shí)例化的對(duì)象的類型(或類)<bean/>币砂。這個(gè) class屬性(在內(nèi)部建峭,它是Class一個(gè)BeanDefinition 實(shí)例的屬性)通常是強(qiáng)制性的。(對(duì)于例外情況道伟,請(qǐng)參閱 使用實(shí)例工廠方法和Bean 定義繼承迹缀。)您可以通過以下兩種方式之一使用Class屬性:

  • 通常使碾,在容器本身通過反射調(diào)用其構(gòu)造函數(shù)直接創(chuàng)建 bean 的情況下蜜徽,指定要構(gòu)造的 bean 類,有點(diǎn)等同于Java 代碼new運(yùn)算符
  • 指定包含static被調(diào)用以創(chuàng)建對(duì)象的工廠方法的實(shí)際類票摇,在不太常見的情況下拘鞋,容器調(diào)用 static類上的工廠方法來創(chuàng)建 bean。調(diào)用static工廠方法返回的對(duì)象類型可能是同一個(gè)類矢门,也可能完全是另一個(gè)類盆色。

嵌套類名
如果要為嵌套類配置 bean 定義,可以使用嵌套類的二進(jìn)制名稱或源名稱祟剔。例如隔躲,如果您有一個(gè)類SomeThing在com.example包中,并且SomeThing該類有一個(gè)static名為OtherThing的嵌套類物延,則它們之間可以用美元符號(hào) () 或點(diǎn) ( .)分隔宣旱。因此bean 中定義class的屬性值將是com.example.SomeThingOtherThing或者是 com.example.SomeThing.OtherThing。

使用構(gòu)造函數(shù)實(shí)例化

當(dāng)您通過構(gòu)造函數(shù)方法創(chuàng)建 bean 時(shí)叛薯,所有普通類都可以被 Spring 使用并與 Spring 兼容浑吟。也就是說笙纤,正在開發(fā)的類不需要實(shí)現(xiàn)任何特定的接口或以特定的方式進(jìn)行編碼。但是组力,根據(jù)您對(duì)該特定 bean 使用的 IoC 類型省容,您可能需要一個(gè)默認(rèn)(空)構(gòu)造函數(shù)。

Spring IoC 容器幾乎可以管理您希望它管理的任何類燎字。它不僅限于管理真正的 JavaBean腥椒。大多數(shù) Spring 用戶更喜歡實(shí)際的 JavaBeans,它只有一個(gè)默認(rèn)(無參數(shù))構(gòu)造函數(shù)和適當(dāng)?shù)?setter 和 getter候衍,它們以容器中的屬性為模型寞酿。您還可以在您的容器中擁有更多異國情調(diào)的非 bean 風(fēng)格的類。例如脱柱,如果您需要使用絕對(duì)不符合 JavaBean 規(guī)范的遺留連接池伐弹,Spring 也可以管理它。

使用基于 XML 的配置元數(shù)據(jù)榨为,您可以按如下方式指定 bean 類:

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

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

有關(guān)向構(gòu)造函數(shù)提供參數(shù)(如果需要)和在構(gòu)造對(duì)象后設(shè)置對(duì)象實(shí)例屬性的機(jī)制的詳細(xì)信息惨好,請(qǐng)參閱注入依賴項(xiàng)。

使用靜態(tài)工廠方法實(shí)例化

在定義使用靜態(tài)工廠方法創(chuàng)建的 bean 時(shí)随闺,使用class 屬性來指定包含static工廠方法的類和命名factory-method為指定工廠方法本身名稱的屬性日川。您應(yīng)該能夠調(diào)用此方法(帶有可選參數(shù),如下所述)并返回一個(gè)活動(dòng)對(duì)象矩乐,隨后將其視為通過構(gòu)造函數(shù)創(chuàng)建的龄句。這種 bean 定義的一種用途是static在遺留代碼中調(diào)用工廠。

以下 bean 定義指定通過調(diào)用工廠方法來創(chuàng)建 bean散罕。定義中沒有指定返回對(duì)象的類型(類)分歇,只指定包含工廠方法的類。在這個(gè)例子中欧漱,createInstance() 方法必須是靜態(tài)方法职抡。以下示例顯示了如何指定工廠方法:

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

以下示例顯示了一個(gè)可以與前面的 bean 定義一起使用的類:

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

有關(guān)在工廠工廠方法提供(可選)參數(shù)和設(shè)置對(duì)象實(shí)例屬性后返回對(duì)象后的機(jī)制的詳細(xì)信息,請(qǐng)參閱詳細(xì)依賴項(xiàng)和配置误甚。

使用實(shí)例工廠方法實(shí)例化

與通過靜態(tài)工廠方法實(shí)例化類似缚甩,使用實(shí)例工廠方法實(shí)例化從容器中調(diào)用現(xiàn)有 bean 的非靜態(tài)方法來創(chuàng)建新 bean。要使用此機(jī)制窑邦,請(qǐng)將class屬性留空擅威,并在factory-bean屬性中指定當(dāng)前(或父或祖先)容器中 bean 的名稱,該容器包含要調(diào)用以創(chuàng)建對(duì)象的實(shí)例方法冈钦。使用factory-method屬性設(shè)置工廠方法本身的名稱郊丛。以下示例顯示了如何配置此類 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"/>

以下示例顯示了相應(yīng)的類

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一個(gè)工廠類也可以包含多個(gè)工廠方法,如下例所示:

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

以下示例顯示了相應(yīng)的類

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) 進(jìn)行管理和配置。請(qǐng)參閱詳細(xì)的依賴關(guān)系和配置宾袜。

在 Spring 文檔中捻艳,“工廠 bean”是指在 Spring 容器中配置并通過實(shí)例或靜態(tài)工廠方法創(chuàng)建對(duì)象的 bean 。相比之下庆猫, FactoryBean(注意大寫)是指特定于 Spring 的FactoryBean實(shí)現(xiàn)類认轨。

確定Bean運(yùn)行時(shí)的類型

確定特定 bean 的運(yùn)行時(shí)類型并非易事。bean 元數(shù)據(jù)定義中的指定類只是一個(gè)初始類引用月培,可能與聲明的工廠方法相結(jié)合嘁字,或者是FactoryBean可能導(dǎo)致 bean 的不同運(yùn)行時(shí)類型的類,或者在實(shí)例的情況下根本沒有設(shè)置 -級(jí)別工廠方法(通過指定factory-bean名稱解析)杉畜。此外纪蜒,AOP 代理可以使用基于接口的代理包裝 bean 實(shí)例,并限制暴露目標(biāo) bean 的實(shí)際類型(僅其實(shí)現(xiàn)的接口)此叠。

找出特定 bean 的實(shí)際運(yùn)行時(shí)類型的推薦方法是使用指定的 bean 名稱調(diào)用BeanFactory.getType纯续。這考慮了上述所有情況,并返回和調(diào)用BeanFactory.getBean返回的對(duì)象類型相同 bean 名稱灭袁。

1.4. 依賴關(guān)系

典型的企業(yè)應(yīng)用程序不包含單個(gè)對(duì)象(或 Spring 用語中的 bean)猬错。即使是最簡(jiǎn)單的應(yīng)用程序也有一些對(duì)象,它們協(xié)同工作以呈現(xiàn)最終用戶所看到的連貫應(yīng)用程序茸歧。下一節(jié)將解釋如何從定義多個(gè)獨(dú)立的 bean 定義到完全實(shí)現(xiàn)的應(yīng)用程序倦炒,其中對(duì)象協(xié)作以實(shí)現(xiàn)目標(biāo)。

1.4.1. 依賴注入

依賴注入 (DI) 是一個(gè)過程软瞎,其中對(duì)象僅通過構(gòu)造函數(shù)參數(shù)逢唤、工廠方法的參數(shù)或在對(duì)象實(shí)例被構(gòu)造或從工廠方法返回。然后容器在創(chuàng)建 bean 時(shí)注入這些依賴項(xiàng)涤浇。這個(gè)過程基本上是 bean 本身的逆過程(因此得名鳖藕,控制反轉(zhuǎn)),通過使用類的直接構(gòu)造或服務(wù)定位器模式自行控制其依賴項(xiàng)的實(shí)例化或位置芙代。

DI 原則使代碼更清晰吊奢,當(dāng)對(duì)象提供依賴關(guān)系時(shí)盖彭,解耦更有效纹烹。該對(duì)象不查找其依賴項(xiàng),也不知道依賴項(xiàng)的位置或類召边。特別是當(dāng)依賴項(xiàng)位于接口或抽象基類上時(shí)铺呵,這允許在單元測(cè)試中使用存根或模擬實(shí)現(xiàn)。

DI 存在兩種主要變體:基于構(gòu)造函數(shù)的依賴注入和基于 Setter 的依賴注入隧熙。

基于構(gòu)造函數(shù)的依賴注入

基于構(gòu)造函數(shù)的 DI 是通過容器調(diào)用具有多個(gè)參數(shù)的構(gòu)造函數(shù)來完成的片挂,每個(gè)參數(shù)代表一個(gè)依賴項(xiàng)。調(diào)用static帶有特定參數(shù)的工廠方法來構(gòu)造 bean 幾乎是等效的,本討論將static類似地處理構(gòu)造函數(shù)和工廠方法的參數(shù)音念。以下示例顯示了一個(gè)只能使用構(gòu)造函數(shù)注入進(jìn)行依賴注入的類:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private final 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...
}

請(qǐng)注意沪饺,這個(gè)類沒有什么特別之處。它是一個(gè)不依賴于容器特定接口闷愤、基類或注解的 POJO整葡。

構(gòu)造函數(shù)參數(shù)解析

構(gòu)造函數(shù)參數(shù)解析匹配通過使用參數(shù)的類型發(fā)生。如果 bean 定義的構(gòu)造函數(shù)參數(shù)中不存在潛在的歧義讥脐,那么在 bean 定義中定義構(gòu)造函數(shù)參數(shù)的順序就是在實(shí)例化 bean 時(shí)將這些參數(shù)提供給適當(dāng)?shù)臉?gòu)造函數(shù)的順序遭居。考慮以下類:

package x.y;

public class ThingOne {

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

假設(shè)ThingTwo和ThingThree類不通過繼承相關(guān)旬渠,則不存在潛在的歧義俱萍。因此,以下配置工作正常告丢,您不需要在<constructor-arg/>元素中顯式指定構(gòu)造函數(shù)參數(shù)索引或類型 枪蘑。

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

當(dāng)另一個(gè) bean 被引用時(shí),類型是已知的岖免,并且可以發(fā)生匹配(就像前面的例子一樣)腥寇。當(dāng)使用簡(jiǎn)單類型時(shí),例如 <value>true</value>觅捆,Spring 無法確定值的類型赦役,因此無法在沒有幫助的情況下按類型進(jìn)行匹配≌こ矗考慮以下類:

package examples;

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

構(gòu)造函數(shù)參數(shù)類型匹配

在上述場(chǎng)景中掂摔,如果您通過type屬性顯式指定構(gòu)造函數(shù)參數(shù)的類型,容器可以使用簡(jiǎn)單類型的類型匹配赢赊,如下例所示:

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

構(gòu)造函數(shù)參數(shù)索引

您可以使用該index屬性顯式指定構(gòu)造函數(shù)參數(shù)的索引乙漓,如以下示例所示:

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

除了解決多個(gè)簡(jiǎn)單值的歧義之外,指定索引還可以解決構(gòu)造函數(shù)具有兩個(gè)相同類型參數(shù)的歧義释移。

構(gòu)造函數(shù)參數(shù)名稱

您還可以使用構(gòu)造函數(shù)參數(shù)名稱進(jìn)行值消歧叭披,如以下示例所示:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

請(qǐng)記住,要使這項(xiàng)工作開箱即用玩讳,您的代碼必須在啟用調(diào)試標(biāo)志的情況下進(jìn)行編譯涩蜘,以便 Spring 可以從構(gòu)造函數(shù)中查找參數(shù)名稱。如果您不能或不想使用調(diào)試標(biāo)志編譯代碼熏纯,則可以使用 @ConstructorProperties JDK 注釋顯式命名構(gòu)造函數(shù)參數(shù)同诫。示例類必須如下所示:

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 是通過容器在調(diào)用無參數(shù)構(gòu)造函數(shù)或無參數(shù)static工廠方法來實(shí)例化bean 之后調(diào)用 bean 上的 setter 方法來完成的。

以下示例顯示了一個(gè)只能使用純 setter 注入進(jìn)行依賴注入的類樟澜。這個(gè)類是傳統(tǒng)的Java误窖。它是一個(gè)不依賴于容器特定接口叮盘、基類或注解的 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支持管理基于構(gòu)造函數(shù)和的Setter DI的Bean霹俺。在已經(jīng)通過構(gòu)造函數(shù)方法注入了一些依賴項(xiàng)之后柔吼,它還支持基于 setter 的 DI。您可以以BeanDefinition的形式配置依賴項(xiàng)丙唧,并將其與PropertyEditor實(shí)例結(jié)合使用嚷堡,以將屬性從一種格式轉(zhuǎn)換為另一種格式。但是艇棕,大多數(shù) Spring 用戶不直接(即以編程方式)使用這些類蝌戒,而是使用 XMLbean 定義、帶注解的組件(即用@Component沼琉、 @Controller等注釋的類)或@Bean基于 Java 的@Configuration類中的方法北苟。這些源然后在內(nèi)部轉(zhuǎn)換為實(shí)例BeanDefinition并用于加載整個(gè) Spring IoC 容器實(shí)例。

基于構(gòu)造函數(shù)還是基于 setter 的 DI打瘪?

由于您可以混合使用基于構(gòu)造函數(shù)和基于 setter 的 DI友鼻,因此根據(jù)經(jīng)驗(yàn),對(duì)強(qiáng)制依賴項(xiàng)使用構(gòu)造函數(shù)闺骚,對(duì)可選依賴項(xiàng)使用 setter 方法或配置方法是一個(gè)很好的經(jīng)驗(yàn)法則彩扔。請(qǐng)注意, 在 setter 方法上使用@Required注釋可用于使屬性成為必需的依賴項(xiàng)僻爽;但是虫碉,最好使用帶有參數(shù)編程驗(yàn)證的構(gòu)造函數(shù)注入。

Spring 團(tuán)隊(duì)通常提倡構(gòu)造函數(shù)注入胸梆,因?yàn)樗梢宰屇鷮?yīng)用程序組件實(shí)現(xiàn)為不可變對(duì)象敦捧,并確保所需的依賴項(xiàng)不是null. 此外,構(gòu)造函數(shù)注入的組件總是以完全初始化的狀態(tài)返回給客戶端(調(diào)用)代碼碰镜。順便提一下兢卵,大量的構(gòu)造函數(shù)參數(shù)是一種糟糕的代碼味道,這意味著該類可能有太多的責(zé)任绪颖,應(yīng)該重構(gòu)以更好地解決適當(dāng)?shù)年P(guān)注點(diǎn)分離問題渣窜。

Setter 注入應(yīng)該主要僅用于可以在類中分配合理默認(rèn)值的可選依賴項(xiàng)咖驮。否則关串,必須在代碼使用依賴項(xiàng)的任何地方執(zhí)行非空檢查平匈。setter 注入的一個(gè)好處是 setter 方法使該類的對(duì)象可以在以后重新配置或重新注入。因此滓鸠,通過JMX MBean 進(jìn)行管理是 setter 注入的一個(gè)引人注目的用例雁乡。

使用對(duì)特定類最有意義的 DI 樣式。有時(shí)糜俗,在處理您沒有源的第三方類時(shí),他的選擇是為你而做的。例如悠抹,如果第三方類不公開任何 setter 方法珠月,則構(gòu)造函數(shù)注入可能是 DI 的唯一可用形式。

依賴解析過程

容器執(zhí)行bean依賴解析如下:

  • 使用ApplicationContext描述所有 bean 的配置元數(shù)據(jù)創(chuàng)建和初始化楔敌。配置元數(shù)據(jù)可以由 XML啤挎、Java 代碼或注釋指定。
  • 對(duì)于每個(gè) bean卵凑,它的依賴關(guān)系以屬性庆聘、構(gòu)造函數(shù)參數(shù)或靜態(tài)工廠方法的參數(shù)(如果您使用它而不是普通構(gòu)造函數(shù))的形式表示。在實(shí)際創(chuàng)建 bean 時(shí)勺卢,將這些依賴關(guān)系提供給 bean伙判。
  • 每個(gè)屬性或構(gòu)造函數(shù)參數(shù)都是要設(shè)置的值的實(shí)際定義,或者是對(duì)容器中另一個(gè) bean 的引用黑忱。
  • 作為值的每個(gè)屬性或構(gòu)造函數(shù)參數(shù)都從其指定格式轉(zhuǎn)換為該屬性或構(gòu)造函數(shù)參數(shù)的實(shí)際類型宴抚。默認(rèn)情況下,Spring 可以將以字符串格式提供的值轉(zhuǎn)換為所有內(nèi)置類型甫煞,例如int菇曲、 long、String抚吠、boolean等常潮。

Spring 容器在創(chuàng)建容器時(shí)驗(yàn)證每個(gè) bean 的配置。但是楷力,在實(shí)際創(chuàng)建 bean 之前不會(huì)設(shè)置 bean 屬性本身蕊玷。創(chuàng)建容器時(shí)會(huì)創(chuàng)建單例范圍并設(shè)置為預(yù)實(shí)例化(默認(rèn))的 Bean。范圍在Bean Scopes中定義弥雹。否則垃帅,僅在請(qǐng)求時(shí)才創(chuàng)建 bean。創(chuàng)建 bean 可能會(huì)導(dǎo)致創(chuàng)建 bean 圖剪勿,因?yàn)?bean 的依賴項(xiàng)及其依賴項(xiàng)的依賴項(xiàng)(等等)被創(chuàng)建和分配贸诚。請(qǐng)注意,這些依賴項(xiàng)之間的解析不匹配可能會(huì)出現(xiàn)較晚 — 即厕吉,在第一次創(chuàng)建受影響的 bean 時(shí)酱固。

循環(huán)依賴

如果您主要使用構(gòu)造函數(shù)注入,則可能會(huì)創(chuàng)建無法解決的循環(huán)依賴場(chǎng)景头朱。

例如:A類通過構(gòu)造函數(shù)注入需要B類的實(shí)例运悲,B類通過構(gòu)造函數(shù)注入需要A類的實(shí)例。如果您將類 A 和 B 的 bean 配置為相互注入项钮,則 Spring IoC 容器在運(yùn)行時(shí)檢測(cè)到此循環(huán)引用班眯,并拋出一個(gè) BeanCurrentlyInCreationException.

一種可能的解決方案是編輯一些類的源代碼希停,以便由 setter 而不是構(gòu)造函數(shù)來配置∈鸢或者宠能,避免構(gòu)造函數(shù)注入并僅使用 setter 注入。也就是說磁餐,雖然不推薦违崇,但是可以通過setter注入來配置循環(huán)依賴。

與典型情況(沒有循環(huán)依賴)不同诊霹,bean A 和 bean B 之間的循環(huán)依賴迫使其中一個(gè) bean 在完全初始化之前注入另一個(gè) bean(經(jīng)典的雞和蛋場(chǎng)景)羞延。

您通常可以相信 Spring 會(huì)做正確的事情脾还。它在容器加載時(shí)檢測(cè)配置問題伴箩,例如對(duì)不存在的 bean 的引用和循環(huán)依賴。Spring 在真正創(chuàng)建 bean 時(shí)盡可能晚地設(shè)置屬性并解析依賴項(xiàng)荠呐。這意味著赛蔫,如果創(chuàng)建該對(duì)象或其依賴項(xiàng)時(shí)出現(xiàn)問題,則已正確加載的 Spring 容器稍后可以在您請(qǐng)求對(duì)象時(shí)生成異衬嗾牛——例如呵恢,由于缺少或無效的屬性,bean拋出異常媚创。某些配置問題的這種潛在延遲可見性是為什么ApplicationContext默認(rèn)情況下渗钉,實(shí)現(xiàn)預(yù)實(shí)例化單例 bean。以在實(shí)際需要之前創(chuàng)建這些 bean 的一些前期時(shí)間和內(nèi)存為代價(jià)钞钙,您ApplicationContext會(huì)在創(chuàng)建時(shí)發(fā)現(xiàn)配置問題鳄橘,而不是稍后。您仍然可以覆蓋此默認(rèn)行為芒炼,以便單例 bean 延遲初始化瘫怜,而不是急切地預(yù)實(shí)例化。

如果不存在循環(huán)依賴本刽,當(dāng)一個(gè)或多個(gè)協(xié)作 bean 被注入依賴 bean 時(shí)鲸湃,每個(gè)協(xié)作 bean 在注入依賴 bean 之前都已完全配置。這意味著子寓,如果 bean A 依賴 bean B暗挑,則 Spring IoC 容器在調(diào)用 bean A 上的 setter 方法之前完全配置 bean B。換句話說斜友,bean 被實(shí)例化(如果它不是預(yù)實(shí)例化的單例) )炸裆,設(shè)置它的依賴,并調(diào)用相關(guān)的生命周期方法(如配置的init方法 或InitializingBean回調(diào)方法)鲜屏。

依賴注入的例子

以下示例將基于 XML 的配置元數(shù)據(jù)用于基于 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"/>

以下示例顯示了相應(yīng)的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 文件中指定的屬性匹配听系。以下示例使用基于構(gòu)造函數(shù)的 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"/>

以下示例顯示了相應(yīng)的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 定義中指定的構(gòu)造函數(shù)參數(shù)用作ExampleBean的構(gòu)造方法贝奇。

現(xiàn)在考慮這個(gè)例子的一個(gè)變體虹菲,其中不使用構(gòu)造函數(shù)靠胜,而是告訴 Spring 調(diào)用static工廠方法來返回對(duì)象的實(shí)例:

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

以下示例顯示了相應(yīng)的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工廠方法的參數(shù)由<constructor-arg/>元素提供,就像實(shí)際使用了構(gòu)造函數(shù)一樣毕源。工廠方法返回的類的類型不必與包含static工廠方法的類的類型相同(盡管在本示例中是)浪漠。實(shí)例(非靜態(tài))工廠方法可以以基本相同的方式使用(除了使用factory-bean屬性而不是class屬性),我們不在這里討論這些細(xì)節(jié)霎褐。

1.4.2. 詳細(xì)依賴和配置

如上一節(jié)所述址愿,您可以將 bean 屬性和構(gòu)造函數(shù)參數(shù)定義為對(duì)其他托管 bean(協(xié)作者)的引用或作為內(nèi)聯(lián)定義的值。為此冻璃,Spring 的基于 XML 的配置元數(shù)據(jù)支持其<property/><constructor-arg/>元素中的子元素類型

直接值(原語响谓、字符串等)

在value所述的屬性<property/>元素指定屬性或構(gòu)造器參數(shù)的人類可讀的字符串表示。Spring 的轉(zhuǎn)換服務(wù)用于將這些值從 a 轉(zhuǎn)換String為屬性或參數(shù)的實(shí)際類型省艳。以下示例顯示了正在設(shè)置的各種值:

<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="misterkaoli"/>
</bean>

以下示例使用p-namespace進(jìn)行更簡(jiǎn)潔的 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="misterkaoli"/>

</beans>

前面的 XML 更簡(jiǎn)潔娘纷。但是,拼寫錯(cuò)誤是在運(yùn)行時(shí)而不是設(shè)計(jì)時(shí)發(fā)現(xiàn)的跋炕,除非您在創(chuàng)建 bean 定義時(shí)使用支持自動(dòng)屬性完成的 IDE(例如IntelliJ IDEA或Spring Tools for Eclipse)赖晶。強(qiáng)烈建議使用此類 IDE 幫助。

您還可以配置一個(gè)java.util.Properties實(shí)例辐烂,如下所示:

<bean id="mappings"
    class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">

    <!-- 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機(jī)制將<value/>元素內(nèi)部的文本轉(zhuǎn)換為 java.util.Properties實(shí)例PropertyEditor遏插。這是一個(gè)很好的捷徑,并且是 Spring 團(tuán)隊(duì)支持使用嵌套<value/>元素而不是value屬性樣式的少數(shù)幾個(gè)地方之一纠修。

idref 元素

所述idref元件是一個(gè)簡(jiǎn)單的防錯(cuò)方法對(duì)通過id(一個(gè)字符串值-而不是參考)在該容器另一個(gè)bean的一個(gè)<constructor-arg/>或<property/> 元件胳嘲。以下示例顯示了如何使用它:

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

<bean id="theClientBean" class="...">
    <property name="targetName">
        <idref bean="theTargetBean"/>
    </property>
</bean>

前面的 bean 定義片段與以下片段完全等效(在運(yùn)行時(shí)):

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

<bean id="client" class="...">
    <property name="targetName" value="theTargetBean"/>
</bean>

第一種形式比第二種形式更可取,因?yàn)槭褂?code>idref標(biāo)記可以讓容器在部署時(shí)驗(yàn)證引用的命名 bean 是否實(shí)際存在扣草。在第二個(gè)變體中了牛,不對(duì)傳遞給beantargetName屬性的值執(zhí)行驗(yàn)證client。只有在client實(shí)際實(shí)例化 bean時(shí)才會(huì)發(fā)現(xiàn)拼寫錯(cuò)誤(最有可能是致命的結(jié)果)德召。如果client bean 是原型bean白魂,則可能只有在部署容器很久之后才能發(fā)現(xiàn)此錯(cuò)誤和由此產(chǎn)生的異常。

4.0 bean XSD不再支持idref元素的本地屬性上岗,因?yàn)樗辉偬峁┏R?guī)bean引用的值福荸。當(dāng)升級(jí)到4.0模式時(shí),將現(xiàn)有的idref本地引用更改為idref bean肴掷。

其中一個(gè)共同的地方(至少在早期比Spring 2.0版本)<idref/>元素帶來的值在配置AOP攔截在 ProxyFactoryBeanbean定義敬锐。<idref/>在指定攔截器名稱時(shí)使用元素可防止您拼錯(cuò)攔截器 ID背传。

對(duì)其他 Bean 的引用(合作者)

ref 元素是<constructor-arg/>或<property/>內(nèi)部定義的元素。在這里台夺,您將 bean 的指定屬性的值設(shè)置為對(duì)容器管理的另一個(gè) bean(協(xié)作者)的引用径玖。被引用的 bean 是要設(shè)置其屬性的 bean 的依賴項(xiàng),在設(shè)置屬性之前根據(jù)需要對(duì)其進(jìn)行初始化颤介。(如果協(xié)作者是一個(gè)單例 bean梳星,它可能已經(jīng)被容器初始化。)所有引用最終都是對(duì)另一個(gè)對(duì)象的引用滚朵。范圍和驗(yàn)證取決于您是否通過bean or parent屬性指定其他對(duì)象的 ID 或名稱冤灾。

通過標(biāo)記的bean屬性指定目標(biāo) bean<ref/>是最通用的形式,它允許創(chuàng)建對(duì)同一容器或父容器中的任何 bean 的引用辕近,無論它是否在同一 XML 文件中韵吨。bean屬性的值 可以id與目標(biāo)bean的屬性相同,也可以與目標(biāo)bean的name屬性中的值之一相同移宅。以下示例顯示了如何使用ref元素:

<ref bean="someBean"/>

通過parent屬性指定目標(biāo) bean會(huì)創(chuàng)建對(duì)當(dāng)前容器的父容器中的 bean 的引用归粉。parent 屬性的值可以id與目標(biāo) bean的屬性或目標(biāo) bean 屬性中的值之一相同name。目標(biāo) bean 必須在當(dāng)前容器的父容器中漏峰。您應(yīng)該主要在具有容器層次結(jié)構(gòu)并且希望使用與父 bean 同名的代理將現(xiàn)有 bean 包裝在父容器中時(shí)使用此 bean 引用變體糠悼。以下清單顯示了如何使用該parent屬性:

<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required 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 bean XSD不再支持ref元素的本地屬性,因?yàn)樗辉偬峁┏R?guī)bean引用的值芽狗。當(dāng)升級(jí)到4.0 schema.descendant時(shí)绢掰,將現(xiàn)有的ref本地引用更改為ref bean

內(nèi)部bean

A <bean/> 元素內(nèi)部的<property/> 或<constructor-arg/> 元素定義一個(gè)內(nèi)部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>

內(nèi)部bean定義不需要定義的 ID 或名稱。如果指定童擎,容器也不會(huì)使用他作為標(biāo)識(shí)符滴劲。容器在創(chuàng)建時(shí)也會(huì)忽scope略標(biāo),因?yàn)閮?nèi)部 bean 始終是匿名的顾复,并且始終與外部 bean 一起創(chuàng)建班挖。不可能獨(dú)立訪問內(nèi)部 bean 或?qū)⑺鼈冏⑷氤忾] bean 之外的協(xié)作 bean 中。

作為一個(gè)極端情況芯砸,可以從自定義范圍接收銷毀回調(diào)——例如萧芙,對(duì)于包含在單例 bean 中的請(qǐng)求范圍內(nèi)的 bean。內(nèi)部 bean 實(shí)例的創(chuàng)建與其包含的 bean 相關(guān)聯(lián)假丧,但銷毀回調(diào)讓它參與請(qǐng)求范圍的生命周期双揪。這不是一個(gè)常見的場(chǎng)景。內(nèi)部 bean 通常只是共享它們包含的 bean 的作用域包帚。

集合

<list/>渔期,<set/>,<map/>,和<props/>元素分別設(shè)置Java Collection類型List疯趟,Set拘哨,Map,和Properties的屬性和參數(shù)信峻。以下示例顯示了如何使用它們:

<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的鍵或值或set的值倦青,也可以是以下任何元素:

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

集合合并

Spring 容器還支持合并集合。應(yīng)用程序開發(fā)人員可以定義父<list/>盹舞,<map/>产镐,<set/>或<props/>元素,并有孩子<list/>矾策,<map/>磷账,<set/>或<props/>元素繼承和父集合覆蓋值峭沦。也就是說贾虽,子集合的值是合并父集合和子集合的元素的結(jié)果,子集合元素覆蓋父集合中指定的值吼鱼。

關(guān)于合并的這一節(jié)討論了父子 bean 機(jī)制蓬豁。不熟悉父和子 bean 定義的讀者可能希望在繼續(xù)之前閱讀相關(guān)部分。

以下示例演示了集合合并:

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

請(qǐng)注意在bean 定義的merge=true屬性的<props/>元素上使用 adminEmails屬性child菇肃。當(dāng)child容器解析并實(shí)例化 bean 時(shí)地粪,生成的實(shí)例有一個(gè)adminEmails Properties集合,其中包含合并子集合 adminEmails與父adminEmails集合的結(jié)果琐谤。以下清單顯示了結(jié)果:

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

子Properties集合的值設(shè)置繼承父所有屬性元素<props/>蟆技,并且子的為值支持覆蓋父集合的值。

這一合并行為同樣適用于<list/>斗忌,<map/>和<set/> 集合類型质礼。在<list/>元素的特定情況下,與List集合類型(即ordered 值集合的概念)相關(guān)聯(lián)的語義得到維護(hù)织阳。父級(jí)的值在所有子級(jí)列表的值之前眶蕉。在Map,Set和Properties集合類型的情況下唧躲,沒有順序存在造挽。因此,對(duì)于容器內(nèi)部使用的關(guān)聯(lián)Map弄痹、Set和Properties實(shí)現(xiàn)類型的集合類型饭入,沒有有效排序語義。

集合合并的限制

您不能合并不同的集合類型(例如 aMap和 a List)肛真。如果您確實(shí)嘗試這樣做谐丢,Exception則會(huì)拋出適當(dāng)?shù)摹erge必須在較低的繼承子定義上指定該屬性毁欣。merge在父集合定義上指定屬性是多余的庇谆,不會(huì)導(dǎo)致所需的合并

強(qiáng)類型集合

隨著 Java 5 中泛型類型的引入岳掐,您可以使用強(qiáng)類型集合。也就是說饭耳,可以聲明一個(gè)Collection類型串述,使其只能包含(例如)String元素。如果您使用 Spring 將強(qiáng)類型依賴注入Collection到 bean 中寞肖,則可以利用 Spring 的類型轉(zhuǎn)換支持纲酗,以便在將強(qiáng)類型Collection 實(shí)例的元素添加到Collection. 以下 Java 類和 bean 定義顯示了如何執(zhí)行此操作:

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>

當(dāng)bean的accounts屬性something準(zhǔn)備注入時(shí),關(guān)于強(qiáng)類型元素類型的泛型信息Map<String, Float>可以通過反射獲得新蟆。因此觅赊,彈簧的類型轉(zhuǎn)換基礎(chǔ)設(shè)施識(shí)別的各種值的元素為類型的Float,并且字符串值(9.99琼稻,2.75吮螺,和 3.99)被轉(zhuǎn)換成實(shí)際的Float類型。

Null 和空字符串值

Spring 將屬性等的空參數(shù)視為空字符串帕翻。以下基于 XML 的配置元數(shù)據(jù)片段將email屬性設(shè)置為空 String值 ("")鸠补。

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

前面的示例等效于以下 Java 代碼:

exampleBean.setEmail("");

該<null/>元素處理null值。以下清單顯示了一個(gè)示例:

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

前面的示例等效于以下 Java 代碼:

exampleBean.setEmail(null);

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

p-namespace 允許您使用bean元素的屬性(而不是嵌套 <property/>元素)來描述協(xié)作 bean 的屬性值嘀掸,或兩者兼而有之紫岩。

Spring 支持具有命名空間的可擴(kuò)展配置格式,這些格式基于 XML 模式定義睬塌。beans本章討論的配置格式是在 XML Schema 文檔中定義的泉蝌。但是,p 命名空間并未在 XSD 文件中定義揩晴,僅存在于 Spring 的核心中勋陪。

以下示例顯示了兩個(gè)解析為相同結(jié)果的 XML 片段(第一個(gè)使用標(biāo)準(zhǔn) XML 格式,第二個(gè)使用 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 定義中調(diào)用的 p 命名空間中的一個(gè)屬性文狱。這告訴 Spring 包含一個(gè)屬性聲明粥鞋。如前所述,p 命名空間沒有模式定義瞄崇,因此您可以將屬性的名稱設(shè)置為屬性名稱呻粹。

下一個(gè)示例包括另外兩個(gè) bean 定義,它們都引用了另一個(gè) 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 命名空間的屬性值苏研,而且還使用特殊格式來聲明屬性引用等浊。第一個(gè) bean 定義用于<property name="spouse" ref="jane"/>創(chuàng)建從 beanjohn到 bean的引用 jane,而第二個(gè) bean 定義p:spouse-ref="jane"用作屬性來執(zhí)行完全相同的操作摹蘑。在這種情況下筹燕,spouse是屬性名稱,而-ref部分表示這不是一個(gè)直接值,而是對(duì)另一個(gè) bean 的引用撒踪。

p 命名空間不如標(biāo)準(zhǔn) XML 格式靈活过咬。例如,聲明屬性引用的格式與以 結(jié)尾的屬性沖突Ref制妄,而標(biāo)準(zhǔn) XML 格式則不然掸绞。我們建議您謹(jǐn)慎選擇您的方法并將其傳達(dá)給您的團(tuán)隊(duì)成員,以避免同時(shí)使用所有三種方法生成 XML 文檔耕捞。

帶有 c 命名空間的 XML 快捷方式

與帶有 p-namespace類似衔掸,Spring 3.1 中引入的 c-namespace 允許內(nèi)聯(lián)屬性來配置構(gòu)造函數(shù)參數(shù)而不是嵌套constructor-arg元素。

以下示例使用c:命名空間執(zhí)行與 Constructor-based Dependency Injection 相同的操作:

<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:相同的約定作為一個(gè)(尾部-ref的bean引用)俺抽,根據(jù)他們的名字設(shè)置構(gòu)造函數(shù)的參數(shù)敞映。同樣,它需要在 XML 文件中聲明磷斧,即使它沒有在 XSD 模式中定義(它存在于 Spring 核心中)振愿。

對(duì)于構(gòu)造函數(shù)參數(shù)名稱不可用的極少數(shù)情況(通常如果字節(jié)碼是在沒有調(diào)試信息的情況下編譯的),您可以使用參數(shù)索引的回退瞳抓,如下所示:

<!-- 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 語法埃疫,索引表示法需要存在前導(dǎo)_,因?yàn)?XML 屬性名稱不能以數(shù)字開頭(即使某些 IDE 允許)孩哑。相應(yīng)的索引符號(hào)也可用于<constructor-arg>元素但不常用,因?yàn)樵谀抢锫暶鞯暮?jiǎn)單順序通常就足夠了翠桦。

實(shí)際上横蜒,構(gòu)造函數(shù)解析機(jī)制在匹配參數(shù)方面非常有效,因此除非您確實(shí)需要销凑,否則我們建議在整個(gè)配置中使用名稱表示法丛晌。

復(fù)合屬性名稱

您可以在設(shè)置 bean 屬性時(shí)使用復(fù)合或嵌套的屬性名稱,只要路徑中除最終屬性名稱之外的所有組件都不是null. 考慮以下 bean 定義:

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

所述something bean具有fred屬性斗幼,該屬性具有bob屬性澎蛛,其具有sammy屬性,并且最終sammy屬性被設(shè)置為值123蜕窿。為了使其工作谋逻,l在 bean 被構(gòu)造之后fred屬性 ofsomething和bob屬性fred不能nul。否則桐经,拋出一個(gè) NullPointerException毁兆。

1.4.3. 使用depends-on

如果一個(gè) bean 是另一個(gè) bean 的依賴項(xiàng),這通常意味著一個(gè) bean 被設(shè)置為另一個(gè) bean 的屬性阴挣。通常气堕,您使用基于 XML 的配置元數(shù)據(jù)中的<ref/> 元素來完成此操作。但是,有時(shí) bean 之間的依賴關(guān)系不那么直接茎芭。例如揖膜,當(dāng)需要觸發(fā)類中的靜態(tài)初始化程序時(shí),例如數(shù)據(jù)庫驅(qū)動(dòng)程序注冊(cè)梅桩。depends-on在初始化使用此元素的 bean 之前次氨,該屬性可以顯式地強(qiáng)制初始化一個(gè)或多個(gè) bean。以下示例使用該depends-on屬性來表達(dá)對(duì)單個(gè) bean 的依賴:

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

要表達(dá)對(duì)多個(gè) bean 的依賴摘投,請(qǐng)?zhí)峁?bean 名稱列表作為depends-on屬性值(逗號(hào)煮寡、空格和分號(hào)是有效的分隔符):

<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屬性可以指定初始化時(shí)依賴項(xiàng),并且僅在單例bean的情況下犀呼,還可以指定相應(yīng)的銷毀時(shí)依賴項(xiàng)幸撕。depends-on在給定的 bean 本身被銷毀之前,首先銷毀與給定 bean定義關(guān)系的依賴 bean 外臂。這樣坐儿,depends-on也可以控制關(guān)機(jī)順序。

1.4.4. 延遲初始化的 Bean

默認(rèn)情況下宋光,ApplicationContext實(shí)現(xiàn)會(huì)在初始化過程中急切地創(chuàng)建和配置所有單例bean貌矿。通常,這種預(yù)實(shí)例化是可取的罪佳,因?yàn)榭梢粤⒓窗l(fā)現(xiàn)配置或周圍環(huán)境中的錯(cuò)誤逛漫,而不是在幾小時(shí)甚至幾天之后。當(dāng)這種行為不可取時(shí)赘艳,您可以通過將 bean 定義標(biāo)記為延遲初始化來防止單例 bean 的預(yù)實(shí)例化酌毡。一個(gè)延遲初始化的 bean 告訴 IoC 容器在它第一次被請(qǐng)求時(shí)創(chuàng)建一個(gè) bean 實(shí)例,而不是在啟動(dòng)時(shí)蕾管。

在 XML 中枷踏,此行為由 元素lazy-init上的屬性控制<bean/>,如以下示例所示:

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

當(dāng)前面的配置被ApplicationContext使用時(shí)掰曾,lazybean 在ApplicationContext啟動(dòng)時(shí)不會(huì)被預(yù)先實(shí)例化旭蠕,而not.lazybean 會(huì)被預(yù)先實(shí)例化。

但是旷坦,當(dāng)延遲初始化 bean 是未延遲初始化的單例 bean 的依賴項(xiàng)時(shí)掏熬,ApplicationContext會(huì)在啟動(dòng)時(shí)創(chuàng)建延遲初始化 bean,因?yàn)樗仨殱M足單例的依賴項(xiàng)塞蹭。延遲初始化的 bean 被注入到其他地方?jīng)]有延遲初始化的單例 bean 中孽江。

您還可以通過使用元素default-lazy-init上的屬性來控制容器級(jí)別的延遲初始化 <beans/>,如以下示例所示:

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

1.4.5. 自動(dòng)裝配合作者

Spring 容器可以自動(dòng)裝配協(xié)作 bean 之間的關(guān)系番电。您可以讓 Spring 通過檢查ApplicationContext. 自動(dòng)裝配具有以下優(yōu)點(diǎn):

  • 自動(dòng)裝配可以顯著減少指定屬性或構(gòu)造函數(shù)參數(shù)的需要岗屏。(本章其他地方討論的其他機(jī)制辆琅,例如 bean 模板 ,在這方面也很有價(jià)值这刷。)

  • 自動(dòng)裝配可以隨著對(duì)象的發(fā)展更新配置婉烟。例如,如果您需要向類添加依賴項(xiàng)暇屋,則無需修改配置即可自動(dòng)滿足該依賴項(xiàng)似袁。因此,自動(dòng)裝配在開發(fā)過程中特別有用咐刨,當(dāng)代碼庫變得更穩(wěn)定時(shí)挠说,不會(huì)否定切換到顯式裝配的選項(xiàng)腺办。

使用基于 XML 的配置元數(shù)據(jù)時(shí)(請(qǐng)參閱依賴注入)处嫌,您可以使用元素的autowire屬性為 bean 定義指定自動(dòng)裝配模式<bean/>笨篷。自動(dòng)裝配功能有四種模式。您可以為每個(gè) bean 指定自動(dòng)裝配联予,因此可以選擇要自動(dòng)裝配的那些啼县。下表描述了四種自動(dòng)裝配模式:

表 2. 自動(dòng)裝配模式

Mode Explanation
no (默認(rèn))沒有自動(dòng)裝配。Bean 引用必須由ref元素定義沸久。對(duì)于較大的部署季眷,不建議更改默認(rèn)設(shè)置,因?yàn)槊鞔_指定協(xié)作者可以提供更好的控制和清晰度卷胯。在某種程度上子刮,它記錄了系統(tǒng)的結(jié)構(gòu)。
byName 按屬性名稱自動(dòng)裝配诵竭。Spring 查找與需要自動(dòng)裝配的屬性同名的 bean话告。例如,如果一個(gè) bean 定義被設(shè)置為按名稱自動(dòng)裝配并且它包含一個(gè)master屬性(即它有一個(gè) setMaster(..)方法)卵慰,Spring 會(huì)查找一個(gè)名為的 bean 定義master并使用它來設(shè)置屬性。
byType 如果容器中只存在一個(gè)屬性類型的 bean佛呻,則讓屬性自動(dòng)裝配裳朋。如果存在多個(gè),則會(huì)引發(fā)致命異常吓著,這表明您不能byType為該 bean使用自動(dòng)裝配鲤嫡。如果沒有匹配的 bean,則不會(huì)發(fā)生任何事情(未設(shè)置屬性)绑莺。
constructor 類似于byType但適用于構(gòu)造函數(shù)參數(shù)暖眼。如果容器中沒有一個(gè)構(gòu)造函數(shù)參數(shù)類型的 bean,則會(huì)引發(fā)致命錯(cuò)誤纺裁。

使用byType或constructor自動(dòng)裝配模式诫肠,您可以連接數(shù)組和類型化集合司澎。在這種情況下,提供容器內(nèi)與預(yù)期類型匹配的所有自動(dòng)裝配候選者以滿足依賴關(guān)系栋豫。Map如果預(yù)期的鍵類型是 挤安,您可以自動(dòng)裝配強(qiáng)類型實(shí)例String。自動(dòng)裝配Map 實(shí)例的值由與預(yù)期類型匹配的所有 bean 實(shí)例組成丧鸯,并且 Map實(shí)例的鍵包含相應(yīng)的 bean 名稱蛤铜。

自動(dòng)裝配的局限性和缺點(diǎn)

自動(dòng)裝配在整個(gè)項(xiàng)目中一致使用時(shí)效果最佳。如果通常不使用自動(dòng)裝配丛肢,開發(fā)人員可能會(huì)使用它來連接一兩個(gè) bean 定義围肥,這可能會(huì)讓人感到困惑。
考慮自動(dòng)裝配的局限性和缺點(diǎn):

  • property和constructor-arg設(shè)置中的顯式依賴項(xiàng)始終覆蓋自動(dòng)裝配蜂怎。您不能自動(dòng)裝配簡(jiǎn)單屬性穆刻,例如Strings、 和Classes(以及此類簡(jiǎn)單屬性的數(shù)組)派敷。此限制是有意設(shè)計(jì)的蛹批。

  • 自動(dòng)裝配不如顯式裝配精確。雖然篮愉,如前面的表中所述腐芍,Spring 小心避免在可能產(chǎn)生意外結(jié)果的歧義的情況下進(jìn)行猜測(cè)。不再明確記錄 Spring 管理的對(duì)象之間的關(guān)系试躏。

  • 可能無法從 Spring 容器生成文檔的工具中使用接線信息猪勇。

  • 容器內(nèi)的多個(gè) bean 定義可能與要自動(dòng)裝配的 setter 方法或構(gòu)造函數(shù)參數(shù)指定的類型相匹配。對(duì)于數(shù)組颠蕴、集合或 Map實(shí)例泣刹,這不一定是問題。但是犀被,對(duì)于期望單個(gè)值的依賴項(xiàng)椅您,這種歧義不會(huì)被任意解決。如果沒有唯一的 bean 定義可用寡键,則拋出異常掀泳。

在后一種情況下,您有多種選擇:

  • 放棄自動(dòng)裝配以支持顯式裝配西轩。
  • 如下一節(jié)所述员舵,通過將其autowire-candidate屬性設(shè)置為false來避免對(duì) bean 定義進(jìn)行自動(dòng)裝配。
  • 通過將primary<bean/>元素的屬性設(shè)置為 藕畔,將單個(gè) bean 定義指定為主要候選者 true马僻。
  • 使用基于注解的配置實(shí)現(xiàn)更細(xì)粒度的控制,如基于注解的容器配置 中所述注服。

從自動(dòng)裝配中排除 Bean

在每個(gè) bean 的基礎(chǔ)上韭邓,您可以從自動(dòng)裝配中排除一個(gè) bean措近。在 Spring 的 XML 格式中,將<bean/>元素的autowire-candidate屬性設(shè)置為false. 容器使該特定 bean 定義對(duì)自動(dòng)裝配基礎(chǔ)設(shè)施不可用(包括注釋樣式配置仍秤,例如@Autowired)熄诡。

該autowire-candidate屬性旨在僅影響基于類型的自動(dòng)裝配。它不會(huì)影響按名稱的顯式引用诗力,即使指定的 bean 未標(biāo)記為自動(dòng)裝配候選者凰浮,也會(huì)解析。因此苇本,如果名稱匹配袜茧,按名稱自動(dòng)裝配仍然會(huì)注入一個(gè) bean。

您還可以根據(jù)對(duì) bean 名稱的模式匹配來限制自動(dòng)裝配候選者瓣窄。頂級(jí)<beans/>元素在其default-autowire-candidates屬性中接受一個(gè)或多個(gè)模式 笛厦。例如,要將自動(dòng)裝配候選狀態(tài)限制為名稱以Repository結(jié)尾的任何 bean 俺夕,請(qǐng)?zhí)峁┲?Repository裳凸。要提供多個(gè)模式,請(qǐng)?jiān)诙禾?hào)分隔的列表中定義它們劝贸。bean 定義falseautowire-candidate的屬性的顯式值 true或始終優(yōu)先姨谷。對(duì)于此類 bean,匹配規(guī)則模式不適用映九。

這些技術(shù)對(duì)于您永遠(yuǎn)不想通過自動(dòng)裝配注入其他 bean 的 bean 很有用梦湘。這并不意味著不能使用自動(dòng)裝配來配置被排除的 bean 本身。相反件甥,bean 本身不是自動(dòng)裝配其他 bean 的候選者捌议。

方法注入

在大多數(shù)應(yīng)用場(chǎng)景中,容器中的大部分 bean 都是 單例的引有。當(dāng)單例 bean 需要與另一個(gè)單例 bean 協(xié)作或非單例 bean 需要與另一個(gè)非單例 bean 協(xié)作時(shí)瓣颅,您通常通過將一個(gè) bean 定義為另一個(gè) bean 的屬性來處理依賴關(guān)系。當(dāng) bean 生命周期不同時(shí)就會(huì)出現(xiàn)問題譬正。假設(shè)單例 bean A 需要使用非單例(原型)bean B弄捕,可能在 A 上的每次方法調(diào)用上。容器只創(chuàng)建單例 bean A 一次导帝,因此只有一次設(shè)置屬性的機(jī)會(huì)。容器無法在每次需要時(shí)為 bean A 提供 bean B 的新實(shí)例穿铆。

一個(gè)解決方案是放棄一些控制反轉(zhuǎn)您单。您可以通過實(shí)現(xiàn)ApplicationContextAware接口,以及在bean A每次需要bean B實(shí)例時(shí)對(duì)容器進(jìn)行g(shù)etBean(“B”)調(diào)用荞雏,從而使bean A意識(shí)到容器虐秦。下面的例子展示了這種方法:

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

前面是不可取的平酿,因?yàn)闃I(yè)務(wù)代碼知道并耦合到 Spring Framework。方法注入是 Spring IoC 容器的一個(gè)有點(diǎn)高級(jí)的特性悦陋,可以讓你干凈地處理這個(gè)用例蜈彼。

查找方法注入

查找方法注入是容器覆蓋容器管理 bean 上的方法并返回容器中另一個(gè)命名 bean 的查找結(jié)果的能力。查找通常涉及原型 bean俺驶,如上一節(jié)中描述的場(chǎng)景幸逆。Spring Framework 通過使用來自 CGLIB 庫的字節(jié)碼生成來動(dòng)態(tài)生成覆蓋該方法的子類來實(shí)現(xiàn)此方法注入。

  • 要使這種動(dòng)態(tài)子類化工作暮现,Spring bean 容器子類化的類不能是final还绘,要覆蓋的方法也不能是final。

  • 對(duì)具有abstract方法的類進(jìn)行單元測(cè)試需要您自己對(duì)類進(jìn)行子類化并提供該abstract方法的存根實(shí)現(xiàn)栖袋。

  • 組件掃描也需要具體的方法拍顷,這需要具體的類來獲取。

  • 另一個(gè)關(guān)鍵限制是查找方法不適用于工廠方法塘幅,尤其不適@Bean用于配置類中的方法昔案,因?yàn)樵谶@種情況下,容器不負(fù)責(zé)創(chuàng)建實(shí)例电媳,因此不能在上創(chuàng)建運(yùn)行時(shí)生成的子類蒼蠅踏揣。

對(duì)于CommandManager前面代碼片段中的類,Spring 容器動(dòng)態(tài)覆蓋了該createCommand() 方法的實(shí)現(xiàn)匆背。該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在本例中為 the)的客戶端類中,要注入的方法需要以下形式的簽名:

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

如果方法是abstract钝尸,則動(dòng)態(tài)生成的子類實(shí)現(xiàn)該方法括享。否則,動(dòng)態(tài)生成的子類會(huì)覆蓋原始類中定義的具體方法珍促×逑剑考慮以下示例:

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

標(biāo)識(shí)為的 bean在需要bean的新實(shí)例時(shí)commandManager調(diào)用它自己的createCommand()方法myCommandmyCommand如果確實(shí)需要猪叙,您必須小心地將bean部署為原型娇斩。如果是單例,myCommand 則每次都返回相同的bean實(shí)例穴翩。

或者犬第,在基于注解的組件模型中,您可以通過@Lookup注解聲明一個(gè)查找方法芒帕,如下例所示:

public abstract class CommandManager {

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

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

或者歉嗓,更慣用的是,您可以依靠目標(biāo) bean 根據(jù)查找方法的聲明返回類型進(jìn)行解析:

public abstract class CommandManager {

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

    @Lookup
    protected abstract Command createCommand();
}

請(qǐng)注意背蟆,您通常應(yīng)該使用具體的存根實(shí)現(xiàn)聲明此類帶注釋的查找方法鉴分,以便它們與 Spring 的組件掃描規(guī)則兼容哮幢,默認(rèn)情況下抽象類將被忽略。此限制不適用于顯式注冊(cè)或顯式導(dǎo)入的 bean 類志珍。

訪問不同范圍的目標(biāo) bean 的另一種方法是ObjectFactory/ Provider注入點(diǎn)橙垢。參見Scoped Beans as Dependencies。
您可能還會(huì)發(fā)現(xiàn)ServiceLocatorFactoryBean(在 org.springframework.beans.factory.config包中)很有用伦糯。

任意方法替換

與查找方法注入相比柜某,一種不太有用的方法注入形式是能夠用另一種方法實(shí)現(xiàn)替換托管 bean 中的任意方法。您可以安全地跳過本節(jié)的其余部分舔株,直到您真正需要此功能莺琳。

使用基于 XML 的配置元數(shù)據(jù),您可以使用該replaced-method元素為已部署的 bean 將現(xiàn)有方法實(shí)現(xiàn)替換為另一個(gè)方法實(shí)現(xiàn)载慈〔训龋考慮下面的類,它有一個(gè)computeValue我們想要覆蓋的方法:

public class MyValueCalculator {

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

    // some other methods...
}

實(shí)現(xiàn)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"/>

您可以<arg-type/>在元素內(nèi)使用一個(gè)或多個(gè)元素<replaced-method/> 來指示被覆蓋的方法的方法簽名辞做。僅當(dāng)方法重載并且類中存在多個(gè)變體時(shí),才需要參數(shù)的簽名寡具。為方便起見秤茅,參數(shù)的類型字符串可以是完全限定類型名稱的子字符串。例如童叠,以下所有匹配 java.lang.String:

java.lang.String
String
Str

由于參數(shù)的數(shù)量通常足以區(qū)分每個(gè)可能的選擇框喳,因此此快捷方式可以讓您只鍵入與參數(shù)類型匹配的最短字符串,從而可以節(jié)省大量輸入厦坛。

1.5. Bean 作用域

創(chuàng)建 bean 定義時(shí)五垮,您創(chuàng)建了一個(gè)配方,用于創(chuàng)建由該 bean 定義定義的類的實(shí)際實(shí)例杜秸。bean 定義是一個(gè)配方的想法很重要放仗,因?yàn)檫@意味著,與類一樣撬碟,您可以從單個(gè)配方創(chuàng)建許多對(duì)象實(shí)例诞挨。

您不僅可以控制要插入到從特定 bean 定義創(chuàng)建的對(duì)象中的各種依賴項(xiàng)和配置值,還可以控制從特定 bean 定義創(chuàng)建的對(duì)象的范圍呢蛤。這種方法功能強(qiáng)大且靈活惶傻,因?yàn)槟梢酝ㄟ^配置選擇您創(chuàng)建的對(duì)象的范圍,而不必在 Java 類級(jí)別烘焙對(duì)象的范圍其障〈锫蓿可以將 Bean 定義為部署在多個(gè)范圍之一中。Spring Framework 支持六個(gè)范圍,其中四個(gè)僅在您使用 web-aware ApplicationContext時(shí)才可用粮揉。您還可以創(chuàng)建自定義范圍。

下表描述了支持的范圍:
表 3. Bean 范圍

Scope Description
single (默認(rèn))將單個(gè) bean 定義范圍限定為每個(gè) Spring IoC 容器的單個(gè)對(duì)象實(shí)例抚笔。
prototype 將單個(gè) bean 定義范圍限定為任意數(shù)量的對(duì)象實(shí)例扶认。
request 將單個(gè) bean 定義范圍限定為單個(gè) HTTP 請(qǐng)求的生命周期。也就是說殊橙,每個(gè) HTTP 請(qǐng)求都有自己的 bean 實(shí)例辐宾,該 bean 實(shí)例是在單個(gè) bean 定義的后面創(chuàng)建的。僅在 web-aware Spring 的上下文中有效ApplicationContext膨蛮。
session 將單個(gè) bean 定義范圍限定為 HTTP 的生命周期Session叠纹。僅在 web-aware Spring 的上下文中有效ApplicationContext。
application 將單個(gè) bean 定義范圍限定為ServletContext. 僅在 web-aware Spring 的上下文中有效ApplicationContext敞葛。
websocket 將單個(gè) bean 定義范圍限定為WebSocket. 僅在 web-aware Spring 的上下文中有效ApplicationContext誉察。

從 Spring 3.0 開始,線程作用域可用惹谐,但默認(rèn)情況下未注冊(cè)持偏。有關(guān)更多信息,請(qǐng)參閱 的文檔SimpleThreadScope氨肌。有關(guān)如何注冊(cè)此或任何其他自定義范圍的說明鸿秆,請(qǐng)參閱使用自定義范圍。

1.5.1. 單例作用域

只有一個(gè)單例 bean 的共享實(shí)例被管理怎囚,并且所有對(duì)帶有一個(gè)或多個(gè) ID 與該 bean 定義匹配的 bean 的請(qǐng)求都會(huì)導(dǎo)致 Spring 容器返回一個(gè)特定的 bean 實(shí)例卿叽。

換句話說,當(dāng)您定義一個(gè) bean 定義并且它的作用域是一個(gè)單例時(shí)恳守,Spring IoC 容器會(huì)創(chuàng)建該 bean 定義定義的對(duì)象的一個(gè)實(shí)例考婴。該單個(gè)實(shí)例存儲(chǔ)在此類單例 bean 的緩存中,并且對(duì)該命名 bean 的所有后續(xù)請(qǐng)求和引用都返回緩存對(duì)象井誉。下圖顯示了單例范圍的工作原理:


image.png

Spring 的單例 bean 概念不同于四人組 (GoF) 模式書中定義的單例模式蕉扮。GoF 單例對(duì)對(duì)象的范圍進(jìn)行了硬編碼,以便每個(gè) ClassLoader 只創(chuàng)建一個(gè)特定類的一個(gè)實(shí)例颗圣。Spring 單例的范圍最好描述為每個(gè)容器和每個(gè) bean喳钟。這意味著,如果您在單個(gè) Spring 容器中為特定類定義一個(gè) bean在岂,則 Spring 容器會(huì)創(chuàng)建該 bean 定義定義的類的一個(gè)且僅一個(gè)實(shí)例奔则。單例作用域是 Spring 中的默認(rèn)作用域。要將 bean 定義為 XML 中的單例蔽午,您可以定義一個(gè) 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. 原型作用域

bean 部署的非單一原型范圍導(dǎo)致每次對(duì)特定 bean 發(fā)出請(qǐng)求時(shí)都會(huì)創(chuàng)建一個(gè)新 bean 實(shí)例。也就是說,bean 被注入到另一個(gè) bean 中抽莱,或者您通過getBean()容器上的方法調(diào)用來請(qǐng)求它范抓。通常,您應(yīng)該對(duì)所有有狀態(tài) bean 使用原型作用域食铐,對(duì)無狀態(tài) bean 使用單例作用域匕垫。

下圖說明了 Spring 原型范圍:


image.png

(數(shù)據(jù)訪問對(duì)象 (DAO) 通常不配置為原型,因?yàn)榈湫偷?DAO 不保存任何會(huì)話狀態(tài)虐呻。我們更容易重用單例圖的核心象泵。)

以下示例將 bean 定義為 XML 中的原型:

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

與其他作用域相比,Spring 不管理原型 bean 的完整生命周期斟叼。容器實(shí)例化偶惠、配置和以其他方式組裝原型對(duì)象并將其交給客戶端,沒有該原型實(shí)例的進(jìn)一步記錄朗涩。因此忽孽,盡管在所有對(duì)象上調(diào)用初始化生命周期回調(diào)方法,而不管范圍如何馋缅,但在原型的情況下扒腕,不會(huì)調(diào)用配置的銷毀生命周期回調(diào)∮┿玻客戶端代碼必須清理原型范圍內(nèi)的對(duì)象并釋放原型 bean 持有的昂貴資源瘾腰。要讓 Spring 容器釋放原型作用域 bean 持有的資源,請(qǐng)嘗試使用自定義bean post-processor覆履,它保存對(duì)需要清理的 bean 的引用蹋盆。

在某些方面,Spring 容器在原型作用域 bean 方面的角色是 Javanew運(yùn)算符的替代品硝全。超過該點(diǎn)的所有生命周期管理都必須由客戶端處理栖雾。有關(guān) Spring 容器中 bean 生命周期的詳細(xì)信息,請(qǐng)參閱生命周期回調(diào)伟众。

1.5.3. 具有 Prototype-bean 依賴關(guān)系的 Singleton Bean

當(dāng)您使用具有對(duì)原型 bean 的依賴的單例作用域 bean 時(shí)析藕,請(qǐng)注意在實(shí)例化時(shí)解析依賴關(guān)系。因此凳厢,如果您將原型范圍的 bean 依賴注入到單例范圍的 bean 中账胧,則會(huì)實(shí)例化一個(gè)新的原型 bean,然后將依賴項(xiàng)注入到單例 bean 中先紫。原型實(shí)例是唯一提供給單例作用域 bean 的實(shí)例治泥。

但是,假設(shè)您希望單例范圍的 bean 在運(yùn)行時(shí)重復(fù)獲取原型范圍的 bean 的新實(shí)例遮精。您不能將原型范圍的 bean 依賴注入到您的單例 bean 中居夹,因?yàn)樵撟⑷雰H發(fā)生一次,當(dāng) Spring 容器實(shí)例化單例 bean 并解析并注入其依賴項(xiàng)時(shí)。如果您在運(yùn)行時(shí)多次需要原型 bean 的新實(shí)例准脂,請(qǐng)參閱方法注入劫扒。

精彩繼續(xù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市意狠,隨后出現(xiàn)的幾起案子粟关,更是在濱河造成了極大的恐慌,老刑警劉巖环戈,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異澎灸,居然都是意外死亡院塞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門性昭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拦止,“玉大人,你說我怎么就攤上這事糜颠⌒谧澹” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵其兴,是天一觀的道長顶瞒。 經(jīng)常有香客問我,道長元旬,這世上最難降的妖魔是什么榴徐? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮匀归,結(jié)果婚禮上坑资,老公的妹妹穿的比我還像新娘。我一直安慰自己穆端,他們只是感情好袱贮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著体啰,像睡著了一般攒巍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狡赐,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天窑业,我揣著相機(jī)與錄音,去河邊找鬼枕屉。 笑死常柄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播西潘,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼卷玉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了喷市?” 一聲冷哼從身側(cè)響起相种,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎品姓,沒想到半個(gè)月后寝并,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腹备,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年衬潦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片植酥。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡镀岛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出友驮,到底是詐尸還是另有隱情漂羊,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布卸留,位于F島的核電站走越,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏艾猜。R本人自食惡果不足惜买喧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匆赃。 院中可真熱鬧淤毛,春花似錦、人聲如沸算柳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞬项。三九已至蔗蹋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間囱淋,已是汗流浹背猪杭。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妥衣,地道東北人皂吮。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓戒傻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜂筹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子需纳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容