iT邦幫忙

2021 iThome 鐵人賽

DAY 8
0
Software Development

Wow ! There is no doubt about Learn Spring framework in a month.系列 第 8

[Day - 08] - Spring 通用注入各式元件運作與設計

Abstract

每個開發者在開發一套系統時,必定會開發些服務所需的相關元件,但如何方便建立這些元件又不佔用JVM的堆積(Heap)呢?就需要有一個容器暫存這些元件類別,此時,我們才選用Spring這套框架提供了多種注入方式,快速的從IoC配置池中進行注入開發者所需要的Bean元件,讓使用者成功地運用相關元件類別進行各類服務運算,在本章節中,我們提供了六種取得Bean類別元件方式,更佳的幫助讀者可透過這幾種方式可進一步獲得所需的解法。

Principle Introduction

無論使用註解(@Annotation)或XML方式獲取所需的Bean元件類別,如同前面章節所述,皆是透過BeanFactory進行獲取對應元件,由於本章節主要以註解方式進行注入元件,故以介紹@Autowired進行注入Bean元件原理為主,此方法主要透過AutowiredAnnotationBeanPostProcessor該類別進行實現,Spring主要採用CGLIB中的MethodInterceptor方法取得專案下的所有類別及註解,並將所有相關模式元件儲存於IoC配置池元件,我們稱此容器元件工廠為BeanFactory,當開發者配置@Autowired時,會自動注入相對應介面之元件或優先權(@Primary)最高之元件,若有多個繼承相同介面的元件及未有配置優先權(@Primary)之元件時,而開發者須配置預選元件名稱(@Qualifier),或透過ApplicationContext進行獲取Bean元件時給予對應預選元件名稱,由此可證,開發者所指定注入的物件,必須不可為共通性物件(Object),須為指定物件類別,透過此運作原理即可獲取您所配置的元件,相關範例如下。

相關Bean元件獲取方法

自動注入最高優先元件

    @Primary
    @Component("North")
    public class NorthCityComponent implements AreaCityComponent {


        @Override
        public List<CityModel> getCityList() {
          .....
          .....
          .....
        }
    }
    @Autowired
    AreaCityComponent primaryComponent;

透過@Qualifer配置預選元件名稱進行注入元件

    @Component("Center")
    public class CenterCityComponent implements AreaCityComponent {


        @Override
        public List<CityModel> getCityList() {
          .....
          .....
          .....
        }
    }
    
    @Qualifier("Center")
    @Autowired
    AreaCityComponent qualifierWithCenterAreaComponent;

透過@Qualifer配置預選元件名稱進行注入Override後的Bean元件

    @Bean("SouthBean")
    public SouthCityComponent getSouthCityComponent() {
        return new SouthCityComponent() {
            @Override
            public List<CityModel> getCityList() {
                List<CityModel> cityModels = southCityComponent.getCityList();

                cityModels = cityModels.stream().map(cityModel -> {
                    if (cityModel.getName().equalsIgnoreCase("Penghu County")) {
                        cityModel.setSpecialLocalProduct("Brown Sugar Steamed Cake");
                    }
                    return cityModel;
                }).collect(Collectors.toList());
                return cityModels;
            }
        };
    }
    
    @Qualifier("SouthBean")
    @Autowired
    AreaCityComponent overrideWithSouthBeanComponent;

透過ApplicationContext取得Bean元件

    AreaCityComponent eastCityComponent;
    
    @Autowired
    private ApplicationContext applicationContext;

    @Before
    public void init() {
        eastCityComponent =  applicationContext.getBean("East",AreaCityComponent.class);
        ........
    }

透過BeanFactory取得Bean元件

    AreaCityComponent fujianCityComponent;
    
    @Autowired
    BeanFactory beanFactory;

    @Before
    public void init() {
        ........
        fujianCityComponent = beanFactory.getBean("Fujian",AreaCityComponent.class);
    }

由上述範例我們可以看出,以@Autowired方式經由多層代理進行注入最方便,但透過BeanFactory直接進行注入取得是最快速的方式,供各位開發者作參考及引用。

Structure

由此架構圖我們可得知開發者所運用的注入式註解(@Autowired)皆透過SpringBeanAutowiringSupport此類別進行代理產生,其架構流程如圖一所示,所有注入式註解(@Autowired)標籤皆透過AutowiredAnnotationBeanPostProcessor核心類別元件進行產生出InjectionMetadata物件,並觸發inject方法產生出對應開發者所需之元件類別,在這過程中必須經過三道邏輯演算法,第一道流程processInjection,區分為方法類型註解(Method Annotation)及欄位類型註解(Field Annotation),此流程會判斷所待處理支援間類別是否有配置候選元件名稱註解(@Qualifier),有混合型註解配置,會觸發postProcessMergedBeanDefinition方法,若其註解作用在方法上,最終會觸發postProcessProperties方法,若僅為單一註解(@Autowired),則進行觸發processInjection方法,所有元資料(Metadata)配置完後即傳送至第二道流程findAutowiringMetadata,若有則選此名稱作為Bean Name,若無則以類別名稱當做Bean Name,並取得相關快取資料後進行相關邏輯處理,最後進入第三方法buildAutowiringMetadata,此方法會分為欄位型及方法型反射性處理,最後透過ShortcutDependencyDesriptor此類別進行代理產生InjectionMetadata物件處理相關反射元件行為。

image
圖一、@Autowird 注入架構圖

Follow up

Run test task

gradle test

Run open result html

open ./build/reports/tests/test/index.html

Test Report

測試結果,透過ApplicationContext及@Autowired皆達到預期目標
image

Sample Source

spring-sample-autowird

Reference Url

How to Get Application Context in Spring Boot

String Boot 使用BeanFactory動態取得bean BeanFactory get bean dynamically

Spring Source Analysis: @Autowire Annotation Principle Analysis

Constructor Dependency Injection in Spring

The Spring @Qualifier Annotation

Java MethodInterceptor類代碼示例


上一篇
[Day - 07] - Spring Component 元註解運作及原理
下一篇
[Day - 09] - Spring 模式註解之服務原理與開發
系列文
Wow ! There is no doubt about Learn Spring framework in a month.30

尚未有邦友留言

立即登入留言