Spring的Bean管理

本博客基於SpringSourceCodeTest倉庫中【SpringDemo1.0】tag進行的編寫。

目錄

Spring的Bean管理

基於配置文件進行bean管理的方案是Spring初期推行的方式,在歷史比較悠久的Spring項目中比較常見。

基於掃描的方式裝載bean

一個比較典型的配置文件通常包含<context>標籤, <context>用來表示該項目中哪些包中的bean需要被進行生命週期管理。

默認情況下,使用<context>後,Spring會默認掃描定義的帶有如下註解的bean:

  • @Component
  • @Repository
  • @Service
  • @Controller
  • @RestController
  • @ControllerAdvice
  • @Configuration

參考項目src/main/resources/beans.xml,一個典型的<context>文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--默認會掃描包文件下的@Controller, @Service, @Repository, @Component等常見註解-->
    <context:component-scan base-package="com.springtest.demo"></context:component-scan>
</beans>

XML格式解讀

在這份xml中,我們可以稍微簡單說明都是做了一些什麼事情。

  1. 首先我們定義了一個<beans>的大標籤,用於生命這個配置文件是用於Spring的生命週期管理,因此我們使用Spring-bean提供的命名空間,"xmlns="http://www.springframework.org/schema/beans"
  2. 在下文中需要使用<context>標籤,因此我們定義context所使用的命名空間xmlns:context="http://www.springframework.org/schema/context"
  3. 然後我們定義我們需要掃描的包的位置<context:component-scan base-package="com.springtest.demo"></context:component-scan>
  4. 至此我們的xml其實就已經不會報錯了,說明此事我們的xml從結構上已經沒有明顯問題;
  5. 但是此時啓動Spring啓動依然會失敗並且報錯Cannot find the declaration of element 'beans'.,因此我們需要繼續補充這份這份xml的schema文件,用來描述我們的xml中的標籤行爲;
  6. 此時我們就需要引入我們的xsi:schemaLocation屬性,爲此我們首先需要引入xsi的前綴命名空間xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  7. 最後我們補充完整的xsi:schemaLocation解析我們所引用的兩個命名空間"http://www.springframework.org/schema/beans""http://www.springframework.org/schema/context"即可。
  8. 附:xmlns即xml namespace的縮寫; xsi即xml schema instance的縮寫; xsd即xml schema definition的縮寫
<?xml version="1.0" encoding="UTF-8"?>
<beans
       <!-- 表明整個beans標籤使用的是xmlns所指向的命名空間 -->
       xmlns="http://www.springframework.org/schema/beans"
       <!-- 表明子節點中context標籤使用的是xmlns:contex所指向的命名空間 -->
       xmlns:context="http://www.springframework.org/schema/context"
       <!-- 表明xsi屬性標籤使用的是xmlns:xsi所指向的命名空間 -->
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       <!-- 表明每一個命名空間具體指向的描述文件(xsd文件) -->
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!--默認會掃描包文件下的@Controller, @Service, @Repository, @Component等常見註解-->
    <context:component-scan base-package="com.springtest.demo"></context:component-scan>
</beans>

XML內容解讀

這裏context:component-scan定義了我們的組件掃描的範圍,我們直接定義到整個項目的根節點com.springtest.demo,這樣這個包內所有的組件都會被掃描並且組裝到上下文中供我們使用。

這裏我們暫時不深究,留在將來“模仿”Spring的時候再來。

本部分主要測試文件以及相關文件清單爲:

  1. test.ConfigurationTest#test1 – 測試函數
  2. src/main/resources/beans.xml – 配置文件

基於定義的方式裝載bean

第二種方式和第一種方式很像,兩者可以在同一個xml中同時使用。

<bean>用來表示該項目中context初始化完成後,有哪些bean需要被補充加載,同時這些bean被裝載過程中,以及被銷燬時,要指定哪些動作,都是在這裏定義的。其內部可使用的參數列表如下圖所示(我的項目中Spring版本爲5.2.2):

bean標籤可定義屬性

