[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类代码示例


<<:  [铁人赛 Day08] 如何使用 memoization 方法减少 useContext 非必要 re-render 的效能问题?

>>:  Day8 Function and Interface

当TrustView(档案加密软件) 白老鼠的惨痛过程记录 ~

外部稽核要求文件需要有保护的机制 , 所以我们引进了 ~ 加密了哪些软件产生的文件 ? Micros...

30天不间断的文章之旅_变数宣告的 var、let、const

这是第一次在公开的地方撰写 JavaScript 相关的技术文章,若有错误或需要补充的地方,也欢迎在...

Day30 利用web发送讯息(下)

在昨天我们把资料写入了firebase,接下来我们再利用googleHomeTrigger 藉由on...

Day20:安全性和演算法-杂凑函数(hash function)

安全性与演算法 在电脑科学的领域里,每一刻都有数以万计的资料在进行传输,在传输的过程中,是真的安全吗...

DOM实作 密码输入

<!DOCTYPE html> <html lang="en"...