接上篇:Spring 框架概述
version 5.1.8.RELEASE
這部分參考文檔涵蓋了 Spring Framework 所有絕對不可或缺的技術。
其中最重要的是 Spring Framework 的控制反轉(IoC)容器。在介紹完 Spring 框架的 IoC 容器之後,緊接着全面介紹 Spring 的面向切面編程(AOP)技術。Spring Framework 有自己的 AOP 框架,它在概念上易於理解,並且成功地定位了 Java 企業編程中 AOP 需求的 80% 最佳擊球點。
Spring 提供與 AspectJ (目前功能最豐富,也是Java企業領域中最成熟的 AOP 實現)的集成。
1. IoC容器
本章介紹 Spring 的控制反轉(IoC)容器。
1.1 Spring IoC 容器和 Bean 簡介
本章介紹了 Spring Framework 控制反轉(IoC)的實現原理。IoC 也稱爲依賴注入(DI)。通過這個機制,對象可以通過構造方法參數、工廠方法參數以及通過工廠方法構建或返回的實例上設置的屬性來定義它們的依賴關係(即它們使用的其他對象)。然後容器在創建 bean 時注入這些依賴項。這個過程從根本上反轉了 Bean 自身通過直接調用構造方法或服務定位模式等機制來控制實例化或定位其依賴的模式,因此叫做控制反轉。
org.springframework.beans
和 org.springframework.context
包是 Spring 框架的 IoC 容器的基礎。BeanFactory
接口提供了一種能夠管理任何類型對象的高級配置機制。ApplicationContext
是 BeanFactory 的子類。它補充的內容有:
- 更容易與 Spring 的 AOP 功能集成
- 消息資源處理(用於國際化)
- 事件發佈
- 應用層特定的上下文,例如在 Web 應用程序中使用的 WebApplicationContext
簡而言之,BeanFactory
提供了配置框架和基本功能,ApplicationContext
添加了更多針對企業級的功能。ApplicationContext
是 BeanFactory
完整的超集,在本章中僅用它描述 Spring IoC 容器。有關使用 BeanFactory
而不是 ApplicationContext
的更多信息 請參考 BeanFactory。
在 Spring 中,構成應用程序架構並由 Spring IoC 容器管理的對象稱爲 beans。bean 是一個由 Spring IoC 容器實例化、組裝或管理的對象。除此之外,bean 只是應用程序中衆多對象之一。Bean 及其之間的依賴關係反映在容器使用的配置元數據中。
1.2 容器概覽
org.springframework.context.ApplicationContext
接口代表 Spring IoC 容器,負責實例化、配置和組裝 bean。容器通過讀取配置元數據獲取有關需要實例化、配置和組裝對象的指令。配置元數據以 XML、Java 註解或 Java 代碼表示,通過它可以表示構成應用的對象以及對象之間豐富的依賴關係。
Spring 提供了多種 ApplicationContext
接口實現。在傳統單機應用中,通常會創建一個 ClassPathXmlApplicationContext
或 FileSystemXmlApplicationContext
的實例。雖然 XML 是定義配置元數據的傳統格式,但你也可以通過提供少量 XML 配置聲明容器啓用對其他元數據格式的支持後使用 Java 註解或代碼作爲元數據格式。
在大多數應用程序方案中,不需要顯式使用代碼來實例化 Spring IoC 容器實例。例如,在 Web 應用程序場景中,應用程序文件 web.xml 中一個 8 行左右的 web 描述模板 XML 就足夠了(請參閱 Web 應用程序快速實例化 ApplicationContext )。如果你使用 Spring Tool Suite(一個基於 Eclipse 的開發環境),只需點擊幾下鼠標或按幾下鍵盤即可輕鬆創建此模板配置。
下圖是 Spring 工作原理的高級視圖。應用程序類與配置元數據相結合,在 ApplicationContext
創建並初始化之後,即可擁有完全配置且可執行的系統或應用程序。
[站外圖片上傳中...(image-79637a-1561521669629)]
1.2.1 配置元數據
如上圖所示,Spring IoC 容器使用一系列配置元數據。這些配置元數據描述了Spring 容器在應用程序中如何實例化,配置和組裝對象。
傳統配置元數據使用簡單直觀的 XML 格式,本章大部分內容也是用 XML 來表達 Spring IoC 容器的關鍵概念和功能。
XML 不是唯一的配置元數據格式。Spring IoC 容器與實際編寫配置元數據的格式完全解耦。目前,許多開發人員爲其 Spring 應用程序選擇基於 Java 的配置。
有關在 Spring 容器中使用其他形式的元數據的信息,請參閱:
- 基於註解的配置:Spring 2.5 引入了對基於註解的配置元數據的支持。
-
基於Java的配置:從 Spring 3.0 開始,Spring JavaConfig 項目提供的許多功能成爲 Spring Framework 核心的一部分。因此,你可以使用 Java 而不是 XML 文件來定義應用程序類外部的 bean。要使用這些新功能,請參閱
@Configuration
,@Bean
,@Import
,和@DependsOn
註解。
Spring 配置信息由至少一個(通常不止一個) 必須由容器進行管理的 bean 定義組成。基於 XML 的配置元數據將這些 bean 配置爲頂級元素 <beans/>
內的 <bean/>
元素。基於 Java 的配置通常在使用 @Configuration
註解的類中使用帶有 @Bean
註解的方法。
這些 bean 定義對應構成應用程序的實際對象。我們一般會定義服務層對象,數據訪問對象(DAO),展示層對象(例如 Struts Action 實例),基礎結構對象(例如 Hibernate SessionFactories、JMS Queues 等)。一般不會在容器中配置細粒度的域對象,因爲通常由 DAO 和業務邏輯負責創建和加載域對象。然而,你可以使用 Spring 集成 AspectJ 來配置非 IoC 容器創建的對象。請參閱使用 Spring 和 AspectJ 進行域對象的依賴注入。
以下示例顯示了基於 XML 的配置元數據的基本結構:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">①②
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
① id 屬性是一個標識單個 bean 定義的字符串
② class 屬性使用完整的類名定義 bean 的類型
id 屬性的值表示協作對象。在此示例中未包含用於引用協作的對象的 XML。有關更多信息,請參閱依賴。
1.2.2 實例化容器
提供給 ApplicationContext 構造函數的位置路徑是一個資源字符串,它允許容器從各種外部資源加載配置元數據,例如本地文件系統、Java 環境變量等。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
在瞭解了 Spring 的 IoC 容器之後,你可能想要了解有關 Spring
資源
抽象化(參考資源
描述)的更多信息,特別是Resource
路徑用於構建應用程序上下文(請參考應用程序上下文和資源路徑),它提供了一種便捷的機制從 URI 語句中定義的位置讀取 InputStream。。
以下示例展示了服務層對象配置文件(services.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
以下示例展示了數據訪問對象文件(daos.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
在前面的示例中,服務層由 PetStoreServiceImpl
類和兩個數據訪問對象 JpaAccountDao
和 JpaItemDao
(基於 JPA 對象關係映射標準)組成。property
元素的 name
屬性指的是 JavaBean 屬性的名稱,ref
屬性指向另一個 bean 定義的名稱。元素 id
和 ref
之間的這種聯繫表達了協作對象之間的依賴關係。有關配置對象的依賴關係的詳細信息,請參閱依賴關係。
編寫基於XML的配置元數據
通常,每個單獨的 XML 配置文件都對應着架構中的邏輯層或模塊,讓 bean 定義在多個 XML 文件中生效會非常有用。
如上一節中所示,應用程序上下文構造函數可以使用多個 Resource 位置,它可以從這些 XML 片段中加載 bean 定義。另外也可以使用一個或多個 <import/>
元素從其他文件加載 bean 定義。以下示例展示瞭如何執行此操作:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
在前面的例子中,從三個文件中加載外部 Bean 定義,分別是 services.xml、messageSource.xml 和 themeSource.xml。對於執行導入的定義文件來說,所有路徑都是相對路徑,因此 services.xml 必須與執行導入的文件位於相同的目錄或環境變量, messageSource.xml 和 themeSource.xml 必須位於導入文件路徑下方的 resources 目錄中。正如你所見,前邊的斜槓會被忽略掉。鑑於提供的都是相對路徑,所以最好不要使用斜槓。這些文件中包括根據 Spring Schema 定義的正確的 XML Bean 在內的內容都會被導入,包括頂級元素 <beans/>
。
雖然可以使用相對路徑“../”引用父目錄中的文件,但不建議這樣使用,因爲這樣做會使得當前應用依賴程序之外的文件。非常不建議使用
classpath:
URL(例如,classpath:../services.xml)引用文件,因爲運行時解析過程會選擇“最近”的環境變量根目錄,然後查找其父目錄。環境變量配置的更改可能導致目錄選擇不正確。可以使用完整的資源位置替代相對路徑,例如,file:C:/config/services.xml 或 classpath:/config/services.xml。然而需要注意應用程序的配置將會與特定的絕對路徑耦合。通常最好爲這些絕對路徑保持間接聯繫,例如通過在運行時通過“$ {...}”佔位符替代 JVM 系統屬性。
命名空間本身提供了導入指令的功能。Spring 提供的一系列 XML 命名空間中提供了除普通 bean 定義之外的其他配置功能,例如 context 和 util 命名空間。
Groovy Bean 定義 DSL
作爲外化配置元數據的另一個示例,bean 定義也可以在 Spring 的 Groovy Bean 定義 DSL 中表示,就像 Grails 框架。通常此類配置位於“.groovy”文件中,其結構如下例所示:
beans {
dataSource(BasicDataSource) {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:mem:grailsDB"
username = "sa"
password = ""
settings = [mynew:"setting"]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
此配置樣式在很大程度上等同於 XML bean 定義,同樣支持 Spring 的 XML 配置命名空間。它還允許通過 importBeans
指令直接導入 XML bean 定義文件。
1.2.3 使用容器
ApplicationContext
是一個高級工廠接口,主要負責維護不同 bean 及其依賴項的註冊。通過使用 T getBean(String name, Class<T> requiredType)
方法可以獲得 Bean 的實例。
通過 ApplicationContext
可以讀取 bean 定義並訪問它們,如下例所示:
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
使用 Groovy 配置時,初始化程序看起來非常相似。不同的是使用了支持 Groovy 的上下文實現類(也支持 XML bean 定義)。以下示例展示了 Groovy 配置:
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
最靈活的使用方式是 GenericApplicationContext
與讀取器委派結合使用,例如針對 XML 文件使用 XmlBeanDefinitionReader
,如以下示例所示:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
還可以使用針對 Groovy 文件使用 GroovyBeanDefinitionReader
,如以下示例所示:
GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
你可以在相同的 ApplicationContext
中混合使用此類讀取器委託, 從不同的配置源讀取 bean 定義。
你可以使用 getBean
方法來獲取 Bean 實例。ApplicationContext
接口還有一些其他方法可以獲取 bean,但理想情況下你的應用程序不應該使用它們。實際上,你的應用程序代碼根本不應該調用 getBean()方法,也應該不依賴於 Spring API。例如,Spring 集成的 Web 框架爲各種 Web 框架組件(如控制器和 JSF 託管的 bean)提供依賴注入,以便通過元數據聲明對特定 bean 的依賴關係,例如自動裝配註解。