如何開發自己的 Spring Boot Starter
我們在使用 Spring Boot 的過程中,往往都是在pom.xml里加了一系列的依賴,然後啟支一個包含main方法的Application,一切就OK啦。給你我的感覺,就像是 自己要動手做個菜,自己不再需要準備每一部分的原材料,直接購買包裝好的一份菜的原料,下鍋即可 。
那我們詳細看下,這份「包裝好」的原料中,到底做了些什麼。
新增Starter依賴
這裡新增的依賴,除了我們之前在Maven中熟悉的之外,還有一些都是長這個樣子:
名為xxx-starter,比如
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
具體這些starter是怎麼起作用的呢,他們什麼時候開始工作的?
一切都要從入口處說起。我們以上面的starter為例,看到這個mybatis的starter,其對應的pom中,包含這些依賴
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-autoconfigure</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </dependency> </dependencies>
我們看到,相當於我們添加了一個Starter的依賴,其背後會引入許多其定義的其他依賴,通過 Maven 的傳遞依賴,這些都會被自動添加了進來。
自動配置
相比傳統的依賴,我們看到其中包含這樣一個: mybatis-spring-boot-autoconfigure
,這也是每個Starter的祕密所在:「AutoConfigure」
它會在實現時,考慮應用中的其他部分因素,「推斷」你所需要的 Spring 配置。
在Spring Boot中,我們最大的感受是配置彷彿都被做好了,直接使用即可,這就是
spring-boot-autoconfigure
. 每個starter都有一個名為
spring.factories
的檔案,存放在META-INF目錄下,其中的內容類似下面這個樣子:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
所有需要自動配置的Class,都需要配置成key是 EnableAutoConfiguration
的。
我們來看類的內部
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnBean({DataSource.class}) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration {
Class 之上, 有不少註解來標識,有幾點需要關注的:
-
其中有標準的 Spring 配置 註解
@Configuration
-
幾個
@ConditionalXX
-
標識執行順序的
@AutoConfigureAfter
其中, @ConditionalOnClass
標識 SqlSessionFactory
類存在時,執行該配置, @ConditionalOnBean
標識 DataSource
Bean 在 Spring Context時,執行配置。
這些 spring.factories
是怎麼被識別的呢? 這就得誇下 Spring 的 FactoriesLoader
了。
看下官方文件說明
Auto-configuration classes are regular Spring {@link Configuration} beans. They are located using the {@link SpringFactoriesLoader} mechanism (keyed against this class). Generally auto-configuration beans are {@link Conditional @Conditional} beans (most often using {@link ConditionalOnClass @ConditionalOnClass} and {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
啟動的時候,根據ClassLoader中的jar,掃描所有 spring.factories
,將其中符合條件的過濾出來,執行對應的配置。重點可以關注下
AutoConfigurationImportFilter
類,
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() { return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { invokeAwareMethods(filter); boolean[] match = filter.match(candidates, autoConfigurationMetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { skip[i] = true; skipped = true; } } } if (!skipped) { return configurations; } List<String> result = new ArrayList<>(candidates.length); for (int i = 0; i < candidates.length; i++) { if (!skip[i]) { result.add(candidates[i]); } } return new ArrayList<>(result); } public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); }
經過這裡的執行之後, filter方法把符合條件的過濾出來了。
建立自定義Starter
經過上面兩步,我們大概知道 Starter的工作原理。有時候,我們需要對外提供一些工具元件時,也想以 Starter 的形式提供出來,供別人使用。步驟也還算清晰,照葫蘆畫瓢。
-
先建立自己的模組
-
增加需要用到的依賴
-
建立對應的
AutoConfiguration
類 -
建立
META-INF/spring.factories
檔案
此時,就不需要再將 Spring Boot 做為 Parent依賴,在單獨的依賴中增加
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.0.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.6.RELEASE</version> </dependency>
AutoConfiguration類也簡單,照上面的建立一個
@Configuration @ConditionalOnClass(HelloService.class) public class HelloServiceAutoConfiguration {
然後,增加檔案
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.demo.HelloServiceAutoConfiguration
在需要這個服務的地方,直接引入依賴就OK啦。
相關閱讀:
ofollow,noindex">Spring 的自定義Schema是如何解析生效的
Tomcat 是怎樣處理 SpringBoot應用的?
關注『 Tomcat那些事兒 』 ,發現更多精彩文章!瞭解各種常見問題背後的原理與答案。深入原始碼,分析細節,內容原創,歡迎關注。
轉發是最大的支援 ,謝謝
更多精彩內容:
一臺機器上安裝多個Tomcat 的原理(回覆001)
監控Tomcat中的各種資料 (回覆002)
啟動Tomcat的安全機制(回覆003)
亂碼問題的原理及解決方式(回覆007)
Tomcat 日誌工作原理及配置(回覆011)
web.xml 解析實現(回覆 012)
執行緒池的原理( 回覆 014)
Tomcat 的叢集搭建原理與實現 (回覆 015)
類載入器的原理 (回覆 016)
類找不到等問題 (回覆 017)
程式碼的熱替換實現(回覆 018)
Tomcat 程序自動退出問題 (回覆 019)
為什麼總是返回404? (回覆 020)
...
PS: 對於一些 Tomcat常見問 題 ,在公眾號的【 常見問題 】選單中,有需要的朋友歡迎關注檢視。