1. 概述

spring boot starter工程对于使用者是非常方便的,使用者通常只要在pom.xml引入starter的jar,则此工程依赖的类,就全部自动引入。因此我们常用的开源组件都会提供一个starter工程给开发者,让开发者非常方便集成本组件到spring中。本文通过自己实现一个简单的demo,详细说明了如何实现一个自己的spring boot starter工程。

2. 例子中工程说明

本文demo参考mybatis-spring-boot-starter和网上的文章创建自己的start工程。

定义我们自己的spring boot starter工程,通常需要以下4个工程:

  1. 模块的真正的实现类,如本文的工程为first
  2. *-spring-boot/*-spring的工程:此工程是*-spring-boot-autoconfigure和*-spring-boot-starter的父工程类。本例子里是first-spring-boot。此工程的命名规则如下:如果你在这个工程中引入spring依赖,则工程名称为*-spring,如果引入spring-boot依赖,则工程名称为*-spring-boot。
  3. *-spring-boot-autoconfigure工程:配置自动配置类,本例的名称first-spring-boot-autoconfigure
  4. *-spring-boot-starter工程:空工程类,在pom.xml中定义需要引入jar,本例的名称first-spring-boot-starter

测试工程:

  • test-main:演示如何使用自己的first-spring-boot-starter

例子的功能说明:

  • 定义ITestService接口和此接口的两个实现类,根据我们的配置,程序自动初始化指定的一个类。

3. first工程

此工程中不要引入扫描相关的注解,如@Autowire, @Componet, @Service, @Control等等; 不要引入spring 相关的类

本工程功能功能: 定义ITestService接口和此接口的两个实现类

定义ITestService接口

public interface ITestService {

    String queryById(String id);
}

此接口的两个实现类

  • TestServiceImpl
public class TestServiceImpl implements ITestService {
    @Override
    public String queryById(String id) {
        return id + ":" + this.getClass().getSimpleName() ;
    }

}
  • TestServiceBackImpl
public class TestServiceBackImpl implements ITestService {

    @Override
    public String queryById(String id) {
        return id + ":" + this.getClass().getSimpleName() ;
    }
}

4. first-spring-boot

此工程中也不要引入扫描相关的注解,如@Autowire, @componet, @Service, @Controller等等
此类可以添加自己的类,这些类需要使用到spring-boot里的类。我们的例子里不需要添加和spring-boot相关的类,所有此工程没有java类。

这个工程主要定义子模块的需要用到的jar

4.1. pom.xml

定义版本信息和属性值,packaging值为pom

   <artifactId>first-spring-boot</artifactId>
    <groupId>com.hry.spring.first</groupId>
    <version>1.0.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>
    <!-- 类型为pom -->
    <packaging>pom</packaging>

    <properties>
        <first.version>1.0.0-SNAPSHOT</first.version>
        <spring-boot.version>1.5.6.RELEASE</spring-boot.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mavenpiler.source>1.8</mavenpiler.source>
        <mavenpiler.target>1.8</mavenpiler.target>
    </properties>

定义两个子模块

 <modules>
     <module>../first-spring-boot-autoconfigure</module>
     <module>../first-spring-boot-starter</module>
 </modules>

由于不是所有的子模块都会用到以下的类,所有使用dependencyManagement预先定义相关的jar和版本。保证在子类中需要使用时,只需要引入包,而不用使用版本号

<!-- 定义子模块中需要的jar -->
<dependencyManagement>
    <dependencies>
        <!-- spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <!-- first model -->
        <dependency>
            <groupId>com.hry.spring.first</groupId>
            <artifactId>first</artifactId>
            <version>${first.version}</version>
        </dependency>
        <!-- first-spring-boot-autoconfigure -->
        <dependency>
            <groupId>com.hry.spring.first</groupId>
            <artifactId>first-spring-boot-autoconfigure</artifactId>
            <version>${first.version}</version>
        </dependency>
        <!--  first-spring-boot-starter -->
        <dependency>
            <groupId>com.hry.spring.first</groupId>
            <artifactId>first-spring-boot-starter</artifactId>
            <version>${first.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

5. first-spring-boot-autoconfigure

配置自动扫描类

5.1 FirstProperties

属性配置类,自动从配置文件中解析属性并生成对象

@ConfigurationProperties(prefix = FirstProperties.TEST_PREFIX)
public class FirstProperties {
   public static final String TEST_PREFIX = "my.test";

   private boolean openTestServiceBack; // 如果true,则创建openTestService,否则使用TestServiceBackImpl

   private String name;

   public boolean isOpenTestServiceBack() {
       return openTestServiceBack;
   }

   public void setOpenTestServiceBack(boolean openTestServiceBack) {
       this.openTestServiceBack = openTestServiceBack;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }
}

5.2. FirstModuleAutoConfiguration

  • @Configuration:定义配置类
  • @EnableConfigurationProperties:启动上面的配置类

    createTestService方法:根据配置的属性生成ITestService 的实现类

@Configuration
@EnableConfigurationProperties(FirstProperties.class)
public class FirstModuleAutoConfiguration {

    @Autowired
    private FirstProperties firstProperties;

    @Bean
    public ITestService createTestService(){
        if(firstProperties.isOpenTestServiceBack()){
            return new TestServiceBackImpl();
        }else{
            return new TestServiceImpl();
        }
    }

}

5.3. @AutoConfigure*和Conditional*

如果我们的FirstModuleAutoConfiguration 的初始化必须依赖其他的自动化配置类(如OtherModuleAutoConfiguration.class),则在此类上增加以下注解

@AutoConfigureAfter(OtherModuleAutoConfiguration.class)

我们还可以在配置类定义以下注解

  1. @AutoConfigureAfter: 提示自动配置应在其他指定的自动配置类之后应用。
  2. @AutoConfigureBefore: 提示自动配置应在其他指定的自动配置类之前应用
  3. @AutoConfigureOrder:定义配置类执行的顺序

另外我们也可以加上以下@Conditional条件注解

  • @ConditionalOnBean:当容器里有指定的Bean 的条件下。
  • @ConditionalOnMissingBean:当容器里没有指定Bean 的情况下
  • @ConditionalOnClass:当类路径下有指定的类的条件下。
  • @ConditionalOnMissingClass:当类路径下没有指定的类的条件下。
  • @ConditionalOnExpression:基于SpEL 表达式作为判断条件。
  • @ConditionalOnJava:基于JVM 版本作为判断条件。
  • @ConditionalOnJndi:在JNDI存在的条件下查找指定的位置。
  • @ConditionalOnProperty:指定的属性是否有指定的值。
  • @ConditionalOnResource:类路径是否有指定的值。
  • @ConditionalOnSingleCandidate:当指定Bean 在容器中只有一个,或者虽然有多个但是指定首选的Bean。
  • @ConditionalOnWebApplication:当前项目是Web 项目的条件下。
  • @ConditionalOnNotWebApplication:当前项目不是Web 项目的条件下。

5.4. spring.factories

src/resources/META-INF/spring.factories:
配置FirstModuleAutoConfiguration在此文件,则spring boot会自动生成初始化此类

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hry.spring.first.autoconfigure.FirstModuleAutoConfiguration

5.5. pom.xml

定义父类pom.xml

<parent>
    <artifactId>first-spring-boot</artifactId>
    <groupId>com.hry.spring.first</groupId>
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>../first-spring-boot/pom.xml</relativePath>
</parent>

<artifactId>first-spring-boot-autoconfigure</artifactId>
<modelVersion>4.0.0</modelVersion>

引入相关的jar。其中 spring-boot-configuration-processor 的作用是编译时生成spring-configuration-metadata.json,此文件主要给IDE使用,用于提示使用。如在intellij idea中,当配置此jar相关配置属性在application.properties,你可以用ctlr+鼠标左键,IDE会跳转到你配置此属性的类中

<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>

     <!-- first model -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot</artifactId>
     </dependency>

     <dependency>
         <groupId>com.hry.spring.first</groupId>
         <artifactId>first</artifactId>
     </dependency>
 </dependencies>

6. first-spring-boot-starter

starter工程,此工程没有java类,只在pom.xml中定义依赖的jar

6.1. spring.provides

此文件说明此工程依赖的模块,和pom.xml不同的地方这个文件主要给IDE使用,IDE会根据此文件提示使用者此模块的依赖jar。所以此文件就算什么都不配,对first模块使用也没有什么影响,最多IDE提示少一些。

英文说明:
It’s for tooling. STS (and other IDEs if they chose to) can index those files and make autocomplete suggestions based on stuff that isn’t yet on the classpath.’

spring.provides内容如下:

provides: spring-boot

6.2. pom.xml

引入父类和 定义artifactId

 <parent>
     <artifactId>first-spring-boot</artifactId>
     <groupId>com.hry.spring.first</groupId>
     <version>1.0.0-SNAPSHOT</version>
     <relativePath>../first-spring-boot/pom.xml</relativePath>
 </parent>

 <artifactId>first-spring-boot-starter</artifactId>

依赖jar

<dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-autoconfigure</artifactId>
     </dependency>
     <!-- first-spring-boot-autoconfigure -->
     <dependency>
         <groupId>com.hry.spring.first</groupId>
         <artifactId>first-spring-boot-autoconfigure</artifactId>
     </dependency>
     <!-- first module -->
     <dependency>
         <groupId>com.hry.spring.first</groupId>
         <artifactId>first</artifactId>
     </dependency>
 </dependencies>

7. test-main测试工程

测试使用上面实现的starter工程

7.1. TestApplication

测试入口类:打印配置信息

@SpringBootApplication
public class TestApplication {
    public static void main(String[] args){
        ConfigurableApplicationContext cac = SpringApplication.run(TestApplication.class, args);
        FirstProperties firstProperties = cac.getBean(FirstProperties.class);
        System.out.println(firstProperties.getName());

        ITestService testService = cac.getBean(ITestService.class);
        System.out.println(testService.queryById("test"));
    }
}

7.2. application.yml

配置以下参数后,系统启动时,会自动生成FirstProperties对象,并注入以下值,然后FirstModuleAutoConfiguration根据open-test-service-back生成不同的ITestService的实现对象。

my:
  test:
    open-test-service-back: true
    name: dsfdsd

pom.xml

引入刚刚生成first-spring-boot-starter工程
必须要引入snakeyaml-*.jar,否则系统无法识别yml格式的application.yml,从而使用我们的配置不起作用

    <groupId>com.hry.spring.start</groupId>
    <version>1.0-SNAPSHOT</version>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>test-main</artifactId>
    <packaging>jar</packaging>

    <properties>
        <mavenpiler.source>1.8</mavenpiler.source>
        <mavenpiler.target>1.8</mavenpiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.hry.spring.first</groupId>
            <artifactId>first-spring-boot-starter</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

        <!-- 如果要解析配置yml,则需要加入yml值 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
            <version>1.17</version>
        </dependency>

    </dependencies>

7.3. 测试

  1. 执行first的pom.xml,生成测试包first-1.0.0-SNAPSHOT.jar
  2. 执行first-spring-boot的pom.xml,生成first-spring-boot-autoconfigure-1.0.0-SNAPSHOT.jar、first-spring-boot-starter-1.0.0-SNAPSHOT.jar、first-spring-boot-1.0.0-SNAPSHOT.pom
  3. 执行TestApplication

输出如下信息,则表示执行成功

dsfdsd
test:TestServiceBackImpl

8. 代码

上文使用到的代码在github上,详细见这里

更多推荐

Spring Boot系列七 实现自己的spring boot starter工程