建立自定義Spring Boot Starter

摘要

在你們使用Spring Boot進行開發的過程當中,應該能夠接觸到Spring Boot提供的不少Starter。好比html

  • spring-boot-starter-web
  • spring-boot-starter-jdbc
  • ...

Spring Boot AutoConfiguration原理

Auto Configuration類

在Spring Boot啓動的過程當中,會去找classpath:META-INF/spring.factories文件,來決定加載哪些AutoConfiguration類。
讓咱們看一下spring-boot-autoconfigure項目中的spring.factories文件。下面列出部分AutoConfiguration類。java

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
...

Spring Boot會嘗試去運行全部org.springframework.boot.autoconfigure.EnableAutoConfiguration列出的AutoConfiguration類。此處你們能夠會有疑問,不少我在項目裏沒有用到。對應組件是否是也會被初始化?答案是不會。咱們用RedisAutoConfiguration爲例來看一下背後發生了什麼。git

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}
  • Line 1: @Configuration定義Configuration組件
  • Line 2: @ConditionalOnClass(RedisOperations.class) 在classpath中若是有RedisOperation類,則會運行此AutoConfiguration。反之則不會運行。
  • Line 8: @ConditionalOnMissingBean(name = "redisTemplate") 若是在Spring容器中沒有名爲redisTemplate的Bean被註冊,則執行redisTemplate()方法。反之則不會執行。

注:Spring Boot提供了一系列@Condition選擇,同時還能夠實現自定義Condition。參考Spring Boot - Condition Annotationsgithub

AutoConfiguration Hints

AutoConfiguration Hints功能主要便於開發人員在作配置時,IDE能夠提示auto complete。以下截圖:
auto-complete
Auto Complete信息包括:web

  • 配置名稱
  • 配置描述
  • 配置默認值

具體信息在additional-spring-configuration-metadata.json中定義redis

建立自定義Starter

  1. 須要封裝的Service類
public class FooService {
    private final String fooMessage;
    private final String barMessage;

    public FooService(String fooMessage, String barMessage) {
        this.fooMessage = fooMessage;
        this.barMessage = barMessage;
    }

    public String getFooMessage() {
        return this.fooMessage;
    }

    public String getBarMessage() {
        return this.barMessage;
    }
}
  1. AutoConfiguration類
package io.markfredchen.custom.starter.config;

import ...;

@Configuration
@PropertySource("classpath:config/foo.properties")
public class FooAutoConfiguration {
    @Value("${foo.fooMessage}")
    private String fooMessage;
    @Value("${foo.barMessage}")
    private String barMessage;

    @Bean
    @ConditionalOnMissingBean
    public FooService fooService() {
        return new FooService(fooMessage, barMessage);
    }
}
  1. spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.markfredchen.custom.starter.config.FooAutoConfiguration
  1. 默認配置

建立 resources/config/foo.propertiesspring

foo.fooMessage=Foo!
foo.barMessage=Bar!

使用自定義Starter

  1. 建立新項目
  2. SpringBootApplication
@SpringBootApplication
public class FooApplication {
    public static void main(String[] args) {
        SpringApplication.run(FooApplication.class, args);
    }

    @Bean
    public CommandLineRunner runner(final FooService fooService) {
        return args -> {
            System.out.println(fooService.getFooMessage());
            System.out.println(fooService.getBarMessage());
        };
    }
}
  1. pom.xml添加如下依賴
<dependencies>
    <dependency>
        <groupId>io.markfredchen</groupId>
        <artifactId>foo-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.0.5.RELEASE</version>
    </dependency>
</dependencies>
  1. 運行結果
2018-10-11 11:28:55.457  INFO 23978 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-11 11:28:55.457  INFO 23978 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-11 11:28:55.548  INFO 23978 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-10-11 11:28:55.586  INFO 23978 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2018-10-11 11:28:55.590  INFO 23978 --- [           main] i.m.c.s.foo.application.FooApplication   : Started FooApplication in 1.725 seconds (JVM running for 2.44)
Foo!
Bar!
  1. 覆蓋默認配置

建立resources/application.propertiesjson

foo.barMessage=Updated Bar!
  1. 運行結果
2018-10-11 11:28:55.457  INFO 23978 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-11 11:28:55.457  INFO 23978 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-10-11 11:28:55.548  INFO 23978 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-10-11 11:28:55.586  INFO 23978 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2018-10-11 11:28:55.590  INFO 23978 --- [           main] i.m.c.s.foo.application.FooApplication   : Started FooApplication in 1.725 seconds (JVM running for 2.44)
Foo!
Updated Bar!
  1. AutoConfiguration Hints

建立resources/META-INF/additional-spring-configuration-metadata.jsontomcat

{
  "properties": [
    {
      "name": "foo.fooMessage",
      "type": "java.lang.String",
      "description": "Foo Message.",
      "defaultValue": "Foo!"
    },
    {
      "name": "foo.barMessage",
      "type": "java.lang.String",
      "description": "Bar Message.",
      "defaultValue": "Bar!"
    }
  ]
}

效果
auto completeapp

參考

總結

本文介紹了spring boot starter機制以及如何建立自定義的starter。starter的目標是對如今有項目進行有效封裝,減小開發人員的重複工做。
完整源代碼訪問GitHub

相關文章
相關標籤/搜索