手把手教你定製標準 Spring Boot starter

寫在前面
咱們每次構建一個 Spring 應用程序時,咱們都不但願從頭開始實現具備「橫切關注點」的內容;相反,咱們但願一次性實現這些功能,並根據須要將它們包含到任何咱們要構建的應用程序中
橫切關注點
橫切關注點: 指的是一些具備橫越多個模塊的行爲 (來自維基百科的介紹)
說白了就是多個項目或模塊均可以用到的內容,好比一個 SDKweb

在Spring Boot中,用於表示提供這種橫切關注點的模塊的術語是 starter,經過依賴 starter 能夠輕鬆使用其包含的一些功能特性,不管你的工做中是否會構建本身的 starter,你都要具備構建 「starter」的思想,本文將結合 Spring Boot 官方標準構建一個簡單的 starter面試

自定義 starter
在咱們深刻了解如何自定義 starter 以前,爲了更好的理解咱們每一步在幹什麼,以及 starter 是如何起做用的,咱們先從宏觀角度來看 starter 的結構組成究竟是什麼樣的spring

一般一個完整的 starter 須要包含下面兩個組件:json

  1. Auto-Configure Module
  2. Starter Module

若是你看下面這兩個組件的解釋有些抽象,大概瞭解一下,閱讀完該文章回看這裏就會豁然開朗了mybatis

Auto-Configure Module
Auto-Configure Module (自動配置模塊) 是包含自動配置類的 Maven 或 Gradle 模塊。經過這種方式,咱們能夠構建能夠自動貢獻於應用程序上下文的模塊,以及添加某個特性或提供對某個外部庫的訪問併發

Starter Module
Spring Boot Starter 是一個 Maven 或 Gradle 模塊,其惟一目的是提供 "啓動" 某個特性所需的全部依賴項。能夠包含一個或多個 Auto-Configure Module (自動配置模塊)的依賴項,以及可能須要的任何其餘依賴項。這樣,在Spring 啓動應用程序中,咱們只須要添加這個 starter 依賴就可使用其特性
: Spring 官方參考手冊建議將自動配置分離,並將每一個自動配置啓動到一個獨立的 Maven 或 Gradle 模塊中,從而將自動配置和依賴項管理分離開來。若是你沒有創建一個供成千上萬用戶使用的開源庫,也能夠將兩者合併到一個 module 中
You may combine the auto-configuration code and the dependency management in a single module if you do not need to separate those two concernsapp

命名
來自 Spring 官方的 starter 都是 以 spring-boot-starter 開頭,好比:maven

  • spring-boot-starter-web
  • spring-boot-starter-aop
    若是咱們自定義 starter 功能名稱叫acme,那麼咱們的命名是這樣的:
  • acme-spring-boot-starter
  • acme-spring-boot-autoconfigure
    若是 starter 中用到了配置 keys,也要注意不要使用 Spring Boot 使用的命名空間,好比(server,management,spring)

Parent Module 建立
先來全局看一下項目結構:
一級目錄結構:
.ide

├── pom.xml
├── rgyb-spring-boot-autoconfigure
├── rgyb-spring-boot-sample
└── rgyb-spring-boot-starter

二級目錄結構:
.spring-boot

├── pom.xml
├── rgyb-spring-boot-autoconfigure
│   ├── pom.xml
│   └── src
├── rgyb-spring-boot-sample
│   ├── pom.xml
│   └── src
└── rgyb-spring-boot-starter
    ├── pom.xml
    └── src

建立一個空的父親 Maven Module,主要提供依賴管理,這樣 SubModule 不用單獨維護依賴版本號,來看 pom.xml 內容:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>

    <!--  添加其餘全局依賴管理到這裏,submodule默認不引入這些依賴,須要顯式的指定  -->
</dependencyManagement>

Auto-Configure Module 構建
新建類 GreetingAutoConfigurationbr/>@Configuration
public class GreetingAutoConfiguration {

@Bean
public GreetingService greetingService(GreetingProperties greetingProperties){
    return new GreetingService(greetingProperties.getMembers());
}

}
咱們用 @Configuration 註解標記類 GreetingAutoConfiguration,做爲 starter 的入口點。這個配置包含了咱們須要提供starter特性的全部 @Bean 定義,在本例中,爲了簡單闡述問題,咱們只將 GreetingService Bean 添加到應用程序上下文

