Springboot 系列(十五)如何編寫自己的 Springboot starter

1. 前言

Springboot 中的自動配置確實方便,減少了我們開發上的複雜性,那麼自動配置原理是什麼呢?之前我也寫過了一篇文章進行了分析。
Springboot 系列(三)Spring Boot 自動配置

由於自動配置用到了配置文件的綁定,如果你還不知道常見的配置文件的用法,可以參考這篇文章。
Springboot 系列(二)Spring Boot 配置文件

在這一次,通過學習 Springboot 自動配置模式,編寫一個自己的 starter,用來加深對自動配置的理解。

熟悉模式,有助於提升編寫的 starter 的規範性,編寫自己的 starter 之前先來學習 Springboot 官方 starter 以及常見框架的整合 starter 的編寫方式 ,可以領略到其中的奧祕。

2. Springboot 官方模式

選擇一個官方的自動配置進行分析,這裏就選擇常見的配置端口號配置。

2.1. 引入依賴

使用端口號之前我們需要先引入 web 依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

如果你觀察 starter 多的話,也許你發已經發現了一個模式Springboot 官方的 starter的名字都是 spring-boot-starter-xxxx命名的。

查看 spring-boot-starter-web 會發現,其實這個依賴只是一個空盒子,除了依賴其他 pom 之外,沒有一行代碼。

這時,發現了另外一個模式starter 只依賴其他 pom,不做代碼實現。

那麼 spring-boot-starter-web 到底依賴了哪些內容?

觀察這個依賴信息,然後再參照其他的官方 starter ,可以找到幾個固定的引入,可以被稱之爲模式的依賴引入。

  1. 依賴 spring-boot-starter
  2. 依賴 spring-boot-autoconfigure

2.2. 自動配置

引入依賴只有配置端口號,像這樣。

server.port=8090

IDEA 中可以通過點擊 server.port 找到這個配置綁定的類文件。可以看到配置最終會注入到類ServerProperties 類的 port 屬性上。

那麼這個 ServerProperties 到底是哪裏使用的呢?繼續查找,找到一個和 Servlet 的有關的調用。

發現是被 ServletWebServerFactoryCustomizer類進行了調用,這個類裏面定義了

private final ServerProperties serverProperties;

用來使用配置的屬性。
繼續查看這個類的調用,發現只有一個類使用這個類,這個類是ServletWebServerFactoryAutoConfiguration

ServletWebServerFactoryAutoConfiguration 類

根據我們對註解的理解,這個類就是自動配置主要類了。同時自動配置類都是以 AutoConfiguration 結尾。

看這個類的幾個註解的意思。

  • 優先級別較高。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  • 只有在 ServletRequest 類存在和是 Web 應用時生效。
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
  • 開啓了 ServerProperties 的配置綁定。
@EnableConfigurationProperties(ServerProperties.class)
  • 導入了幾個類。
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

同時注入配置到 Bean 工廠以供其他地方調用。

@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    return new ServletWebServerFactoryCustomizer(serverProperties);
}

自動配置僅僅是這些東西嗎?根據之前文章裏的分析,我們知道不止代碼,至少還有一個指定自動配置類的配置文件需要讀取。也就是 spring.factories 文件。

如果你不知道,可以先看這篇文章。Springboot 系列(三)Spring Boot 自動配置 。
事實確實如此,可以在 spring.factories 中找到上面跟蹤到的類。
也就是 ServletWebServerFactoryAutoConfiguration.

根據上面的分析,可以發現 Springboot 官方 starter 的幾個模式

  1. 使用 XXXProperties 自動綁定 XXX 開頭的配置信息,如:ServerProperties
  2. 把 XXXProperties 定義到要使用的類中,如:ServletWebServerFactoryCustomizer
  3. 編寫一個 XXXAutoConfiguration ,開啓 XXXProperties 的自動配置,限定生效場景,創建需要的類到 Bean 工廠。如:ServletWebServerFactoryAutoConfiguration

3. 第三方集成模式

Springboot 官方如果把所有的框架都編寫成 starter,是不現實的。因此很多第三方框架需要主動集成到 springboot,所以我們選擇一個常用的框架分析它的 starter 實現。因爲已經看過了 springboot 官方 starter 是如何配置的, 第三方框架也是類似,所以在下面觀察的過程中會直接指出相同點,而不再做對比詳細對比。

這裏選擇 mybatis-spring-boot-starter 進行學習分析。

3.1 引入依賴

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

這裏 mybatis 框架的 starter 依賴符合一定的規則,即 xxx-spring-boot-starter.

觀察這個 starter,發現它也沒有做任何的代碼實現,這一點和 springboot 官方一致。

mybatis-spring-boot-starter

查看 mybatis-spring-boot-starter 的依賴項,有很多,其中和自動配置有關的主要是。

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>

3.2 自動配置

查看 mybatis-spring-boot-autoconfigure 的內容發現和 springboot 官方的 autoconfigure結構上是差不多的。

mybatis-spring-boot-autoconfigure

mybatis 的自動配置也是通過 spring.factories 來指明自動配置,然後通過 XxxAutoConfiguration 綁定 XxxProperties 來進行自動配置.

