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

image

1. 前言

Springboot 中的自動配置確實方便惯悠,減少了我們開發(fā)上的復(fù)雜性职车,那么自動配置原理是什么呢洲胖?之前我也寫過了一篇文章進行了分析康震。
Springboot 系列(三)Spring Boot 自動配置

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

在這一次橘忱,通過學(xué)習(xí) Springboot 自動配置模式,編寫一個自己的 starter卸奉,用來加深對自動配置的理解钝诚。

熟悉模式,有助于提升編寫的 starter 的規(guī)范性榄棵,編寫自己的 starter 之前先來學(xué)習(xí) Springboot 官方 starter 以及常見框架的整合 starter 的編寫方式 凝颇,可以領(lǐng)略到其中的奧秘。

2. Springboot 官方模式

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

2.1. 引入依賴

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

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

如果你觀察 starter 多的話瘪弓,也許你發(fā)已經(jīng)發(fā)現(xiàn)了一個模式垫蛆,Springboot 官方的 starter的名字都是 spring-boot-starter-xxxx命名的。

查看 spring-boot-starter-web 會發(fā)現(xiàn)腺怯,其實這個依賴只是一個空盒子袱饭,除了依賴其他 pom 之外,沒有一行代碼呛占。

spring-boot-starter-web

這時虑乖,發(fā)現(xiàn)了另外一個模式starter 只依賴其他 pom,不做代碼實現(xiàn)晾虑。

那么 spring-boot-starter-web 到底依賴了哪些內(nèi)容疹味?

spring-boot-starter-web 的依賴

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

  1. 依賴 spring-boot-starter坠狡。
  2. 依賴 spring-boot-autoconfigure继找。

2.2. 自動配置

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

server.port=8090

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

Server 屬性配置

那么這個 ServerProperties 到底是哪里使用的呢边臼?繼續(xù)查找哄尔,找到一個和 Servlet 的有關(guān)的調(diào)用。

getPort 的調(diào)用

發(fā)現(xiàn)是被 ServletWebServerFactoryCustomizer類進行了調(diào)用柠并,這個類里面定義了

private final ServerProperties serverProperties;

用來使用配置的屬性岭接。
繼續(xù)查看這個類的調(diào)用,發(fā)現(xiàn)只有一個類使用這個類臼予,這個類是ServletWebServerFactoryAutoConfiguration鸣戴。

ServletWebServerFactoryAutoConfiguration 類

根據(jù)我們對注解的理解,這個類就是自動配置主要類了粘拾。同時自動配置類都是以 AutoConfiguration 結(jié)尾窄锅。

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

  1. 優(yōu)先級別較高缰雇。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
  1. 只有在 ServletRequest 類存在和是 Web 應(yīng)用時生效入偷。
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
  1. 開啟了 ServerProperties 的配置綁定。
@EnableConfigurationProperties(ServerProperties.class)
  1. 導(dǎo)入了幾個類械哟。
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

同時注入配置到 Bean 工廠以供其他地方調(diào)用疏之。

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

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

spring.factories

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

根據(jù)上面的分析薄霜,可以發(fā)現(xiàn) Springboot 官方 starter 的幾個模式

  1. 使用 XXXProperties 自動綁定 XXX 開頭的配置信息纸兔,如:ServerProperties惰瓜。
  2. XXXProperties 定義到要使用的類中,如:ServletWebServerFactoryCustomizer汉矿。
  3. 編寫一個 XXXAutoConfiguration 崎坊,開啟 XXXProperties 的自動配置,限定生效場景洲拇,創(chuàng)建需要的類到 Bean 工廠奈揍。如:ServletWebServerFactoryAutoConfiguration曲尸。

3. 第三方集成模式

Springboot 官方如果把所有的框架都編寫成 starter,是不現(xiàn)實的男翰。因此很多第三方框架需要主動集成到 springboot另患,所以我們選擇一個常用的框架分析它的 starter 實現(xiàn)。因為已經(jīng)看過了 springboot 官方 starter 是如何配置的蛾绎, 第三方框架也是類似昆箕,所以在下面觀察的過程中會直接指出相同點,而不再做對比詳細(xì)對比租冠。

這里選擇 mybatis-spring-boot-starter 進行學(xué)習(xí)分析鹏倘。

3.1 引入依賴

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

這里 mybatis 框架的 starter 依賴符合一定的規(guī)則,即 xxx-spring-boot-starter.

觀察這個 starter肺稀,發(fā)現(xiàn)它也沒有做任何的代碼實現(xiàn)第股,這一點和 springboot 官方一致。

