1.Ioc容器
????核心技術(shù) 5.2.2Release
? ? 參考文檔的這一部分涵蓋了Spring框架中不可或缺的所有技術(shù)。
????其中最重要的是Spring框架的控制反轉(zhuǎn)(IoC)容器。在對(duì)Spring框架的IoC容器進(jìn)行了全面的處理之后,還對(duì)Spring的面向方面編程(AOP)技術(shù)進(jìn)行了全面的介紹。Spring框架有自己的AOP框架,它在概念上很容易理解,并且成功地解決了Java企業(yè)編程中80%的AOP需求。
????還提供了Spring與AspectJ集成的內(nèi)容(目前在特性方面是最豐富的,當(dāng)然也是Java企業(yè)空間中最成熟的AOP實(shí)現(xiàn))。
????1.1 Spring IoC 容器和Beans 簡介
????本章介紹了控制反轉(zhuǎn)(IoC)原理的Spring框架實(shí)現(xiàn)。IoC也稱為依賴項(xiàng)注入(DI)。這是一個(gè)對(duì)象僅通過構(gòu)造函數(shù)參數(shù)、工廠方法的參數(shù)或?qū)ο髮?shí)例構(gòu)造或從工廠方法返回后在對(duì)象實(shí)例上設(shè)置的屬性來定義其依賴項(xiàng)(即使用的其他對(duì)象)的過程。然后容器在創(chuàng)建bean時(shí)注入這些依賴項(xiàng)。這個(gè)過程基本上是bean本身的逆過程(因此稱為控制反轉(zhuǎn)),通過使用類的直接構(gòu)造或服務(wù)定位器模式等機(jī)制來控制其依賴項(xiàng)的實(shí)例化或位置。
? ??org.springframework.beans 和org.springframework.context 是Spring框架的IoC容器的基礎(chǔ)。BeanFactory接口提供了能夠管理任何類型對(duì)象的高級(jí)配置機(jī)制。ApplicationContext是BeanFactory的子接口。它的特點(diǎn):? ?
? ? 1.更容易與Spring的AOP特性集成?
? ? 2.消息資源處理(用于國際化)
? ? 3.事件發(fā)布
? ? 4.特定于應(yīng)用程序?qū)拥纳舷挛?,如用于web應(yīng)用程序的WebApplicationContext。
????簡而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多企業(yè)特定的功能。ApplicationContext是BeanFactory的一個(gè)完整超集,在本章描述Spring的IoC容器時(shí)專門使用它。有關(guān)使用BeanFactory而不是ApplicationContext的更多信息,請(qǐng)參見BeanFactory。
? ??在Spring中,構(gòu)成應(yīng)用程序主干并由Spring IoC容器管理的對(duì)象稱為bean。bean是由Spring IoC容器實(shí)例化、組裝和管理的對(duì)象。否則,bean只是應(yīng)用程序中的眾多對(duì)象之一。bean及其之間的依賴關(guān)系反映在容器使用的配置元數(shù)據(jù)中。
? ? 1.2 容器總覽
???org.springframework.context.ApplicationContext 接口表示Spring IoC容器,負(fù)責(zé)實(shí)例化、配置和組裝bean。容器通過讀取配置元數(shù)據(jù)獲取關(guān)于要實(shí)例化、配置和組裝哪些對(duì)象的指令。配置元數(shù)據(jù)用XML、Java注釋或Java代碼表示。它允許您表達(dá)組成應(yīng)用程序的對(duì)象以及這些對(duì)象之間豐富的相互依賴關(guān)系。
????ApplicationContext接口的幾個(gè)實(shí)現(xiàn)由Spring提供。在獨(dú)立應(yīng)用程序中,通常創(chuàng)建ClassPathXmlApplicationContext或FileSystemXmlApplicationContext的實(shí)例。雖然XML一直是定義配置元數(shù)據(jù)的傳統(tǒng)格式,但是可以通過提供少量XML配置以聲明方式支持這些額外的元數(shù)據(jù)格式,從而指示容器使用Java注釋或代碼作為元數(shù)據(jù)格式。
????在大多數(shù)應(yīng)用程序場景中,不需要顯式的用戶代碼來實(shí)例化一個(gè)或多個(gè)Spring IoC容器實(shí)例。例如,在web應(yīng)用程序場景中,在應(yīng)用程序的web. XML文件中使用8行(大約)簡單的樣板web描述符XML通常就足夠了(請(qǐng)參閱web應(yīng)用程序的方便的ApplicationContext實(shí)例化)。如果您使用Spring工具套件(一個(gè)eclipse支持的開發(fā)環(huán)境),那么只需幾次鼠標(biāo)單擊或擊鍵,您就可以輕松地創(chuàng)建這個(gè)樣板配置。
????下圖顯示了Spring如何工作的高級(jí)視圖。您的應(yīng)用程序類與配置元數(shù)據(jù)相結(jié)合,這樣,在創(chuàng)建并初始化ApplicationContext之后,您就擁有了一個(gè)完全配置和可執(zhí)行的系統(tǒng)或應(yīng)用程序。

