iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0
Software Development

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

[Day - 13] - Spring 依賴性注入元件管理運作與方法

  • 分享至 

  • xImage
  •  

Abstract

無論何種時候,每種系統的開發元件勢必都有先後啟動順序,如何有效管控每項元件的啟動流程相當重要,今天將介紹一個依賴性註冊的註解模式給大家,依賴式註解(@DependOn)可讓開發者評估哪些元件的先後註冊順序,減少元件註冊的衝突,避免相關物件依賴錯誤,進而導致啟動失敗,在Spring Boot註冊邏輯中,是採用字母排序進行註冊,故開發者可自行來透過依靠模式註解來進行決定自身所開發的元件註冊先後順序,但須注意,昨日所述懶惰式(@Lazy)註冊會無法產生效用,除非前後兩者都加入懶惰式(@Lazy)註冊,並無相關類別再啟動時有所依賴,才會進行跳過,以下將介紹相關範例與原理介紹。

Principle Introduction

在Spring Boot中,我們都知道所有的元件都會註冊進BeanFactory配置池中,在依靠模式註解中,會先判斷是否存在此項元件,若不存在就以尋找此依靠物件為主進行註冊,若找不到則會拋出“No bean named '*********' available,若找到則會在分析是否又有下一個依靠元件,以樹狀模式尋找到沒有依靠元件為主,並會從每個類別的元件名稱尋找至每個方法的Bean名稱,當註冊完該元件候,會再循序往下尋找未註冊之元件,進行後續元件註冊,以下面範例為例,透過此範例來分析與否採用依靠式註解的結果。

配置等待CompiledLanguage元件註冊完後才可進行註冊


@Service("InterpretedLanguageService")
@DependsOn("CompiledLanguageService")
public class InterpretedLanguServiceImpl implements ProgramLangService {

    @PostConstruct
    public void init() {
        System.out.println("InterpretedLanguServiceImpl initial after CompiledLanguage.");
    }

    ......
    ......
    ......
    ......
}

亦可等待各類方法的上的Bean元件

@Configuration
@DependsOn("InterpretedLanguageService")
public class NewLanguageConfiguration {

    @PostConstruct
    public void init() {
        System.out.println("NewLanguageConfiguration initial after InterpretedLanguage.");
    }

    @Bean("GoBean")
    public ProgramLangMdl getProgramLangMdlWithGo() {
       return new ProgramLangMdl()
                .setId("id-005")
                .setName("GoLang")
                .setDescritpion("Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.");
    }

    @Bean("LuaBean")
    @DependsOn("GoBean")
    public ProgramLangMdl getProgramLangMdlWithLua() {
        return new ProgramLangMdl()
                .setId("id-006")
                .setName("Lua")
                .setDescritpion("Lua is a lightweight, high-level, multi-paradigm programming language designed primarily for embedded use in applications. Lua is cross-platform, since the interpreter of compiled bytecode is written in ANSI C,[4] and Lua has a relatively simple C API to embed it into applications.");
    }
}

透過上面各類的方法依靠方式註解,可以得到以下結果,區塊一為未採用依賴式註解(DependOn)的結果,根據實驗結果僅透過字母排序進行註冊,區塊二為採用依賴式註解(DependOn)的結果,可由初始化結果得知註冊的先後順序,故此註解可決定初始化註冊順序,亦可決定銷毀的先後順序,因銷毀順序為註冊順序索引再相反進行銷毀刪除,提供開發者作參考。

區塊一 未採用依靠式方法

CompiledLangServiceImpl initial.
CompiledLanguageService is afterPropertiesSet
FuturePopLanguageServiceImpl initial after LuaBean,GoBean.
FutureLanguageService is afterPropertiesSet
InterpretedLanguServiceImpl initial after CompiledLanguage.
InterpretedLanguageService is afterPropertiesSet
NewLanguageConfiguration initial after InterpretedLanguage.
NewLanguageConfiguration is afterPropertiesSet
2021-08-31 01:19:39.816  INFO 16416 --- [    Test worker] sw.project.sample.DepondOnTestSuite      : Started DepondOnTestSuite in 2.144 seconds (JVM running for 3.316)
Get Compiled object test success.
Get Bean object test success.
Get Bean object test success.
NewLanguageConfiguration is destroy
InterpretedLanguageService is destroy
FutureLanguageService is destroy
CompiledLanguageService is destroy

區塊二 採用依靠式方法

CompiledLangServiceImpl initial.
CompiledLanguageService is afterPropertiesSet
InterpretedLanguServiceImpl initial after CompiledLanguage.
InterpretedLanguageService is afterPropertiesSet
NewLanguageConfiguration initial after InterpretedLanguage.
NewLanguageConfiguration is afterPropertiesSet
FuturePopLanguageServiceImpl initial after LuaBean,GoBean.
FutureLanguageService is afterPropertiesSet
2021-08-31 01:17:02.475  INFO 16392 --- [    Test worker] sw.project.sample.DepondOnTestSuite      : Started DepondOnTestSuite in 2.086 seconds (JVM running for 3.459)
Get Bean object test success.
FutureLanguageService is destroy
NewLanguageConfiguration is destroy
InterpretedLanguageService is destroy
CompiledLanguageService is destroy

Structure

透過架構圖可知,一個Bean的生成會封裝成BeanDefinition,透過ClassPathBeanDefintionScanner中的doScan方法,,然後在進入processCommonDefinitionAnnotations過濾各類註解模式服務,分別為@Lazy、@Primary、@DependsOn、@Role及@Description,當分析到該元件有配置依賴關係註解,則會在BeanDefinition進行觸發setDependsOn方法,當所有註解模式過濾配置完後,即可透過代理模式配置成BeanDefinitionHolder,最後在進行註冊該BeanDefinition,即可完成分析過濾依賴關係註解。
image

Follow up

Run test task

gradle test

Run open result html

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

Test Report

Depend-on register test report
image

Mind-blowing Depend-on register flow & test detail
image

Sample Source

spring-sample-dependOn

Reference Url

DependOn

Spring中@DependsOn注解的作用及实现原理解析


上一篇
[Day - 12] - Spring 注入式效能提升運作與方法
下一篇
[Day - 14] - Spring 優化應用程序元件註冊順序開發與方法
系列文
Wow ! There is no doubt about Learn Spring framework in a month.30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言