编写自己的Spring-Boot的starter

  • 【概念理解】starter是一种服务——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息, 由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入相应的Bean。举个栗子,spring-boot-starter-jdbc这个starter的存在, 使得我们只需要在BookPubApplication下用@Autowired引入DataSource的bean就可以,Spring Boot会自动创建DataSource的实例。(记住啦,是一个服务,service, starter is a service)
  • 【关于配置的好习惯】
    @ConfigurationProperties注解的类会被spring-boot-configuration-processor检测,spring-boot-configuration-processor 的作用是编译时生成spring-configuration-metadata.json, 此文件主要给IDE使用,用于提示使用。如在intellij idea中,当配置此jar相关配置属性在application.yml, 你可以用ctlr+鼠标左键,IDE会跳转到你配置此属性的类中。这个是附加功能,对starter的用户友好。我为人人,人人为我。
  • 【关于命名】这里说下artifactId的命名问题,Spring 官方 Starter通常命名为spring-boot-starter-{name} 如 spring-boot-starter-web。Spring官方建议非官方Starter命名应遵循{name}-spring-boot-starter的格式。
  • 【要素】
  • 编写service类实现,
  • 编写ServiceProperties,要用@ConfigurationProperties注解声明一下,这样Spring就会根据注解读取application.yml的配置,根据配置创建这个ServiceProperties的Bean实例。
  • 编写AutoConfigure,用于控制Bean的初始化创建工作(这个类似编写application-context.xml, 但是他比application多一个功能,那就是可以通过@Conditional这些条件注解,进行控制是否开启。因为Spring-Boot强调的是约定优先,事先约定条件,进行默认配置。不要动不动就得重新配置)
  • 在resources/META-INF/创建spring.factories文件,文件内容如下,由于SpringBoot启动的时候不会扫描AutoConfigure类(Bean创建用的上下文),而是扫描所有Jar包的【resources/META-INF/】目录,然后才会加载AutoConfigure类,并初始化Service实例。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xncoding.starter.config.ExampleAutoConfigure

完整代码如下:

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


// 编写服务类
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;
    }
}

// 编写Spring上下文类
@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());
    }

}

// 添加meta文件到resources/META-INF/spring.factories

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


// mvn:install 打包安装


// 测试过程

// 引入starter
<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
 // main方法
 
@RunWith(SpringRunner.class)@SpringBootTestpublic class ApplicationTests {
    @Autowired
    private ExampleService exampleService;

    @Test
    public void testStarter() {
        System.out.println(exampleService.wrap("hello"));
    }
}
  • 【关键知识点,必看】@ConditionalOnMissingBean配置注解的类,可以被重写(也就可以重新定义类的初始化,比如默认是单例的Bean可以改成多例,又或者默认的application.yml参数满足你的需求,你也可以重新搞一个)