實現一個Spring Boot Starter超簡單,讀 Starter 源碼也不在話下

Spring Boot 對比 Spring MVC 最大的優點就是使用簡單,約定大於配置。不會像之前用 Spring MVC 的時候,時不時被 xml 配置文件搞的暈頭轉向,冷不防還因爲 xml 配置上的一點疏忽,導致整個項目莫名其妙的不可用,頓感生活無所依戀,簡稱生無可戀。

這要歸功於組成了 Spring Boot 的各種各樣的 starters,有官方提供的,也有第三方開源出來。可以這麼說,基本上你打算用的功能都可以找到,如果沒有找到,那就再找一找。

用 Spring Boot 的功能組件(例如 spring-boot-starter-actuator、 spring-boot-starter-data-redis 等)的步驟非常簡單,用著名的把大象放冰箱的方法來概括的話,有以下三步就可以完成組件功能的使用:

STEP 1

在 pom 文件中引入對應的包,例如:

<dependency> 
  <groupId>org.springframework.boot</groupId>  
  <artifactId>spring-boot-starter-actuator</artifactId> 
</dependency>
複製代碼

STEP 2

在應用配置文件中加入相應的配置,配置都是組件約定好的,需要查看官方文檔或者相關說明。有些比較複雜的組件,對應的參數和規則也相應的較多,有點可能多大幾十上百了。

STEP 3

以上兩步都正常的情況下,我們就可以使用組件提供的相關接口來開發業務功能了。

沒錯吧,這個過程我們在日常的開發中不知道已經實踐了多少遍。那麼 Spring Boot 爲什麼能做到如此簡單易用呢,它內部是什麼樣的工作機制呢,不知道你有沒有研究過。

以下是爲了理解 Spring Boot 組件的實現機制而製作的一個 demo starter。理解其中的原理,對我們日後的工作有什麼意義呢?

  1. 遇到問題的時候,可以幫助我們更有頭緒的排查問題;
  2. 可以幫助我們正確的閱讀源代碼,組件的切入口在哪兒,配置屬性是什麼等等;

開始實現一個 Spring Boot Starter

下面我們來實現這個簡單的 starter,這個 starter 並沒有什麼實際的功能,只是爲了做個演示而已。

開始之前,我們要理解一下 spring boot starter 是什麼呢?

實際上 starter 並不會包含多少功能代碼,我們可以把它理解成一個「連接包」(我自己造的概念),按照這個概念來說:

它首先是一個包,一個集合,它把需要用的其他功能組件囊括進來,放到自己的 pom 文件中。

然後它是一個連接,把它引入的組件和我們的項目做一個連接,並且在中間幫我們省去複雜的配置,力圖做到使用最簡單。

實現一個 starter 有四個要素:

  1. starter 命名 ;
  2. 自動配置類,用來初始化相關的 bean ;
  3. 指明自動配置類的配置文件 spring.factories ;
  4. 自定義屬性實體類,聲明 starter 的應用配置屬性 ;

好了,開始實現我們的 demo

1. 給 starter 起個名字

也就是我們使用它的時候在 pom 中引用的 artifactId。命名有有規則的,官方規定:

官方的 starter 的命名格式爲 spring-boot-starter-{name} ,例如上面提到的 spring-boot-starter-actuator。

非官方的 starter 的命名格式爲 {name}-spring-boot-starter,我們把自定的 starter 命名爲 kite-spring-boot-starter,命名在 pom 文件裏。

<groupId>kite.springcloud</groupId>
<artifactId>kite-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
複製代碼

2. 引入自動配置包及其它相關依賴包

實現 starter 主要依賴自動配置註解,所以要在 pom 中引入自動配置相關的兩個 jar 包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
複製代碼

除此之外,依賴的其他包當然也要引進來。

3. 創建 spring.factories 文件

在 resource/META-INF 目錄下創建名稱爲 spring.factories 的文件,爲什麼在這裏?當 Spring Boot 啓動的時候,會在 classpath 下尋找所有名稱爲 spring.factories 的文件,然後運行裏面的配置指定的自動加載類,將指定類(一個或多個)中的相關 bean 初始化。

例如本例中的配置信息是這樣的:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  kite.springcloud.boot.starter.example.KiteAutoConfigure
複製代碼

等號前面是固定的寫法,後面就是我們自定義的自動配置類了,如果有多個的話,用英文逗號分隔開。

4. 編寫自動配置類

自動配置類是用來初始化 starter 中的相關 bean 的。可以說是實現 starter 最核心的功能。

@Configuration
@ConditionalOnClass(KiteService.class)
@EnableConfigurationProperties(KiteProperties.class)
@Slf4j
public class KiteAutoConfigure {

    @Autowired
    private KiteProperties kiteProperties;

    @Bean
    @ConditionalOnMissingBean(KiteService.class)
    @ConditionalOnProperty(prefix = "kite.example",value = "enabled", havingValue = "true")
    KiteService kiteService(){
        return new KiteService(kiteProperties);
    }
}
複製代碼

