在學習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檔案,增加對應的庫。
org.springframework.boot
-
新建包結構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的依賴
com.test
-
把StartupRunner相關的註釋掉,然後在main函式上右鍵Run BookPubApplication.main(...),可以看出我們編寫的starter被主程式使用了。
分析
正規的starter是一個獨立的工程,然後在maven中新倉庫註冊釋出,其他開發人員就可以使用你的starter了。
常見的starter會包括下麵幾個方面的內容:
-
自動配置檔案,根據classpath是否存在指定的類來決定是否要執行該功能的自動配置。
-
spring.factories,非常重要,指導Spring Boot找到指定的自動配置檔案。
-
endpoint:可以理解為一個admin,包含對服務的描述、介面、互動(業務資訊的查詢)
-
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介面。
兩個需要註意的點
-
@ConditionalOnMissingBean的作用是:只有對應的ban在系統中都沒有被建立,它修飾的初始化程式碼塊才會執行,使用者自己手動建立的bean優先;
-
Spring Boot starter如何找到自動配置檔案(xxxxAutoConfiguration之類的檔案)?
-
spring.factories:由Spring Boot觸發探測classpath目錄下的類,進行自動配置;
-
@Enable:有時需要由starter的使用者觸發*查詢自動配置檔案的過程。