其中我們常用的屬性爲:

  • name: 用於標記bean的名字
  • class: 用於標記需要被實例化的class
  • scope: singleton表示單例; prototype表示每次getBean會獲得一個實例; request表示每次請求獲得一個實例; session表示每次灰化獲得一個實例
  • lazy-init: 表示裝載時間,如果爲false表示Spring啓動時裝載,如果爲true表示getBean時裝載; 默認是false
  • init-method: 當bean構造完成並且屬性賦值完成之後,裝載進context之前被執行的方法,不能帶參數,可以拋出異常。
    • 也可以通過實現Spring的InitializingBean接口;
    • 或者使用標準的@PostConstruct實現該功能
  • destroy-method: 當bean被銷燬時,需要被執行的方法,不能帶參數,可以拋出異常。
    • 也可以通過實現Spring的DisposableBean接口;
    • 或者實現Java的Closeable/AutoCloseable接口;
    • 或者使用標準的@PreDestroy註解實現該功能
  • factory-method: 定義實例化對象時調用的函數

一個比較常見的純使用bean標籤定義需要被裝載的組件的配置文件如下:

<?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
        http://www.springframework.org/schema/beans/spring-beans.xsd>

    <bean id="person" class="com.springtest.demo.entity.Person" name="person" scope="prototype" >
    </bean>
</beans>

不難發現,這種方式定義上下文裝載bean時,我們不需要對我們的類做任何註解(不需要@Component等)。

本部分主要測試文件以及相關文件清單爲:

  1. test.ConfigurationTest#test1 – 測試函數
  2. src/main/resources/beans.xml – 配置文件
  3. com.springtest.demo.entity.Person – 被實例化的類

基於註解的方式裝載bean

廢話不多說,直接上源碼。相關詳細測試需要在項目中運行才能直觀感受到(ConfigurationTest.test3),這裏只做粗略解釋。相關內容需要閱讀註釋和各個註解的源碼。

package com.springtest.demo.config;

import com.springtest.demo.config.typefilter.PersonTypeFilter;
import com.springtest.demo.entity.Person;
import com.springtest.demo.service.PersonService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

// 配置類等效配置文件
@Configuration(value = "beansConfig2")  // 表示這是一個配置類
//@ComponentScan(value = "com.springtest.demo" )  // 表示需要掃描的包,可以嘗試註釋該行然後運行ConfigurationTest.test3查看區別
@ComponentScan(value = "com.springtest.demo",
        // 定製掃描規則,默認掃描@Controller, @Service, @Repository, @Component,控制排除哪些組件不需要被包含
         //excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Controller.class),
        useDefaultFilters = false,
        includeFilters = {// 定製掃描規則,控制只掃描哪些組件,需要關閉默認Filter
                // @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}), // FilterType.ANNOTATION 按照註解方式進行操作
                //@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {PersonService.class}),  // FilterType.ASSIGNABLE_TYPE 按照指定類型的方式進行組件掃描
                // FilterType.ASPECTJ  比較不常用,指定AspectJ表達式進行組件掃描
                // FilterType.REGEX  指定符合正則的包進行掃描
                // FilterType.CUSTOM // 使用自定義規則
                @Filter(type = FilterType.CUSTOM,classes = {PersonTypeFilter.class})  // 使用自定義規則
        }
)

// Component是一個可重複定義組件: @Repeatable
//@ComponentScan
public class BeansConfig {
    @Bean(name = "person3")
    public Person person01() {
        Person person = new Person();
        person.setAge(20);
        person.setName("lisi");
        return person;
    }
}

上述代碼中對@ComponentScan中各種掃描規則以及掃描過程中過濾器@Filter的用法做了一些demo。詳細實現需要再運行源碼體會一下。主要使用內容如下:

  • ComponentScan註解:
    • includeFilters需要包含的掃描範圍定義
    • excludeFilters 不需要包含的掃描範圍定義
    • @Filte註解:用於上述兩種掃描規則的具體過濾規則定義,其Filterype分爲如下幾種過濾規則:
      • FilterType.ANNOTATION:對不同的註解進行掃描
      • FilterType.ASSIGNABLE_TYPE:對不同的類名進行規則掃描
      • FilterType.ASPECTJ: 比較不常用,指定AspectJ表達式進行組件掃描
      • FilterType.REGEX: 指定符合正則的包進行掃描
      • FilterType.CUSTOM:使用自定義規則,上述集中規則應該均由該方式實現

注1: 由於Config自身首先需要進行註冊然後才能進行相關的執行,因此Config類本身是肯定會被註冊的。

本部分主要測試文件以及相關文件清單爲:

  1. test.ConfigurationTest#test2 – 測試函數
  2. com.springtest.demo.config.BeansConfig – 註解掃描的定義類
  3. com.springtest.demo.entity.Person – 被實例化的類
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章