GreetingService 內容以下:

@AllArgsConstructor
public class GreetingService {

    private List<String> members = new ArrayList<>();

    public void sayHello(){
        members.forEach(s -> System.out.println("hello " + s));
    }
}

在 resources 目錄下新建文件 META-INF/spring.factories (若是目錄 META-INF 不存在須要手工建立),向文件寫入內容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
top.dayarch.autoconfigure.GreetingAutoConfiguration
Spring 啓動時會在其 classpath 中全部的 spring.factoreis文件,並加載裏面的聲明配置,GreetingAutoConfiguration 類就緒後,咱們的 Spring Boot Starter 就有了一個自動激活的入口點

到這裏這個 "不徹底的 starter" 已經可使用了。但由於它是自動激活的,爲了個讓其靈活可用,咱們須要讓其按照咱們的意願來激活使用,因此咱們須要條件註解來幫忙

條件配置
爲類添加兩個條件註解:

@Configuration
@ConditionalOnProperty(value = "rgyb.greeting.enable", havingValue = "true")
@ConditionalOnClass(DummyEmail.class)
public class GreetingAutoConfiguration {
    ...
}

經過使用 @ConditionalOnProperty 註解,咱們告訴 Spring,只有屬性 rgyb.greeting.enable值被設置爲 true 時,纔將 GreetingAutoConfiguration (以及它聲明的全部 bean ) 包含到應用程序上下文中
經過使用 @ConditionalOnClass 註解,咱們告訴Spring 只有類 DummyEmail.class 存在於 classpath 時,纔將 GreetingAutoConfiguration (以及它聲明的全部 bean ) 包含到應用程序上下文中
多個條件是 and/與的關係,既只有知足所有條件時,纔會加載 GreetingAutoConfiguration

若是你對條件註解的使用還不是很明確,能夠查看我以前的文章: @Conditional註解,靈活配置 Spring Boot

配置屬性管理
上面使用了 @ConditionalOnProperty 註解,實際 starter 中可能有很是多的屬性,因此咱們須要將這些屬性集中管理:

@Data
@ConfigurationProperties(prefix = "rgyb.greeting")
public class GreetingProperties {

    /**
     * GreetingProperties 開關
     */
    boolean enable = false;

    /**
     * 須要打招呼的成員列表
     */
    List<String> members = new ArrayList<>();
}

咱們知道這些屬性是要在 application.yml 中使用的,當咱們須要使用這些屬性時,爲了讓 IDE 給出更友好的提示,咱們須要在 pom.xml 中添加依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

這樣當咱們 mvn compile 時,會在生成一個名爲 spring-configuration-metadata.json JSON 文件,文件內容以下:

手把手教你定製標準 Spring Boot starter

生成的內容在接下來的內容中用到,且看

提高啓動時間
對於類路徑上的每一個自動配置類,Spring Boot 必須計算 @Conditional… 條件值,用於決定是否加載自動配置及其所需的全部類,根據 Spring 啓動應用程序中 starter 的大小和數量,這多是一個很是昂貴的操做,而且會影響啓動時間,爲了提高啓動時間,咱們須要在 pom.xml 中添加另一個依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure-processor</artifactId>
    <optional>true</optional>
</dependency>

這個註解會生成一個名爲 spring-autoconfigure-metadata.properties Property 文件,其內容以下:

手把手教你定製標準 Spring Boot starter

這樣,Spring Boot 在啓動期間讀取這些元數據,能夠過濾出不知足條件的配置,而沒必要實際檢查這些類,提高啓動速度

到這裏關於 Auto-Configure Module 就構建完了,咱們須要繼續完成 Starter Module 的構建

Starter Module 構建
Starter Module 的構建很簡單了,你能夠認爲它就是一個空 module,除了依賴 Auto-Configure Module,其惟一做用就是爲了使用 starter 功能特性提供全部必須依賴,因此咱們爲 starter module 的 pom.xml 文件添加以下內容:

<dependencies>
    <dependency>
        <groupId>top.dayarch.learnings</groupId>
        <artifactId>rgyb-spring-boot-autoconfigure</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>

    <!-- 在此處添加其餘必要依賴,保證starter可用 -->
