容器概述

The org.springframework.context.ApplicationContext 介面代表 Spring IoC 容器,負責例項化、配置和組裝 bean。容器透過讀取配置元資料來獲取例項化、配置和組裝元件的指令。配置元資料可以表示為帶註解的元件類、帶有工廠方法的配置類,或者外部 XML 檔案或 Groovy 指令碼。無論採用哪種格式,你都可以構建你的應用以及這些元件之間豐富的相互依賴關係。

ApplicationContext 介面的幾種實現在 Spring 核心中。在獨立應用中,通常建立 AnnotationConfigApplicationContextClassPathXmlApplicationContext 的例項。

在大多數應用場景中,不需要顯式使用者程式碼來例項化一個或多個 Spring IoC 容器例項。例如,在普通 Web 應用場景中,應用 web.xml 檔案中的簡單樣板 Web 描述符 XML 就足夠了(參閱 Web 應用的便捷 ApplicationContext 例項化)。在 Spring Boot 場景中,應用上下文會根據常見的設定約定為你隱式啟動。

下圖展示了 Spring 的高階工作檢視。您的應用類與配置元資料相結合,這樣在建立和初始化 ApplicationContext 後,您將擁有一個完全配置好的可執行系統或應用。

container magic
圖 1. Spring IoC 容器

配置元資料

如前圖所示,Spring IoC 容器消費某種形式的配置元資料。這些配置元資料代表了您作為應用開發者如何告訴 Spring 容器例項化、配置和組裝應用中的元件。

Spring IoC 容器本身與配置元資料實際編寫的格式完全解耦。如今,許多開發者為他們的 Spring 應用選擇 基於 Java 的配置

Spring 配置至少包含一個(通常不止一個)由容器管理的 bean 定義。Java 配置通常在 @Configuration 類中使用 @Bean 註解的方法,每個方法對應一個 bean 定義。

這些 bean 定義對應於構成您應用的實際物件。通常,您會定義服務層物件、持久層物件(例如倉庫或資料訪問物件 (DAO))、表示層物件(例如 Web 控制器)、基礎設施物件(例如 JPA EntityManagerFactory、JMS 佇列等)。通常,容器中不配置細粒度的領域物件,因為建立和載入領域物件通常是倉庫和業務邏輯的責任。

XML 作為外部配置 DSL

基於 XML 的配置元資料將這些 bean 配置為頂級 <beans/> 元素內的 <bean/> 元素。以下示例展示了基於 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="..."> (1) (2)
		<!-- 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>
1 id 屬性是一個字串,用於標識單個 bean 定義。
2 class 屬性定義了 bean 的型別,使用完全限定類名。

id 屬性的值可用於引用協作物件。本示例未展示引用協作物件的 XML。有關更多資訊,請參閱依賴

為了例項化容器,需要向 ClassPathXmlApplicationContext 建構函式提供 XML 資原始檔的位置路徑或多個路徑,該建構函式允許容器從各種外部資源(例如本地檔案系統、Java CLASSPATH 等)載入配置元資料。

  • Java

  • Kotlin

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

學習 Spring 的 IoC 容器後,您可能想了解更多關於 Spring 的 Resource 抽象(如資源中所述)的資訊,它提供了一種方便的機制,可以從 URI 語法定義的位置讀取 InputStream。特別是,Resource 路徑用於構建應用上下文,如應用上下文和資源路徑中所述。

以下示例展示了服務層物件 (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 類和兩個資料訪問物件組成,型別分別為 JpaAccountDaoJpaItemDao(基於 JPA 物件關係對映標準)。property name 元素引用 JavaBean 屬性的名稱,ref 元素引用另一個 bean 定義的名稱。idref 元素之間的這種關聯表達了協作物件之間的依賴關係。有關配置物件依賴項的詳細資訊,請參閱依賴

組合基於 XML 的配置元資料

將 bean 定義分佈在多個 XML 檔案中會很有用。通常,每個單獨的 XML 配置檔案代表架構中的一個邏輯層或模組。

您可以使用 ClassPathXmlApplicationContext 建構函式從 XML 片段載入 bean 定義。這個建構函式接受多個 Resource 位置,如上一節所示。或者,使用一個或多個 <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.xmlmessageSource.xmlthemeSource.xml。所有位置路徑都相對於進行匯入的定義檔案,因此 services.xml 必須與進行匯入的檔案位於同一目錄或類路徑位置,而 messageSource.xmlthemeSource.xml 必須位於匯入檔案位置下的 resources 位置。正如您所見,開頭的斜槓會被忽略。但是,考慮到這些路徑是相對的,最好根本不使用斜槓。被匯入檔案的內容(包括頂級 <beans/> 元素)必須根據 Spring Schema 是有效的 XML bean 定義。

可以使用相對路徑 "../" 引用父目錄中的檔案,但不推薦這樣做。這樣做會建立對當前應用外部檔案的依賴。特別是,對於 classpath: URL(例如,classpath:../services.xml),不推薦使用這種引用,因為執行時解析過程會選擇“最近的”類路徑根目錄,然後查詢其父目錄。類路徑配置更改可能導致選擇不同且不正確的目錄。

您始終可以使用完全限定的資源位置而不是相對路徑:例如,file:C:/config/services.xmlclasspath:/config/services.xml。但是,請注意,這樣做會將您的應用配置與特定的絕對位置耦合。通常最好保持對這些絕對位置的間接引用 — 例如,透過在執行時針對 JVM 系統屬性解析的 "${…​}" 佔位符。

名稱空間本身提供了匯入指令功能。Spring 提供的一系列 XML 名稱空間中提供了超出普通 bean 定義的更多配置特性 — 例如,contextutil 名稱空間。

Groovy Bean 定義 DSL

作為外部化配置元資料的另一個示例,bean 定義也可以使用 Spring 的 Groovy Bean Definition 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 定義檔案。

使用容器

The ApplicationContext 是一個高階工廠介面,能夠維護不同 bean 及其依賴項的登錄檔。透過使用方法 T getBean(String name, Class<T> requiredType),您可以檢索 bean 的例項。

The ApplicationContext 允許您讀取 bean 定義並訪問它們,如下例所示

  • Java

  • Kotlin

// 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();
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()

使用 Groovy 配置,啟動過程看起來非常相似。它有一個不同的上下文實現類,該類感知 Groovy(但也理解 XML bean 定義)。以下示例展示了 Groovy 配置

  • Java

  • Kotlin

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

最靈活的變體是 GenericApplicationContext 結合讀者委託(reader delegates)— 例如,使用 XmlBeanDefinitionReader 處理 XML 檔案,如下例所示

  • Java

  • Kotlin

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

您也可以對 Groovy 檔案使用 GroovyBeanDefinitionReader,如下例所示

  • Java

  • Kotlin

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

您可以在同一個 ApplicationContext 上混合和匹配這些讀者委託,從不同的配置源讀取 bean 定義。

然後,您可以使用 getBean 來檢索 bean 的例項。ApplicationContext 介面還有一些用於檢索 bean 的其他方法,但理想情況下,您的應用程式碼永遠不應使用它們。實際上,您的應用程式碼根本不應該呼叫 getBean() 方法,因此完全不依賴於 Spring API。例如,Spring 與 Web 框架的整合提供了對各種 Web 框架元件(如控制器和 JSF 管理的 bean)的依賴注入,允許您透過元資料(例如自動裝配註解)宣告對特定 bean 的依賴。