如何開發本身的Spring Boot Starter

mp.weixin.qq.com/s/Eg_z26Hnc…


咱們在使用 Spring Boot 的過程當中,每每都是在pom.xml里加了一系列的依賴,而後啓支一個包含main方法的Application,一切就OK啦。給你個人感受,就像是本身要動手作個菜,本身再也不須要準備每一部分的原材料,直接購買包裝好的一份菜的原料,下鍋便可
那咱們詳細看下,這份「包裝好」的原料中,到底作了些什麼。web

添加Starter依賴

這裏添加的依賴,除了咱們以前在Maven中熟悉的以外,還有一些都是長這個樣子:
名爲xxx-starter,好比spring

<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是怎麼起做用的呢,他們何時開始工做的?bash

一切都要從入口處提及。咱們以上面的starter爲例,看到這個mybatis的starter,其對應的pom中,包含這些依賴mybatis

<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 的傳遞依賴,這些都會被自動添加了進來。spring-boot

自動配置

相比傳統的依賴,咱們看到其中包含這樣一個:mybatis-spring-boot-autoconfigure,這也是每一個Starter的祕密所在:「AutoConfigure」
它會在實現時,考慮應用中的其餘部分因素,「推斷」你所須要的 Spring 配置。工具

在Spring Boot中,咱們最大的感覺是配置彷彿都被作好了,直接使用便可,這就是
spring-boot-autoconfigure. 每一個starter都有一個名爲spring.factories
的文件,存放在META-INF目錄下,其中的內容相似下面這個樣子:ui

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration複製代碼

全部須要自動配置的Class,都須要配置成key是EnableAutoConfiguration的。
咱們來看類的內部this

@Configuration@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})@ConditionalOnBean({DataSource.class})@EnableConfigurationProperties({MybatisProperties.class})@AutoConfigureAfter({DataSourceAutoConfiguration.class})public class MybatisAutoConfiguration {複製代碼

Class 之上, 有很多註解來標識,有幾點須要關注的:spa

  • 其中有標準的 Spring 配置註解 @Configurationcode

  • 幾個@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啦。

相關文章
相關標籤/搜索