</dependencies>

一樣在 resources 目錄下新建文件 META-INF/spring.providers , 其內容以下:
providers: rgyb-spring-boot-autoconfigure
該文件主要做用是說明 starter module 的依賴信息,多個依賴以逗號分隔就好,該文件不會影響 starter 的使用,無關緊要

Starter Module 就能夠這麼簡單,將兩個 module 分別 mvn install 到本地 Maven Repository,接下來咱們建立 sample module 引入這個 starter 依賴時就會從本地 Maven Repository 中拉取

建立 Sample Module
咱們能夠經過 Spring Initializr 正常初始化一個 Spring Boot 項目 (rgyb-spring-boot-sample),引入咱們剛剛建立的 starter 依賴,在 sample pom.xml 中添加依賴:

<dependency>
    <groupId>top.dayarch.learnings</groupId>
    <artifactId>rgyb-spring-boot-starter</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>

接下來配置 application.yml 屬性

rgyb:
  greeting:
    enable: true
    members:
      - 李雷
      - 韓梅梅

在咱們配置 YAML 的時候,會出現下圖的提示,這樣會更友好,固然爲了規範,屬性描述最好也用英文描述,這裏爲了說明問題用了中文描述:

手把手教你定製標準 Spring Boot starter

編寫測試類
咱們編寫測試用例:

@Autowired(required = false)
private GreetingService greetingService;

@Test
public void testGreeting() {
    greetingService.sayHello();
}

測試結果以下:

hello 李雷
hello 韓梅梅

總結
到這裏完整的 starter 開發就結束了,但願你們瞭解其構建過程,目錄結構及命名等標準,這樣有相應的業務需求時均可以開發本身的 starter 被其餘人應用起來

starter 開發好了,別人能夠手動添加依賴引入 starter 的相關功能,那咱們如何像 Spring Initializr 同樣,經過下來菜單選擇咱們的 starter 呢,這樣直接初始化好整個項目,接下來的文章咱們會模仿 Spring Initializr 自定義咱們自的 Initializr

知識點說明
Dependency optinal
爲何 Auto-Configure Module 的 dependency 都是 optional = true 呢?

這涉及到 Maven 傳遞性依賴的問題,詳情請看 Maven 依賴傳遞性透徹理解

spring.factories
Spring Boot 是如何加載這個文件並找到咱們的配置類的

下圖是 Spring Boot 應用程序啓動的調用棧的一部分,我添加了斷點:
手把手教你定製標準 Spring Boot starter

打開 SpringFactoriesLoader 類,映入眼簾的就是這個內容:
手把手教你定製標準 Spring Boot starter

這兩張圖應該足夠說明問題了,是 SPI 的一種加載方式,更細節的內容請你們本身去發現吧

實際案例
這裏推薦查看 mybatis-spring-boot-starter 這個非 Spring 官方的案例,從中咱們:

  • 模仿其目錄結構
  • 模仿其設計理念
  • 模仿其編碼規範
    手把手教你定製標準 Spring Boot starter

另外,本文的案例我已上傳,公衆號回覆「demo」,打開連接,查看 customstarter 目錄下內容便可

靈魂追問

  1. 在生成 spring-autoconfigure-metadata.properties 文件時,爲何 @ConditionalOnProperty 的內容沒有被寫進去
  2. 若是咱們要將依賴上傳至 remote central repository,你知道怎樣搭建本身的 maven repository 嗎?
  3. 你的燈還亮着嗎?

提早發現更多精彩,請訪問: https://dayarch.top

提升效率工具

手把手教你定製標準 Spring Boot starter

手把手教你定製標準 Spring Boot starter
手把手教你定製標準 Spring Boot starter

  • 讀取Excel還用POI?試試這款開源工具
  • Maven optional 關鍵字透徹圖解
  • 如何避免死鎖,咱們有套路可循
  • 面試併發volatile關鍵字時,咱們應該具有哪些談資?
  • 如何設計好的RESTful API

手把手教你定製標準 Spring Boot starter

tan日拱一兵轉發在看也很贊喜歡做者

相關文章
相關標籤/搜索