前言
這個系列的文章,以Spring5.*的官方文檔爲藍本,經過翻譯、整理和簡化而成,沒有添加任何其他來源的內容。因此,內容的正確性是有保證的;不好的地方是,由於本人功力有限,翻譯整理後的文字流暢性不是很好。另一方面,Spring的官方文檔,本身就不具備深入淺出、實例豐富的特點,本人寫作的時候也就不提更高的要求了;但對於一些有疑問的點,還是通過實例代碼稍微測試了一下,這些代碼放在git上,文章裏面必要時會提及。
第一篇介紹Spring容器,基本上使用Spring的人都必然已經瞭解這些知識了,權當熱身吧。
容器簡介
Spring最基礎的功能就是實現了Ioc容器,Ioc也可以叫做DI(Dependency injection)。容器管理的對象稱之爲bean,容器負責bean的創建、初始化、配置、裝配。 Ioc體現在,bean只需聲明對其他的bean的依賴,不負責創建後者,也不需要直到後者如何被創建;容器在裝配過程中負責注入這些依賴。
容器如何初始化、配置並組裝bean呢?它需要一份對bean的定義數據,這個數據叫做配置元數據(configuration metadata)。最新版本的Spring允許我們通過xml、java註解兩種方式來提供元數據。
org.springframework.context
這個package包含了容器相關的基礎類和接口定義,其中接口BeanFactory定了最基本的bean管理方法,接口ApplicationContext擴展了BeanFactory,添加了AOP,MessageSource,事件機制,Context層級結構的支持。我們在應用中使用的context實例,基本都實現了ApplicationContext,在需要訪問當前context的場景下,我們基本也都使用ApplicationContext這個接口。
對於非Web應用,我們一般創建ClassPathXmlApplicationContext
或FileSystemXmlApplicationContext
類型的容器實例。顧名思義,前者從classpath加載配置文件,後者從文件系統路徑加載配置文件。xml是傳統的配置數據格式,我們可以在xml裏面通過聲明來開啓對java註解配置的支持(具體方式後面會介紹)。對於web應用,不需要手動創建容器實例,Spring MVC框架會自動創建。
總之,在應用啓動時,容器讀入配置數據,完成對bean的初始化和裝配工作,得到一個完全初始化,可提供服務的應用系統。
配置元數據
開發者通過配置元數據告訴容器如何初始化、配置和組裝bean。傳統上,配置元數據通過xml格式來提供,它的特點是所有配置集中在一起,容易看明白,特別適合用作示例,所以本章節及後續(在講到註解配置之前)主要使用xml格式。
下面是一個簡單的配置文件sever.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="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>
頂層beans標籤,包含一個或多個bean標籤,每個bean標籤定義一個bean,class屬性指明要創建的bean對應的java類型。
另一個配置文件dao.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>
容器初始化
假設上面兩個xml位於類路徑下面,那麼我們可以這樣來創建容器:
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
我們還可以換一種方式,讓server.xml來導入dao.xml,Context加載server.xml即可。
--services.xml
<beans>
<import resource="daos.xml"/>
<!-- services.xml自身的定義在這 -->
<beans>
注意import標籤的resource屬性指向的路徑是services.xml所在位置的相對路徑。
初始化完成之後,可以調用getBean方法來獲取一個bean的實例:
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
Bean介紹
Spring容器通過配置數據來獲取bean的定義,每個bean的定義會被讀取爲一個BeanDefinition對象,它包含以下信息:
- bean對象對應的完全限定類名;
- 行爲相關屬性,比如scope,lefcycle回調等;
- 對其他bean的依賴引;
- 屬性值的配置
Spring的容器實現還允許將容器外部創建的對象註冊到容器裏面,方法如下:
DefaultListableBeanFactory factory = context.getBeanFactory();
factory.registerSingleton(beanName,objectInstance);
bean的命名
bean可以有一個或多個身份標識,每個標識在容器中都應該是唯一的。在xml配置中,bean的id屬性可以配置單個標識,name屬性可以配置逗號分隔的多個標識。
如果我們不提供任何標識定義,容器會生成一個內部唯一的標識。在xml裏面,通過bean名字引用bean是很常見的,比如services.xml裏面petStore的屬性配置中,ref標籤指向名叫dao的bean。
注1:bean的命名應該和變量命名一樣,採用駝峯命名法;
注2:spring在給bean自動命名時,會嘗試使用類名,並將第一個字符變成小寫;但如果類名的前兩個字符都是大小,那麼第一個字符保持大寫;如果類名是NameBean,生成的名字就是nameBean,如果類名是NAmeBean,那麼生成的名字是NAameBean。
我們可以額外給bean添加別名,這往往發生在大型系統裏面,A某塊導入了B模塊定義的bean,卻想換個名字:
<alias name="fromName" alias="toName"/>
bean的創建
bean的定義可以配置3種bean的創建方式:
1、指定bean的類名,默認通過對構造函數創建,這是最常見的方式;
2、指定bean的類名,以及一個靜態工廠方法:
//類定義
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
//bean配置
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
這個配置指示容器調用examples.ClientService.createInstance()來創建名叫clientService的bean。
3、指定bean的工廠bean,以及一個工廠方法:
//工廠類
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
//工廠bean和bean
<bean id="serviceLocator" class="examples.DefaultServiceLocator"/>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
上面的配置中,clientService是由另一個bean serviceLocator創建的,此時後者被稱之爲FactoryBean。
總結
- Spring的容器是用來管理bean的,包括bean的創建、bean的初始化,以及依賴注入;
- bean的定義和創建是完全解耦的,使得Spring很方便地支持多種bean的定義格式:xml,註解,java代碼或它們的混合;
- 不管何種形式,bean定義在Spring內部表示爲BeanDefinition數據結構;
- BeanDefinition是bean的元數據(相當於菜譜),容器在需要時基於元數據創建bean的實例;
這是Spring容器的核心理念,也是整個Spring技術框架體系的基石。
示例代碼的container_xml模塊,展示了最基本的容器功能。