代碼非常簡單,放眼望去,最多的就是各種註解。

@Configuration 這個不用解釋,表示這是個自動配置類,我們平時做項目時也會用到,一般是用作讀取配置文件的時候。

@ConditionalOnClass(KiteService.class) :

只有在 classpath 中找到 KiteService 類的情況下,纔會解析此自動配置類,否則不解析。

@EnableConfigurationProperties(KiteProperties.class):

啓用配置類。

@Bean:實例化一個 bean 。

@ConditionalOnMissingBean(KiteService.class):

與 @Bean 配合使用,只有在當前上下文中不存在某個 bean 的情況下才會執行所註解的代碼塊,也就是當前上下文還沒有 KiteService 的 bean 實例的情況下,纔會執行 kiteService() 方法,從而實例化一個 bean 實例出來。

@ConditionalOnProperty:

當應用配置文件中有相關的配置纔會執行其所註解的代碼塊。

這個類的整體含義就是: 當 classpath 中存在 KiteService 類時解析此配置類,什麼情況下纔會在 classpath 中存在呢,就是項目引用了相關的 jar 包。並且在上下文中沒有 KiteService 的 bean 實例的情況下,new 一個實例出來,並且將應用配置中的相關配置值傳入。

5. 實現屬性配置類

@Data
@ConfigurationProperties("kite.example")
public class KiteProperties {

    private String host;

    private int port;
}
複製代碼

配置類很簡單,只有兩個屬性,一個 host ,一個 port 。配置參數以 kite.example 作爲前綴。稍後我們在使用這個 starter 的時候會看到如何聲明配置屬性。

6. 實現相關功能類

也就是前面一直提到的 KiteService,其實嚴格來講,這個業務功能類不應該放在 starter 中,應該放在單獨的 jar 包裏,但是此處 demo 非常簡單,也就在這裏寫了。

@Slf4j
public class KiteService {

    private String host;

    private int port;

    public KiteService(KiteProperties kiteProperties){
        this.host = kiteProperties.getHost();
        this.port = kiteProperties.getPort();
    }

    public void print(){
        log.info(this.host + ":" +this.port);
    }
}
複製代碼

一個構造函數和一個 print 方法。

7. 打包

通過 maven 命令將這個 starter 安裝到本地 maven 倉庫

mvn install
複製代碼

也可以通過 mvn package deploy 發佈到你的私服

或者發佈到中央倉庫。

使用剛創建的 starter

上面已經完成了 starter 的開發,並安裝到了本地倉庫,然後就是在我們的項目中使用它了。

1. 創建項目,在 pom 中引用

<dependency>
    <groupId>kite.springcloud</groupId>
    <artifactId>kite-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
複製代碼

2. 應用配置項

創建 application.yml ,配置如下:

server:
  port: 3801
kite:
  example:
    enabled: true  # 開啓才生效
    host: 127.0.0.1
    port: 3801
複製代碼

3. 調用 KiteService 的服務方法

@RestController
@RequestMapping(value = "use")
public class UseController {

    @Autowired
    private KiteService kiteService;

    @GetMapping(value = "print")
    public void print(){
        kiteService.print();
    }
}
複製代碼

4. 啓動服務,並訪問接口

訪問 /use/print 接口,會發現在日誌中打印出了配置信息

2019-05-24 16:45:04.234  INFO 36687 --- [nio-3801-exec-1] k.s.boot.starter.example.KiteService     : 127.0.0.1:3801
複製代碼

拋磚引玉,如何閱讀其他 starter 源碼

順着上面的思路,我們來看一下官方的 starters 的結構。先來把 Spring Boot 從 github 上 clone 一份下來。用 idea 打開,可以看到項目結構如下

Spring-boot-starters 中就是官方提供的主要 starters,比如 jdbc、redis、security、web 等等。

我們拿 spring-boot-starter-data-redis 這個 starter 作爲例子,來說一說官方是怎麼組織項目結構的,以及閱讀源碼的順序應該是怎樣的。

1. 展開 Spring-boot-staters 下的 redis starter,我們看到目錄結構如下

其中並沒有 Java 代碼,只有一個 spring.provides 文件,裏面的內容如下:

provides: spring-data-redis,lettuce-core
複製代碼

意思就是說,本項目依賴 spring-data-redis 和 lettuce-core 這兩個包,並且在 pom 文件中引用了。其目的就是告知使用者在引用此包的時候,不必再引用 provides 中的依賴包了。

2. 然後就是自動註解了,所有 stater 的自動註解類、屬性配置類都放到了 spring-boot-autoconfigure 這個項目下

看到熟悉的 spring.factories 沒有,前面我們自己實現過。這個內容比較多,我們只看 redis 相關的

org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration
複製代碼

包含三個自動配置文件,然後順着配置,我們找到所在 package

然後就可以開始閱讀代碼了。其他的 starter 也是同樣的結構。

 

壯士且慢,先給點個贊吧,總是被白嫖,身體吃不消!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章