Spring基礎一:容器和Bean

前言

這個系列的文章,以Spring5.*的官方文檔爲藍本,經過翻譯、整理和簡化而成,沒有添加任何其他來源的內容。因此,內容的正確性是有保證的;不好的地方是,由於本人功力有限,翻譯整理後的文字流暢性不是很好。另一方面,Spring的官方文檔,本身就不具備深入淺出、實例豐富的特點,本人寫作的時候也就不提更高的要求了;但對於一些有疑問的點,還是通過實例代碼稍微測試了一下,這些代碼放在git上,文章裏面必要時會提及。

Spring官方文檔地址

第一篇介紹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應用,我們一般創建ClassPathXmlApplicationContextFileSystemXmlApplicationContext類型的容器實例。顧名思義,前者從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對象,它包含以下信息:

  1. bean對象對應的完全限定類名;
  2. 行爲相關屬性,比如scope,lefcycle回調等;
  3. 對其他bean的依賴引;
  4. 屬性值的配置

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。

總結

  1. Spring的容器是用來管理bean的,包括bean的創建、bean的初始化,以及依賴注入;
  2. bean的定義和創建是完全解耦的,使得Spring很方便地支持多種bean的定義格式:xml,註解,java代碼或它們的混合;
  3. 不管何種形式,bean定義在Spring內部表示爲BeanDefinition數據結構;
  4. BeanDefinition是bean的元數據(相當於菜譜),容器在需要時基於元數據創建bean的實例;

這是Spring容器的核心理念,也是整個Spring技術框架體系的基石。

示例代碼的container_xml模塊,展示了最基本的容器功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章