鍍金池/ 教程/ Java/ Spring Boot:定制自己的starter
通過JMX監(jiān)控Spring Boot應(yīng)用
Spring Boot:定制PropertyEditors
配置是否初始化Bean的方法
Spring Boot的自動(dòng)配置、Command-line Runner
Spring Boot:定制URL匹配規(guī)則
Spring Boot的自動(dòng)配置、Command-line Runner
利用Mockito模擬DB
Spring Boot應(yīng)用的打包和部署
了解Spring Boot的自動(dòng)配置
Spring Boot應(yīng)用的健康監(jiān)控
了解Spring Boot的自動(dòng)配置
初始化數(shù)據(jù)庫和導(dǎo)入數(shù)據(jù)
Spring Boot應(yīng)用的健康監(jiān)控
Docker with Spring Boot
RESTful by Spring Boot with MySQL
Spring Boot:定制攔截器
Spring Boot:定制static path mappings
Spring Boot with Mysql
Spring Boot:定制自己的starter
在測試中使用內(nèi)存數(shù)據(jù)庫
Restful: Spring Boot with Mongodb
Spring Boot with Redis
Spring Boot:定制HTTP消息轉(zhuǎn)換器
Spring Boot: Data Rest Service
Spring Boot:定制type Formatters
在Spring Boot項(xiàng)目中使用Spock框架
選擇Spring Boot項(xiàng)目的內(nèi)嵌容器
通過EmbeddedServletContainerCustomizer接口調(diào)優(yōu)Tomcat
Spring Boot應(yīng)用的打包和部署
Spring Boot Admin的使用
讓你的Spring Boot工程支持HTTP和HTTPS
Spring Boot:定制servlet filters
Spring Boot:定制URL匹配規(guī)則
Spring Boot應(yīng)用的測試——Mockito
Spring Boot應(yīng)用的測試——Mockito
Spring Boot:定制servlet filters
通過@Enable*注解觸發(fā)Spring Boot配置

Spring Boot:定制自己的starter

在學(xué)習(xí)Spring Boot的過程中,接觸最多的就是starter??梢哉J(rèn)為starter是一種服務(wù)——使得使用某個(gè)功能的開發(fā)者不需要關(guān)注各種依賴庫的處理,不需要具體的配置信息,由Spring Boot自動(dòng)通過classpath路徑下的類發(fā)現(xiàn)需要的Bean,并織入bean。舉個(gè)例子,spring-boot-starter-jdbc這個(gè)starter的存在,使得我們只需要在BookPubApplication下用@Autowired引入DataSource的bean就可以,Spring Boot會(huì)自動(dòng)創(chuàng)建DataSource的實(shí)例。

這里我們會(huì)用一個(gè)不太規(guī)范的starter展示Spring Boot的自動(dòng)配置的運(yùn)行原理。Spring Boot的自動(dòng)配置、Command-line Runner一文中曾利用StartupRunner類在程序運(yùn)行啟動(dòng)后首先查詢數(shù)據(jù)庫中書的數(shù)目,現(xiàn)在換個(gè)需求:在系統(tǒng)啟動(dòng)后打印各個(gè)實(shí)體的數(shù)量。

How Do

  • 新建一個(gè)模塊db-count-starter,然后修改db-count-starter模塊下的pom文件,增加對應(yīng)的庫。
<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>
  • 新建包結(jié)構(gòu)com/test/bookpubstarter/dbcount,然后新建DbCountRunner類,實(shí)現(xiàn)CommandLineRunner接口,在run方法中輸出每個(gè)實(shí)體的數(shù)量。
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";
    }
}
  • 增加自動(dòng)配置文件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文件,這個(gè)文件用于告訴Spring Boot去找指定的自動(dòng)配置文件,因此它的內(nèi)容是
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.test.bookpubstarter.dbcount.DbCountAutoConfiguration
  • 在之前的程序基礎(chǔ)上,在頂層pom文件中增加starter的依賴
<dependency>
   <groupId>com.test</groupId>
   <artifactId>db-count-starter</artifactId>
   <version>0.0.1-SNAPSHOT</version>
</dependency>
  • 把StartupRunner相關(guān)的注釋掉,然后在main函數(shù)上右鍵Run BookPubApplication.main(...),可以看出我們編寫的starter被主程序使用了。

http://wiki.jikexueyuan.com/project/spring-boot-cookbook-zh/images/c0.png" alt="自己的starter簡單演示.png" />

分析

正規(guī)的starter是一個(gè)獨(dú)立的工程,然后在maven中新倉庫注冊發(fā)布,其他開發(fā)人員就可以使用你的starter了。

常見的starter會(huì)包括下面幾個(gè)方面的內(nèi)容:

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

在應(yīng)用程序啟動(dòng)過程中,Spring Boot使用SpringFactoriesLoader類加載器查找org.springframework.boot.autoconfigure.EnableAutoConfiguration關(guān)鍵字對應(yīng)的Java配置文件。Spring Boot會(huì)遍歷在各個(gè)jar包種META-INF目錄下的spring.factories文件,構(gòu)建成一個(gè)配置文件鏈表。除了EnableAutoConfiguration關(guān)鍵字對應(yīng)的配置文件,還有其他類型的配置文件:

  • 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在編譯時(shí)不需要依賴Spring Boot的庫。這個(gè)例子中依賴spring boot并不是因?yàn)樽詣?dòng)配置要用spring boot,而僅僅是因?yàn)樾枰獙?shí)現(xiàn)CommandLineRunner接口。

兩個(gè)需要注意的點(diǎn)

  1. @ConditionalOnMissingBean的作用是:只有對應(yīng)的ban在系統(tǒng)中都沒有被創(chuàng)建,它修飾的初始化代碼塊才會(huì)執(zhí)行,用戶自己手動(dòng)創(chuàng)建的bean優(yōu)先;

  2. Spring Boot starter如何找到自動(dòng)配置文件(xxxxAutoConfiguration之類的文件)?
    • spring.factories:由Spring Boot觸發(fā)探測classpath目錄下的類,進(jìn)行自動(dòng)配置;
    • @Enable:有時(shí)需要由starter的用戶觸發(fā)*查找自動(dòng)配置文件的過程。