Spring Boot實戰之定製自己的starter

本文首發於個人網站,原文地址:http://www.javaadu.online/?p=535,如需轉載,請註明出處

在學習Spring Boot的過程中,接觸最多的就是starter。可以認爲starter是一種服務——使得使用某個功能的開發者不需要關注各種依賴庫的處理,不需要具體的配置信息,由Spring Boot自動通過classpath路徑下的類發現需要的Bean,並織入bean。舉個例子,spring-boot-starter-jdbc這個starter的存在,使得我們只需要在BookPubApplication下用@Autowired引入DataSource的bean就可以,Spring Boot會自動創建DataSource的實例。

這裏我們會用一個不太規範的starter展示Spring Boot的自動配置的運行原理。Spring Boot的自動配置、Command-line Runner一文中曾利用StartupRunner類在程序運行啓動後首先查詢數據庫中書的數目,現在換個需求:在系統啓動後打印各個實體的數量

How Do

  • 新建一個模塊db-count-starter,然後修改db-count-starter模塊下的pom文件,增加對應的庫。
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot</artifactId>
        <!-- version繼承父模塊的-->
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-commons</artifactId>
        <version>1.9.3.RELEASE</version>
    </dependency></dependencies>
  • 新建包結構com/test/bookpubstarter/dbcount,然後新建DbCountRunner類,實現CommandLineRunner接口,在run方法中輸出每個實體的數量。
package com.test.bookpubstarter.dbcount;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;

public class DbCountRunner implements CommandLineRunner {
    protected final Logger logger = LoggerFactory.getLogger(DbCountRunner.class);
    private Collection<CrudRepository> repositories;

    public DbCountRunner(Collection<CrudRepository> repositories) {
        this.repositories = repositories;
    }
    @Override
    public void run(String... strings) throws Exception {
        repositories.forEach(crudRepository -> {
            logger.info(String.format("%s has %s entries",
                    getRepositoryName(crudRepository.getClass()),
                    crudRepository.count()));
        });
    }

    private static String getRepositoryName(Class crudRepositoryClass) {
        for (Class repositoryInterface : crudRepositoryClass.getInterfaces()) {
            if (repositoryInterface.getName().startsWith("com.test.bookpub.repository")) {
                return repositoryInterface.getSimpleName();
            }
        }
        return "UnknownRepository";
    }
}
  • 增加自動配置文件DbCountAutoConfiguration
package com.test.bookpubstarter.dbcount;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;

@Configuration
public class DbCountAutoConfiguration {
    @Bean
    public DbCountRunner dbCountRunner(Collection<CrudRepository> repositories) {
        return new DbCountRunner(repositories);
    }
}
  • 在src/main/resources目錄下新建META-INF文件夾,然後新建spring.factories文件,這個文件用於告訴Spring Boot去找指定的自動配置文件,因此它的內容是
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.test.bookpubstarter.dbcount.DbCountAutoConfiguration
  • 在之前的程序基礎上,在頂層pom文件中增加starter的依賴
<dependency>
   <groupId>com.test</groupId>
   <artifactId>db-count-starter</artifactId>
   <version>0.0.1-SNAPSHOT</version>
</dependency>
  • 把StartupRunner相關的註釋掉,然後在main函數上右鍵Run BookPubApplication.main(...),可以看出我們編寫的starter被主程序使用了。

自己的starter簡單演示.png

分析

正規的starter是一個獨立的工程,然後在maven中新倉庫註冊發佈,其他開發人員就可以使用你的starter了。

常見的starter會包括下面幾個方面的內容:

  1. 自動配置文件,根據classpath是否存在指定的類來決定是否要執行該功能的自動配置。
  2. spring.factories,非常重要,指導Spring Boot找到指定的自動配置文件。
  3. endpoint:可以理解爲一個admin,包含對服務的描述、界面、交互(業務信息的查詢)
  4. health indicator:該starter提供的服務的健康指標

在應用程序啓動過程中,Spring Boot使用SpringFactoriesLoader類加載器查找org.springframework.boot.autoconfigure.EnableAutoConfiguration關鍵字對應的Java配置文件。Spring Boot會遍歷在各個jar包種META-INF目錄下的spring.factories文件,構建成一個配置文件鏈表。除了EnableAutoConfiguration關鍵字對應的配置文件,還有其他類型的配置文件:

  • org.springframework.context.ApplicationContextInitializer
  • org.springframework.context.ApplicationListener
  • org.springframework.boot.SpringApplicationRunListener
  • org.springframework.boot.env.PropertySourceLoader
  • org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider
  • org.springframework.test.contex.TestExecutionListener

Spring Boot的starter在編譯時不需要依賴Spring Boot的庫。這個例子中依賴spring boot並不是因爲自動配置要用spring boot,而僅僅是因爲需要實現CommandLineRunner接口。

兩個需要注意的點

  1. @ConditionalOnMissingBean的作用是:只有對應的ban在系統中都沒有被創建,它修飾的初始化代碼塊纔會執行,用戶自己手動創建的bean優先
  2. Spring Boot starter如何找到自動配置文件(xxxxAutoConfiguration之類的文件)?

    • spring.factories:由Spring Boot觸發探測classpath目錄下的類,進行自動配置;
    • @Enable:有時需要由starter的用戶觸發*查找自動配置文件的過程。

Spring Boot 1.x系列

  1. Spring Boot的自動配置、Command-line-Runner
  2. 瞭解Spring Boot的自動配置
  3. Spring Boot的@PropertySource註解在整合Redis中的使用
  4. Spring Boot項目中如何定製HTTP消息轉換器
  5. Spring Boot整合Mongodb提供Restful接口
  6. Spring中bean的scope
  7. Spring Boot項目中使用事件派發器模式
  8. Spring Boot提供RESTful接口時的錯誤處理實踐

本號專注於後端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,爲讀者提供一線開發者的工作和成長經驗,期待你能在這裏有所收穫。
javaadu

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