MybatisAutoConfiguration

在原理上,和上面 springboot 官方的 starter是相同的,所以不做過多的介紹了。

4. 編寫自己的 starter

說了那麼多,終於到了實操環節,通過上面的介紹,我們可以大致得出編寫自己的 starter步驟。

1. 創建名字爲 xxx-spring-boot-starter 的啓動器項目。

2. 創建名字爲 xxx-spring-boot-autoconfigure的項目。

  • 編寫屬性綁定類 xxxProperties.
  • 編寫服務類,引入 xxxProperties.
  • 編寫自動配置類XXXAutoConfiguration注入配置。
  • 創建 spring.factories 文件,用於指定要自動配置的類。

3. 啓動器項目爲空項目,用來引入 xxx-spring-boot-autoconfigure等其他依賴。

4. 項目引入 starter,配置需要配置的信息。

4.1 創建啓動器項目

由於啓動器不需要代碼實現,只需要依賴其他項目,所以直接創建一個空的 maven 項目。但是名字要規範。
這裏創建的 starter 是 myapp-spring-boot-starter

myapp-spring-boot-starter

pom 文件非常簡單,只需要引入接下來要創建的 myapp-spring-boot-autoconfigure.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.codingme.starter</groupId>
    <artifactId>myapp-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 啓動器 -->
    <dependencies>
        <!--  引入自動配置項目 -->
        <dependency>
            <groupId>net.codingme.starter</groupId>
            <artifactId>myapp-spring-boot-autoconfigure</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

4.2 創建自動配置項目

結合上面對 starter 的分析,直接創建一個名字爲 myapp-spring-boot-autoconfigure 的項目。項目中只引入 springboot 父項目以及 spring-boot-starter

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>net.codingme.starter</groupId>
    <artifactId>myapp-spring-boot-autoconfigure</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>myapp-spring-boot-autoconfigure</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    </dependencies>
</project>

項目的總體結構看圖。

myapp-spring-boot-starter-autoconfigure

在 HelloProperties中通過註解 @ConfigurationProperties(prefix = "myapp.hello")讓類中的屬性與 myapp.hello開頭的配置進行綁定。

/**
 * <p>
 *
 * @Author SpringRoot
 * @Date 2019/11/29 23:51
 */
@ConfigurationProperties(prefix = "myapp.hello")
public class HelloProperties {

    private String suffix;

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

然後在 HelloService中的 sayHello方法使用 HelloProperties 中自動綁定的值。

public class HelloService {
    HelloProperties helloProperties;

    public String sayHello(String name) {
        return "Hello " + name + "," + helloProperties.getSuffix();
    }

    public HelloProperties getHelloProperties() {
        return helloProperties;
    }

    public void setHelloProperties(HelloProperties helloProperties) {
        this.helloProperties = helloProperties;
    }
}

爲了讓 HelloService 可以自動注入且能正常使用 HelloProperties,所以我們在
HelloServiceAutoConfiguration 類中把 HelloProperties.class 引入,然後把 HelloService 注入到 Bean

/**
 * web應用才生效
 */
@ConditionalOnWebApplication
/**
 * 讓屬性文件生效
 */
@EnableConfigurationProperties(HelloProperties.class)
/***
 * 聲明是一個配置類
 */
@Configuration
public class HelloServiceAutoConfiguration {

    @Autowired
    private HelloProperties helloProperties;

    @Bean
    public HelloService helloService() {
        HelloService helloService = new HelloService();
        helloService.setHelloProperties(helloProperties);
        return helloService;
    }
}

最後在 spring.factories中只需要指定要自動配置的類即可。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.codingme.starter.HelloServiceAutoConfiguration

到這裏,自動配置項目就完成了。可以在 myapp-spring-boot-autoconfigure項目執行 mvn install 把自動配置項目打包到本地倉庫,然後使用相同的命令把 myapp-spring-boot-starter 安裝到倉庫。因爲後者依賴於前者項目,所以這裏前者需要先進 mvn install

4.3 使用自定義的啓動器

創建一個 springboot項目myapp-spring-boot-starter-test

myapp-spring-boot-starter-test

引入 web 依賴,引入自己編寫的 myapp-spring-boot-starter.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入自己的 starter -->
<dependency>
    <groupId>net.codingme.starter</groupId>
    <artifactId>myapp-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

編寫一個 HelloController 注入自動配置裏的 HelloService用於測試。

@RestController
public class HelloController {

    @Autowired
    HelloService helloService;

    @GetMapping("/hello")
    public String sayHello(String name) {
        return helloService.sayHello(name);
    }
}

由於 autoConfigure 項目中定義了 sayHello 方法會輸出“Hello”+傳入的 name + 配置的 hello.suffix,所以我們在 springboot 配置文件中配置這個屬性。

myapp.hello.suffix=早上好

運行測試項目,訪問 /hello 路徑傳入一個 name 看看自動配置有沒有生效。

從測試結果可以看到自動配置的早上好已經生效了。到這裏自己編寫的 starter也已經完工。

發佈了96 篇原創文章 · 獲贊 162 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章