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)用程序屈溉。
圖 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è)類型JpaAccountDao
和JpaItemDao
(基于 JPA 對(duì)象-關(guān)系映射標(biāo)準(zhǔn))的數(shù)據(jù)訪問對(duì)象組成搓译。該property name
元素是指JavaBean屬性的名稱悲柱,以及ref
元素指的是另一個(gè)bean定義的名稱。id
和ref
元素之間的這種聯(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) (OtherThing或者是 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攔截在 ProxyFactoryBean
bean定義敬锐。<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()
方法myCommand
。myCommand
如果確實(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ì)象井誉。下圖顯示了單例范圍的工作原理:
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 原型范圍:
(數(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ù)