自己實現一個Starter

自己實現一個Starter

自己實現SpringBoot裏面的Starter

原理

一個公用是starter我們只需要引入pom文件,SpringBoot就會進行自動配置。那麼SpringBoot是如何知道要實例化哪些類,並進行簡單配置呢?

  1. 首先,SpringBoot在啓動時會去依賴的Starter包中尋找resources/META-INF/spring.factories 文件,然後根據文件中配置的Jar包去掃描項目所依賴的Jar包,這類似於 Java 的 SPI 機制。

  2. 第二步,根據 spring.factories配置加載AutoConfigure

  3. 最後,根據 @Conditional註解的條件,進行自動配置並將Bean注入Spring Context 上下文當中。

    我們也可以使用@ImportAutoConfiguration({MyServiceAutoConfiguration.class}) 指定自動配置哪些類。

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

核心知識

1. @Conditional註解

核心就是條件註解 @Conditional ,使用方式如下

//當項目當前Classpath存在 HelloService的時候 後面的配置才生效
@ConditionalOnClass(HelloService.class)
public class HelloServiceAutoConfiguration {}

SpringBoot許多源碼裏面,都會出現條件註解,這就是Starter配置的核心之一。

  1. spring.factories 文件,這個是springboot啓動會掃描的文件,然後尋找所依賴的jar包,寫法如下
org.springframework.boot.autoconfigure.EnableAutoConfiguration = 
//等於號後面是定義的starter的路徑
com.example.autocinfigure.StarterAutoConfigure

這裏舉個栗子:我們自己開發項目引入的mybatis-starter如下
同樣能看到mybatis的spring.factories文件
同樣能看到mybatis的spring.factories文件

打開後,如下

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

開發自己的Starter

所謂starter,就是一個普通的Maven項目

我們要實現的starter很簡單, 提供一個Service, 包含一個sayHello() 方法

主要分爲下面幾步驟

  1. 創建Maven項目
  2. 編寫service
  3. 編寫屬性類
  4. 編寫自動配置類
  5. 創建spring.factories文件,打包

創建Maven工程

新建一個普通Maven項目

  • 命名:這裏說下artifactId的命名問題,Spring 官方 Starter通常命名爲spring-boot-starter-{name}spring-boot-starter-web

    Spring官方建議非官方Starter命名應遵循{name}-spring-boot-starter的格式。

  • 注意其中 spring-boot-configuration-processor 的作用是編譯時生成spring-configuration-metadata.json, 此文件主要給IDE使用,用於提示使用。如在intellij idea中,當配置此jar相關配置屬性在application.yml, 你可以用ctlr+鼠標左鍵,IDE會跳轉到你配置此屬性的類中。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xncoding</groupId>
    <artifactId>simple-spring-boot-starter</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>simple-spring-boot-starter</name>
    <description>一個簡單的自定義starter</description>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
    </parent>


    <dependencies>
        <!-- @ConfigurationProperties annotation processing (metadata for IDEs)
                 生成spring-configuration-metadata.json類,需要引入此類-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
    </dependencies>
</project>

編寫Servcie

public class ExampleService {

    private String prefix;
    private String suffix;

    public ExampleService(String prefix, String suffix) {
        this.prefix = prefix;
        this.suffix = suffix;
    }
    public String wrap(String word) {
        return prefix + word + suffix;
    }
}

編寫配置屬性類

@ConfigurationProperties("example.service")
public class ExampleServiceProperties {
    private String prefix;
    private String suffix;

    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
}

其中@ConfigurationPropeties 註解是讀取 application.yml中 以 example.service 開頭的屬性

編寫自動配置類

@Configuration
@ConditionalOnClass(ExampleService.class)
@EnableConfigurationProperties(ExampleServiceProperties.class)
public class ExampleAutoConfigure {

    private final ExampleServiceProperties properties;

    @Autowired
    public ExampleAutoConfigure(ExampleServiceProperties properties) {
        this.properties = properties;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "example.service", value = "enabled",havingValue = "true")
    ExampleService exampleService (){
        return  new ExampleService(properties.getPrefix(),properties.getSuffix());
    }

}

解釋下用到的幾個和Starter相關的註解:

1. @ConditionalOnClass,當classpath下發現該類的情況下進行自動配置。
2. @ConditionalOnMissingBean,當Spring Context中不存在該Bean時。
3. @ConditionalOnProperty(prefix = "example.service",value = "enabled",havingValue = "true"),當配置文件中example.service.enabled=true時。

最後一步:添加spring.factories文件

最後一步,在resources/META-INF/下創建spring.factories文件,內容供參考下面:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xncoding.starter.config.ExampleAutoConfigure

如果有多個自動配置類,用逗號分隔換行即可。

OK,完事,運行 mvn:install 打包安裝,一個Spring Boot Starter便開發完成了。

測試

打包好了當然要測試一下看看了。另外創建一個SpringBoot工程,在maven中引入這個starter依賴, 然後在單元測試中引入這個Service看看效果。

<dependency>
    <groupId>com.xncoding</groupId>
    <artifactId>simple-spring-boot-starter</artifactId>
    <version>1.0.0-SNAPSHOT</version>
</dependency>

修改application.yml配置文件,添加如下內容:

example.service:
  enabled: true
  prefix: ppp
  suffix: sss

測試類

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
    @Autowired
    private ExampleService exampleService;

    @Test
    public void testStarter() {
        System.out.println(exampleService.wrap("hello"));
    }
}

運行結果

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