mybatis-spring-boot-starter

查看 mybatis-spring-boot-starter 的依賴項话原,有很多夕吻,其中和自動配置有關(guān)的主要是。

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

3.2 自動配置

查看 mybatis-spring-boot-autoconfigure 的內(nèi)容發(fā)現(xiàn)和 springboot 官方的 autoconfigure結(jié)構(gòu)上是差不多的繁仁。

mybatis-spring-boot-autoconfigure

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

MybatisAutoConfiguration

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

4. 編寫自己的 starter

說了那么多,終于到了實操環(huán)節(jié)捻浦,通過上面的介紹晤揣,我們可以大致得出編寫自己的 starter步驟。

  1. 創(chuàng)建名字為 xxx-spring-boot-starter 的啟動器項目朱灿。
  2. 創(chuàng)建名字為 xxx-spring-boot-autoconfigure的項目昧识。
    • 編寫屬性綁定類 xxxProperties.
    • 編寫服務(wù)類,引入 xxxProperties.
    • 編寫自動配置類XXXAutoConfiguration注入配置盗扒。
    • 創(chuàng)建 spring.factories 文件跪楞,用于指定要自動配置的類。
  3. 啟動器項目為空項目侣灶,用來引入 xxx-spring-boot-autoconfigure等其他依賴甸祭。
  4. 項目引入 starter,配置需要配置的信息褥影。

4.1 創(chuàng)建啟動器項目

由于啟動器不需要代碼實現(xiàn)池户,只需要依賴其他項目,所以直接創(chuàng)建一個空的 maven 項目。但是名字要規(guī)范煞檩。
這里創(chuàng)建的 startermyapp-spring-boot-starter处嫌。

myapp-spring-boot-starter

pom 文件非常簡單,只需要引入接下來要創(chuàng)建的 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 創(chuàng)建自動配置項目

結(jié)合上面對 starter 的分析斟湃,直接創(chuàng)建一個名字為 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>

項目的總體結(jié)構(gòu)看圖凝赛。

myapp-spring-boot-starter-autoconfigure

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

/**
 * <p>
 *
 * @Author niujinpeng
 * @Date 2019/10/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應(yīng)用才生效
 */
@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項目執(zhí)行 mvn install 把自動配置項目打包到本地倉庫,然后使用相同的命令把 myapp-spring-boot-starter 安裝到倉庫烤宙。因為后者依賴于前者項目遍烦,所以這里前者需要先進 mvn install

4.3 使用自定義的啟動器

創(chuàng)建一個 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 看看自動配置有沒有生效叉瘩。

訪問測試

從測試結(jié)果可以看到自動配置的早上好已經(jīng)生效了坡脐。到這里自己編寫的 starter也已經(jīng)完工。

項目已經(jīng)傳到 Github.
https://github.com/niumoo/springboot/tree/master/springboot-starter

<完>
個人網(wǎng)站:https://www.codingme.net
如果你喜歡這篇文章房揭,可以關(guān)注公眾號,一起成長晌端。

公眾號

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捅暴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子咧纠,更是在濱河造成了極大的恐慌蓬痒,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漆羔,死亡現(xiàn)場離奇詭異梧奢,居然都是意外死亡狱掂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門亲轨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趋惨,“玉大人,你說我怎么就攤上這事惦蚊∑飨海” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵蹦锋,是天一觀的道長兆沙。 經(jīng)常有香客問我,道長莉掂,這世上最難降的妖魔是什么葛圃? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮憎妙,結(jié)果婚禮上库正,老公的妹妹穿的比我還像新娘。我一直安慰自己尚氛,他們只是感情好诀诊,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阅嘶,像睡著了一般属瓣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讯柔,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天抡蛙,我揣著相機與錄音,去河邊找鬼魂迄。 笑死粗截,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捣炬。 我是一名探鬼主播熊昌,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼湿酸!你這毒婦竟也來了婿屹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤推溃,失蹤者是張志新(化名)和其女友劉穎昂利,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡蜂奸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年犁苏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扩所。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡围详,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碌奉,到底是詐尸還是另有隱情短曾,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布赐劣,位于F島的核電站嫉拐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏魁兼。R本人自食惡果不足惜婉徘,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咐汞。 院中可真熱鬧盖呼,春花似錦、人聲如沸化撕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽植阴。三九已至蟹瘾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掠手,已是汗流浹背憾朴。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留喷鸽,地道東北人众雷。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像做祝,于是被迫代替她去往敵國和親砾省。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容