? ? 1.2.1 配置元數(shù)據(jù)
????如上圖所示,Spring IoC容器使用配置元數(shù)據(jù)的一種形式。此配置元數(shù)據(jù)表示作為應(yīng)用程序開發(fā)人員,您如何告訴Spring容器實(shí)例化、配置和組裝應(yīng)用程序中的對(duì)象。
????配置元數(shù)據(jù)通常以簡單直觀的XML格式提供,本章的大部分內(nèi)容都使用這種格式來傳達(dá)Spring IoC容器的關(guān)鍵概念和特性。
? ? 注意:基于xml的元數(shù)據(jù)不是惟一允許的配置元數(shù)據(jù)形式。Spring IoC容器本身與實(shí)際編寫配置元數(shù)據(jù)的格式完全解耦?,F(xiàn)在,許多開發(fā)人員為他們的Spring應(yīng)用程序選擇基于java的配置。
? ??有關(guān)在Spring容器中使用其他形式的元數(shù)據(jù)的信息,請(qǐng)參閱:
? ? ? ? 1. 基于注釋的配置:spring2.5引入了對(duì)基于注釋的配置元數(shù)據(jù)的支持。
? ? ? ? 2.?基于java的配置:從Spring 3.0開始,Spring JavaConfig項(xiàng)目提供的許多特性成為Spring核心框架的一部分。因此,您可以使用Java而不是XML文件來定義應(yīng)用程序類外部的bean。要使用這些新特性,請(qǐng)參見@Configuration、@Bean、@Import和@DependsOn注釋。
? ??Spring配置由容器必須管理的至少一個(gè)和通常多個(gè)bean定義組成?;趚ml的配置元數(shù)據(jù)將這些bean配置為頂級(jí)<beans/>元素中的<bean/>元素。Java配置通常在@Configuration類中使用@ bean注釋的方法。
????這些bean定義對(duì)應(yīng)于組成應(yīng)用程序的實(shí)際對(duì)象。通常,您要定義服務(wù)層對(duì)象、數(shù)據(jù)訪問對(duì)象(DAOs)、表示對(duì)象(如Struts Action實(shí)例)、基礎(chǔ)設(shè)施對(duì)象(如Hibernate SessionFactories)、JMS隊(duì)列等等。通常,不會(huì)在容器中配置細(xì)粒度域?qū)ο螅驗(yàn)橥ǔS蒁AOs和業(yè)務(wù)邏輯負(fù)責(zé)創(chuàng)建和加載域?qū)ο?。但是,您可以使用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屬性是標(biāo)識(shí)單個(gè)bean定義的字符串。
class屬性定義bean的類型并使用完全限定的類名。
? ? 1.2.2 實(shí)例化容器
????提供給ApplicationContext構(gòu)造函數(shù)的位置路徑是資源字符串(可以是多個(gè)),它允許容器從各種外部資源(如本地文件系統(tǒng)、Java類路徑等)裝載配置元數(shù)據(jù)。
? ? Java:?ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
? ? Kotlin:val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
? ??在了解了Spring的IoC容器之后,您可能想了解更多關(guān)于Spring的資源抽象(如參考資料中所述)的信息,它提供了一種方便的機(jī)制,用于從URI語法中定義的位置讀取InputStream。特別是,資源路徑用于構(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類和JpaAccountDao和JpaItemDao類型的兩個(gè)數(shù)據(jù)訪問對(duì)象(基于JPA對(duì)象-關(guān)系映射標(biāo)準(zhǔn))組成。property name元素引用JavaBean屬性的名稱,而ref元素引用另一個(gè)bean定義的名稱。id和ref元素之間的這種鏈接表達(dá)了協(xié)作對(duì)象之間的依賴關(guān)系。有關(guān)配置對(duì)象依賴項(xiàng)的詳細(xì)信息,請(qǐng)參閱依賴項(xiàng)。
????組合基于xml的配置元數(shù)據(jù)
????讓bean定義跨越多個(gè)XML文件可能很有用。通常,每個(gè)單獨(dú)的XML配置文件表示體系結(jié)構(gòu)中的邏輯層或模塊。
????可以使用應(yīng)用程序上下文構(gòu)造函數(shù)從所有這些XML片段加載bean定義。此構(gòu)造函數(shù)接受多個(gè)資源位置,如前一節(jié)所示。或者,使用一個(gè)或多個(gè)<import/>元素來從另一個(gè)或多個(gè)文件加載bean定義。下面的例子演示了如何做到這一點(diǎn):
<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è)文件加載的: service.xml,? messageSource.xml和themeSource.xml。所有位置路徑都相對(duì)于執(zhí)行導(dǎo)入的定義文件,因此services.xml必須與執(zhí)行導(dǎo)入的文件位于相同的目錄或類路徑位置,而messageSource.xml和themeSource.xml必須位于導(dǎo)入文件位置之下的資源位置??梢钥吹剑懊娴男备鼙缓雎粤?。但是,考慮到這些路徑是相對(duì)的,所以最好不要使用斜杠。根據(jù)Spring模式,要導(dǎo)入的文件的內(nèi)容,包括頂級(jí)的<beans/>元素,必須是有效的XML bean定義。
注意:可以但不推薦,引用文件在父目錄使用一個(gè)父路徑"../”。這樣做會(huì)在當(dāng)前應(yīng)用程序之外的文件上創(chuàng)建一個(gè)依賴項(xiàng)。特別地,對(duì)于classpath: URLs(例如,classpath:.. ./services.xml),不推薦使用這個(gè)引用,因?yàn)檫\(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)屬性解析。
????命名空間本身提供了import指令特性。除了普通bean定義之外,還有一些配置特性可以在Spring提供的XML命名空間選擇中找到——例如,上下文和util命名空間。
???Groovy Bean 定義 DSL
????作為外部化配置元數(shù)據(jù)的另一個(gè)示例,bean定義也可以用Spring的Groovy bean定義DSL表示,這在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是能夠維護(hù)不同bean及其依賴項(xiàng)的注冊(cè)表的高級(jí)工廠的接口。通過使用方法T getBean(String name, Class<T> requiredType),可以檢索bean的實(shí)例。
ApplicationContext允許您讀取bean定義并訪問它們,如下面的示例所示:
Java:
// 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();?Kotlin:
import org.springframework.beans.factory.getBean
?// create and configure beans
val context = ClassPathXmlApplicationContext("services.xml", "daos.xml")
?// retrieve configured instance
val service = context.getBean<PetStoreService>("petStore")
?// use configured instance
var userList = service.getUsernameList()?
對(duì)于Groovy配置,引導(dǎo)看起來非常類似。它有一個(gè)不同的上下文實(shí)現(xiàn)類,它支持groovy(但也理解XML bean定義)。下面的示例顯示了Groovy配置:
Java:
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
Kotlin:
val context = GenericGroovyApplicationContext("services.groovy", "daos.groovy")
最靈活的變體是GenericApplicationContext與reader委托相結(jié)合—。例如,XmlBeanDefinitionReader用于XML文件,如下例所示:
Java:
GenericApplicationContext context = new GenericApplicationContext();new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();Kotlin:
val context = GenericApplicationContext()GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy")context.refresh()
您可以在相同的ApplicationContext上混合和匹配這樣的reader委托,從不同的配置源讀取bean定義。
然后可以使用getBean檢索bean的實(shí)例。ApplicationContext接口有一些用于檢索bean的其他方法,但在理想情況下,應(yīng)用程序代碼不應(yīng)該使用它們。實(shí)際上,您的應(yīng)用程序代碼應(yīng)該完全不調(diào)用getBean()方法,因此完全不依賴于Spring APIs。例如,Spring用于web框架的集成為各種web框架組件(如控制器和jsf管理的bean)提供了依賴項(xiàng)注入,允許您通過元數(shù)據(jù)(如自動(dòng)裝配注釋)聲明對(duì)特定bean的依賴項(xiàng)。