Springboot 的 Starter 全面介绍和一个简单案例

1 概述

1.1 概念

Starter 是一组可以被包含在应用程序中的便利的依赖描述。有了 starter,你相当于有了一个一站式服务点,该服务点针对所有你需要的 Spring 和 Spring 相关的技术,不需要再去翻找样例代码和 复制-粘贴 依赖描述的工作。例如,需要使用 Spring 和 JPA(用于访问数据库),只需要在项目中引入 spring-boot-starter-data-jpa 依赖即可。
Starter 包含了很多你需要的依赖,这些依赖使用一组一致的相互兼容的管理传递依赖,来构建和快速运行项目。

参考的官方文档链接:

  1. https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
  2. https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration
  3. https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.custom-starter

1.2 命名规范

官方的 Starter 的命名样式是 spring-boot-starter-*,其中 * 是一个特定类型的应用。该命名结构可以帮助你方便地找到想要的 Starter。比如在大多数 IDE(比如 IDEA)中,假设在 pom.xml 中的 dependency.artifactId 属性中已存在文本spring-boot-starter--,那么我们按快捷键 Ctrl + Space ,则 IDE 会自动补全一份 starter 的下拉列表,供我们选择,如下图:

而第三方的 starter 名称则不能以 spring-boot 开头,因为这个是给官方 Spring Boot artifacts 预留的。第三方 starter 的名称通常以项目名称开头,例如一个第三方项目的名称是 thirdpartyproject,则其构建的 starter 名称通常为 thirdpartyproject-spring-boot-starter

2 Spring Boot 官方 Starters

2.1 应用 Starters

这些 starters 都在 org.springframework.boot 组中:

NameDescription
spring-boot-starterCore starter, including auto-configuration support, logging and YAML
spring-boot-starter-activemqStarter for JMS messaging using Apache ActiveMQ
spring-boot-starter-amqpStarter for using Spring AMQP and Rabbit MQ
spring-boot-starter-aopStarter for aspect-oriented programming with Spring AOP and AspectJ
spring-boot-starter-artemisStarter for JMS messaging using Apache Artemis
spring-boot-starter-batchStarter for using Spring Batch
spring-boot-starter-cacheStarter for using Spring Framework’s caching support
spring-boot-starter-data-cassandraStarter for using Cassandra distributed database and Spring Data Cassandra
spring-boot-starter-data-cassandra-reactiveStarter for using Cassandra distributed database and Spring Data Cassandra Reactive
spring-boot-starter-data-couchbaseStarter for using Couchbase document-oriented database and Spring Data Couchbase
spring-boot-starter-data-couchbase-reactiveStarter for using Couchbase document-oriented database and Spring Data Couchbase Reactive
spring-boot-starter-data-elasticsearchStarter for using Elasticsearch search and analytics engine and Spring Data Elasticsearch
spring-boot-starter-data-jdbcStarter for using Spring Data JDBC
spring-boot-starter-data-jpaStarter for using Spring Data JPA with Hibernate
spring-boot-starter-data-ldapStarter for using Spring Data LDAP
spring-boot-starter-data-mongodbStarter for using MongoDB document-oriented database and Spring Data MongoDB
spring-boot-starter-data-mongodb-reactiveStarter for using MongoDB document-oriented database and Spring Data MongoDB Reactive
spring-boot-starter-data-neo4jStarter for using Neo4j graph database and Spring Data Neo4j
spring-boot-starter-data-r2dbcStarter for using Spring Data R2DBC
spring-boot-starter-data-redisStarter for using Redis key-value data store with Spring Data Redis and the Lettuce client
spring-boot-starter-data-redis-reactiveStarter for using Redis key-value data store with Spring Data Redis reactive and the Lettuce client
spring-boot-starter-data-restStarter for exposing Spring Data repositories over REST using Spring Data REST
spring-boot-starter-freemarkerStarter for building MVC web applications using FreeMarker views
spring-boot-starter-graphqlStarter for building GraphQL applications with Spring GraphQL
spring-boot-starter-groovy-templatesStarter for building MVC web applications using Groovy Templates views
spring-boot-starter-hateoasStarter for building hypermedia-based RESTful web application with Spring MVC and Spring HATEOAS
spring-boot-starter-integrationStarter for using Spring Integration
spring-boot-starter-jdbcStarter for using JDBC with the HikariCP connection pool
spring-boot-starter-jerseyStarter for building RESTful web applications using JAX-RS and Jersey. An alternative to spring-boot-starter-web
spring-boot-starter-jooqStarter for using jOOQ to access SQL databases with JDBC. An alternative to spring-boot-starter-data-jpa or spring-boot-starter-jdbc
spring-boot-starter-jsonStarter for reading and writing json
spring-boot-starter-jta-atomikosStarter for JTA transactions using Atomikos
spring-boot-starter-mailStarter for using Java Mail and Spring Framework’s email sending support
spring-boot-starter-mustacheStarter for building web applications using Mustache views
spring-boot-starter-oauth2-clientStarter for using Spring Security’s OAuth2/OpenID Connect client features
spring-boot-starter-oauth2-resource-serverStarter for using Spring Security’s OAuth2 resource server features
spring-boot-starter-quartzStarter for using the Quartz scheduler
spring-boot-starter-rsocketStarter for building RSocket clients and servers
spring-boot-starter-securityStarter for using Spring Security
spring-boot-starter-testStarter for testing Spring Boot applications with libraries including JUnit Jupiter, Hamcrest and Mockito
spring-boot-starter-thymeleafStarter for building MVC web applications using Thymeleaf views
spring-boot-starter-validationStarter for using Java Bean Validation with Hibernate Validator
spring-boot-starter-webStarter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container
spring-boot-starter-web-servicesStarter for using Spring Web Services
spring-boot-starter-webfluxStarter for building WebFlux applications using Spring Framework’s Reactive Web support
spring-boot-starter-websocketStarter for building WebSocket applications using Spring Framework’s WebSocket support

2.2 用于添加产品级(production-ready)功能的 Starters

产品 starters:

NameDescription
spring-boot-starter-actuatorStarter for using Spring Boot’s Actuator which provides production ready features to help you monitor and manage your application

2.3 用于排除或替换特定技术部分的 Starters

技术 starters:

NameDescription
spring-boot-starter-jettyStarter for using Jetty as the embedded servlet container. An alternative to spring-boot-starter-tomcat
spring-boot-starter-log4j2Starter for using Log4j2 for logging. An alternative to spring-boot-starter-logging
spring-boot-starter-loggingStarter for logging using Logback. Default logging starter
spring-boot-starter-reactor-nettyStarter for using Reactor Netty as the embedded reactive HTTP server.
spring-boot-starter-tomcatStarter for using Tomcat as the embedded servlet container. Default servlet container starter used by spring-boot-starter-web
spring-boot-starter-undertowStarter for using Undertow as the embedded servlet container. An alternative to spring-boot-starter-tomcat

注:

  1. 详细的替换技术部分,请参考 替换web服务器日志系统how-to 文档。

  2. 社区贡献的 starters 列表,参考 GitHub 的 spring-boot-starters 模块的 README 文件。

3 创建自定义 Starters

3.1 概述

一个典型的 Spring Boot starter 包含“自动配置(auto-configuration)”和“定制化给定技术的基础架构”的代码,我们称之为 acme。为了使其容易扩展,我们可以暴露一些专门的命名空间的配置 keys 给到环境。最终,会提供一个单独的“starter”依赖来帮助用户尽可能容易地启动。

具体来说,一个自定义的 starter 可以包含以下部分:

  1. autoconfigure 模块,包含了用于 acme 的自动配置代码;
  2. starter 模块,提供了用于 autoconfigure 模块 和 acme 的一个依赖,以及任何有用的额外依赖。总之,添加 starter 需要提供需要使用该库启动的一切内容。

不过,分隔为2个模块完全没有必要。

如果“acme”有几种风味,选择,或者可选特性(optional features),则最好分隔该自动配置,因为你能够清晰地表达“某些功能特性是可选的”这一事实。另外,你能够制作一个 starter ,该 starter 能提供一项关于那些可选依赖的意见。同时,其他人可以仅依赖该 autoconfigure 模块,使用不同的意见制作他们自己的 starter。

如果该自动配置相对简单,且没有可选特性,则在 starter 中合并2个模块当然是一个不错的选择。

在介绍更深入的内容之前,我们需要先了解一些前置知识:3.2 自动配置(Auto-configuration)

3.2 自动配置(Auto-configuration)

3.2.1 概述

如果你们公司需要开发共享库,或者你要开发开源库或商用库,你可能会想开发自己的 auto-configuration。Auto-configuration 类可以绑定到外部 JAR 包,且仍然可以被 Spring Boot 识别到。

Auto-configuration 可以关联到一个 starter,该 starter 提供了 auto-configuration 代码和你将会用到的典型库。首先,我们覆盖需要的内容,从而构建你自己的 auto-configuration;然后,我们移步到创建自定义 starter 的必要常用步骤

创建 starter 的官方样例:demo project

3.2.2 理解自动配置(auto-configuration) Beans

在底层,auto-configuration 使用注解 @AutoConfiguration 实现。该注解本身是使用 @Configuration 作为元注解,从而可以将 auto-configurations 制作成标准的 @Configuration 类。额外的 @Conditional 注解则用于约束何时应用 auto-configuration。通常,auto-configuration 类使用 @ConditionalOnClass 和 @ConditionalOnMissingBean 注解。这保证了 auto-configuration 仅仅在 相关类被发现 并且 你没有声明你自己的 @Configuration 类 时被应用。

你可以浏览 spring-boot-autoconfigure 的源码,查看 Spring 提供的 @Configuration 类(查看 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件)。

3.2.3 定位 Auto-configuration 候选者

Spring Boot 检查 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件是否存在于你发布的 jar 包中。该文件应该列举出你的配置类,可以参考下面的例子:

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

注:

  1. 可以在该文件中使用 # 添加注释;
  2. Auto-configurations 必须且只能使用这种方法载入。我们需要确保这些 Auto-configurations 在特定的包(package)中定义了,并且他们绝对不会是组件扫描(component scanning)的目标。此外,auto-configuration 类不应该启用组件扫描来发现额外的组件,相反,应该使用特定的 @Imports 注解。

如果配置需要按照特定顺序被加载,你可以使用注解 @AutoConfigureAfter@AutoConfigureBefore。例如,如果你提供了特定的 web 配置,你的类可能需要在 WebMvcAutoConfiguration 之后被加载。

如果你使用了注解 @AutoConfiguration,则可以使用属性 before, beforeName, after and afterName 作为专用的注解的相应属性的别名。我这么说大家有点懵,没关系,只要一看源码就懂了:

package org.springframework.boot.autoconfigure;
// 省略 import 代码
// 省略 注释
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {
	/**
	 * Explicitly specify the name of the Spring bean definition associated with the
	 * {@code @AutoConfiguration} class. If left unspecified (the common case), a bean
	 * name will be automatically generated.
	 * <p>
	 * The custom name applies only if the {@code @AutoConfiguration} class is picked up
	 * via component scanning or supplied directly to an
	 * {@link AnnotationConfigApplicationContext}. If the {@code @AutoConfiguration} class
	 * is registered as a traditional XML bean definition, the name/id of the bean element
	 * will take precedence.
	 * @return the explicit component name, if any (or empty String otherwise)
	 * @see AnnotationBeanNameGenerator
	 */
	@AliasFor(annotation = Configuration.class)
	String value() default "";

	/**
	 * The auto-configure classes that should have not yet been applied.
	 * @return the classes
	 */
	@AliasFor(annotation = AutoConfigureBefore.class, attribute = "value")
	Class<?>[] before() default {};

	/**
	 * The names of the auto-configure classes that should have not yet been applied.
	 * @return the class names
	 */
	@AliasFor(annotation = AutoConfigureBefore.class, attribute = "name")
	String[] beforeName() default {};

	/**
	 * The auto-configure classes that should have already been applied.
	 * @return the classes
	 */
	@AliasFor(annotation = AutoConfigureAfter.class, attribute = "value")
	Class<?>[] after() default {};

	/**
	 * The names of the auto-configure classes that should have already been applied.
	 * @return the class names
	 */
	@AliasFor(annotation = AutoConfigureAfter.class, attribute = "name")
	String[] afterName() default {};
}

如果你想排序某些相互间不应该有直接了解的 auto-configurations ,你也能使用 @AutoConfigureOrder。该注解和 常规注解 @Order 有同样的语义,但是为 auto-configuration 类提供了一个专门的顺序。

使用标准注解 @Configuration 的类,auto-configuration 类被加载的顺序,仅仅影响了 auto-configuration 类中的 beans 被定义的顺序。随后,那些 beans 被创建的顺序,却不会被影响,而是由每个 bean 的依赖关系 和 任何 @DependsOn 关系所决定。

注:
Auto-configuration 中,每个返回 bean 的方法上,都可以使用注解 @DependsOn,该注解可以设置在生成该返回的 bean 之前,需要先生成另一个 bean。举例如下:

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfiguration implements InitializingBean, DisposableBean {
    ...
    @Bean
    @DependsOn({"redisProvider"})
    public RedisCacheAspect redisCacheAspect(RedisProvider redisProvider) {
        return new RedisCacheAspect(redisProvider);
    }
    ...
}

上述 auto-configuration 代码中,redisCacheAspect() 方法返回一个 bean,默认该bean的名称是 redisCacheAspect,该方法上面的注解 @DependsOn({"redisProvider"}),表明在生成该 bean 之前,先要生成 另一个 bean redisProvider。这就是上面说的 beans 间的“ @DependsOn 关系”。

3.2.4 条件注解

你几乎总是想在你的 auto-configuration 类中包含1个或多个注解 @Conditional。注解 @ConditionalOnMissingBean 是一个普遍的用于允许开发者覆盖 auto-configuration (如果开发者不想用默认配置的话)的例子。

Spring Boot 包含了一些在你自己的代码中可复用的 @Conditional 注解,使用方法是给带 @Configuration 注解的类 或 单独的带 @Bean 注解的方法 添加该类注解。这些注解包括:

  • Class Conditions
  • Bean Conditions
  • Property Conditions
  • Resource Conditions
  • Web Application Conditions
  • SpEL Expression Conditions
3.2.4.1 Class 条件

注解 @ConditionalOnClass@ConditionalOnMissingClass 基于特定类的存在或不存在,让 @Configuration 类 被加载。由于注解元数据使用 ASM 解析,你可以使用 value 属性引用真正的类,即使实际上类可能在运行的应用程序中没有出现。你也能使用 name 属性,如果你偏向于使用 字符串值 指定类名。

该机制并不以同样的方法应用在 @Bean 方法上(该类方法的返回类型通常是条件的目标):在方法上的条件应用之前,JVM 已经载入了类和潜在处理的方法引用,而如果类不存在,该方法引用将会失败。

为了处理这类场景,我们可以使用一个单独的 @Configuration 类来隔离条件,如下例子所示:

@AutoConfiguration
// Some conditions ...
public class MyAutoConfiguration {
    // Auto-configured beans ...
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService.class)
    public static class SomeServiceConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public SomeService someService() {
            return new SomeService();
        }
    }
}

注:
如果你使用 @ConditionalOnClass@ConditionalOnMissingClass 作为一个元注解的一部分,来组合你自己的组合注解,则必须使用 name 属性,因为在这种情况下,引用类是不会被处理的。

3.2.4.2 Bean 条件

注解 @ConditionalOnBean@ConditionalOnMissingBean 基于特定 beans 的存在或不存在,使一个 bean 被包含。你能使用 value 属性,通过类型来指定 beans,或使用 name 属性,通过名称来指定 beans。search 属性则在搜索 beans 时,限制了需要考虑的 ApplicationContext 层级。

当这两个注解放置在一个 @Bean 方法上时,目标类型默认是方法返回值的类型,如下例子所示:

@AutoConfiguration
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new SomeService();
    }
}

在先前的例子中,如果 ApplicationContext 中没有 SomeService 类型的 bean的话,someService bean 将被创建。

注:

  1. 你需要非常小心 bean 定义添加到顺序,因为这些条件是基于“到目前为止已经处理了什么内容(即已经创建加载了哪些 bean)”来评估的。据此,我们推荐在 auto-configuration 类中仅使用注解 @ConditionalOnBean@ConditionalOnMissingBean (因为这两个注解保证了在任何用户定义的 bean 定义被添加之后,再载入他们注解的 bean)。

  2. @ConditionalOnBean@ConditionalOnMissingBean 并不阻止创建 @Configuration 类。“在类级别上使用这些条件”和“用该注解标记每个内部的 @Bean 方法(即,在方法级别上使用这些条件)”,两者唯一的区别是,如果条件不匹配,前者将阻止 @Configuration 类注册为一个 bean。

  3. 当声明一个 @Bean 方法时,在方法返回类型中提供尽可能多的类型信息。例如,你的 bean 的具体类实现了一个接口,则该 bean 方法的返回类型应该是该具体类,而非接口。当使用 bean 条件评估只能依赖方法签名中可用的类型信息时,在 @Bean 方法中提供尽可能多的类型信息尤其重要。

3.2.4.3 Property 条件

The @ConditionalOnProperty annotation lets configuration be included based on a Spring Environment property. Use the prefix and name attributes to specify the property that should be checked. By default, any property that exists and is not equal to false is matched. You can also create more advanced checks by using the havingValue and matchIfMissing attributes.
注解 @ConditionalOnProperty 基于 Spring Environment property 来评估是否包含某个配置(configuration)。使用属性 prefixname 来指定应该被检查的 property。默认情况下,匹配任何现有的不为 false 的 property。你也能使用属性 havingValuematchIfMissing 创建更多的高级检查。

3.2.4.4 Resource 条件

注解 @ConditionalOnResource 仅仅在一个特定的资源(Resource)存在时,才包含配置(configuration)。可以使用通常的 Spring 惯例指定资源,举例如下:file:/home/user/test.dat

3.2.4.5 Web Application 条件

The @ConditionalOnWarDeployment annotation lets configuration be included depending on whether the application is a traditional WAR application that is deployed to a container. This condition will not match for applications that are run with an embedded server.
注解 @ConditionalOnWebApplication@ConditionalOnNotWebApplication 依据应用是否是一个 “web 应用”来决定是否包含配置(configuration)。一个基于 servlet 的 web 应用,是任何使用了 Spring WebApplicationContext,定义了一个 session scope,或者有一个 ConfigurableWebEnvironment 的应用。一个 reactive web 应用是任何使用 ReactiveWebApplicationContext,或者有一个 ConfigurableReactiveWebEnvironment 的应用。

3.2.4.6 SpEL Expression 条件

注解 @ConditionalOnExpression 基于 SpEL 表达式的结果来决定是否包含配置(configuration)。

注:
引用一个在表达式中的 bean,将导致该 bean 在上下文刷新处理中很早就被初始化。结果是,在 后处理(post-processing)(如,配置属性绑定)时,bean 不符合条件,且其状态可能为“未完成”。

3.2.5 测试 Auto-configuration

一个 auto-configuration 可以被许多因素影响:用户配置(@Bean 定义和环境的个性化定制),条件评估(一个特定库的存在),还有其他种种。具体来说,每个测试应该创建一个定义良好的 ApplicationContext,来代表那些个性化定制的联合体。ApplicationContextRunner 提供了一个很好的方式来实现该目标。

ApplicationContextRunner 经常作为一个测试类的字段来定义,用于获取基础、通用的配置。以下例子保证了 MyServiceAutoConfiguration 总是被调用:

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
        .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));

注:
如果多个 auto-configurations 需要被定义,没有必要对他们的声明进行排序,因为他们严格按照“和运行应用时相同的顺序”来被调用。

每个测试都能使用 runner 来代表一个特定的用例。例如,以下例子调用了一个用户配置(UserConfiguration),并且检查了 auto-configuration 是否适当地回退了。调用 run 方法,提供了一个可以结合 AssertJ 来使用的回调上下文。

@Test
void defaultServiceBacksOff() {
    this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
    });
}

@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
    @Bean
    MyService myCustomService() {
        return new MyService("mine");
    }
}

可以轻松定制 Environment,如下例子所示:

@Test
void serviceNameCanBeConfigured() {
    this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context.getBean(MyService.class).getName()).isEqualTo("test123");
    });
}

runner 也能用于显示 ConditionEvaluationReport。能在 INFODEBUG 级别打印该 report。以下例子展示了在 auto-configuration 测试中,如何使用 ConditionEvaluationReportLoggingListener 来打印报告:

class MyConditionEvaluationReportingTests {
    @Test
    void autoConfigTest() {
        new ApplicationContextRunner()
            .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
            .run((context) -> {
                    // Test something...
            });
    }
}
3.2.5.1 模拟一个 Web Context

如果你需要测试一个仅在 servletreactive web 应用上下文中工作的 auto-configuration,请分别使用 WebApplicationContextRunnerReactiveWebApplicationContextRunner

3.2.5.2 覆盖 Classpath

我们还可以测试当一个特定 类 和/或 包 在运行时不存在时,会发生什么。Spring Boot 附带了一个 FilteredClassLoader,runner 很容易就能使用它。在下面的例子里,我们断言:如果 myService bean 不存在,则 auto-configuration 被恰当地禁用:

@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
            .run((context) -> assertThat(context).doesNotHaveBean("myService"));
}

— 下面我们回到创建自定义 starter 的内容 —


3.3 命名(Naming)

你必须确保为你的 starter 提供一个合适的命名空间。不要使用“spring-boot”开头来命名你的模块名称,即使你使用了一个不同的 Maven groupId。在未来,我们可能为你自动配置的内容提供官方支持。

根据经验,你应该在 starter 之后命名一个组合模块。例如,假设你正在为“acme”创建一个 starter,并且你命名该自动配置模块为 acme-spring-boot,以及该 starter 为 acme-spring-boot-starter。如果你只有一个模块(该模块将两者结合起来),请将其命名为 acme-spring-boot-starter

3.4 配置键(Configuration keys)

如果你的 starter 提供了配置键,那么给他们使用一个唯一的命名空间。尤其是不要将你的键包含在 Spring Boot 使用的命名空间中(如,server, management, spring, ...)。如果你使用相同的命名空间,在未来,我们可能以破坏你的模块的方式修改这些命名空间。根据经验,使用一个命名空间来作为你所有键的前缀(如 acme)。

请确保配置键通过“为每个属性添加字段的 javadoc 注释”被记录下来,如下例子所示:

import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

    /**
     * Whether to check the location of acme resources.
     */
    private boolean checkLocation = true;

    /**
     * Timeout for establishing a connection to the acme server.
     */
    private Duration loginTimeout = Duration.ofSeconds(3);

    public boolean isCheckLocation() {
        return this.checkLocation;
    }

    public void setCheckLocation(boolean checkLocation) {
        this.checkLocation = checkLocation;
    }

    public Duration getLoginTimeout() {
        return this.loginTimeout;
    }

    public void setLoginTimeout(Duration loginTimeout) {
        this.loginTimeout = loginTimeout;
    }
}

注:
你应该仅使用纯文本编写 @ConfigurationProperties 字段的 Javadoc 注释,因为他们在被添加到 JSON 中之前,没有被处理。

下面是一些我们内部遵循的规则,来确保描述是一致的:

  • 描述不要以 “The”或“A”开头
  • 对于布尔类型,描述以“Whether”或“Enable”开头
  • 对于基于集合的类型,描述以“逗号分隔的列表”开头
  • 使用 java.time.Duration 而非 long,并且如果默认单位不是“毫秒(milliseconds)”,则描述默认单位,如“如果一个 duration 后缀未指定,则使用 seconds”
  • 描述中不要提供默认值,除非它必须在运行时被决定
  • 确保 触发元数据的生成,以便你的键也可以使用 IDE 帮助。你可能想要回顾生成的元数据(META-INF/spring-configuration-metadata.json),以确保你的键被恰当地记录。在一个兼容的 IDE 中使用你自己的 starter,也是校验元数据质量的好主意。

3.5 “autoconfigure”模块

autoconfigure 模块包含“和库一起启动的必需的”所有东西。它可能也包含了配置键定义(configuration key definitions)(如 @ConfigurationProperties),和任何可被用于进一步定制组件如何初始化的回调接口。

注:
你应该标记该库的依赖为 optional,以便能够更容易地在你的项目包含 autoconfigure 模块。如果这么做的话,该库不会被提供,且默认 Spring Boot 会回退。

Spring Boot 使用注解处理器来收集 metadata 文件(META-INF/spring-autoconfigure-metadata.properties)中的 auto-configurations 中的条件。如果那个文件存在,则它被用于即时过滤不匹配的 auto-configurations,这将改善启动时间。推荐在模块中添加以下包含 auto-configurations 的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure-processor</artifactId>
    <optional>true</optional>
</dependency>

如果你已经在你的应用中直接定义了auto-configurations,请确保配置 spring-boot-maven-plugin 以防止 repackage goal 添加依赖到 fat jar 中:

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-autoconfigure-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Gradle 4.5和以下版本,应该在 compileOnly 配置中声明依赖,如下所示:

dependencies {
    compileOnly "org.springframework.boot:spring-boot-autoconfigure-processor"
}

Gradle 4.6和以上版本,应该在 annotationProcessor 配置中声明依赖,如下所示:

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}

3.6 Starter 模块

Starter 实际上是一个空的 jar。它唯一的目的是提供必要的依赖,以便和库一起工作。你可以把它当成启动所必需的一个固定的视图(opinionated view)。

不要对添加你的 starter 的工程做出假设。如果你正在自动配置的库通常需要其他 starters,那么也要包含他们。如果可选依赖的数量很多,提供适当的一组默认依赖可能比较困难,因为你应该避免包含对于库的一个典型用法非必须的依赖。换句话说,你不应该包含可选依赖。

注:
不管怎样,你的 starter 必须引用核心 Spring Boot starter(spring-boot-starter),直接或间接地(如果你的 starter 依赖另一个 starter,则没必要添加 spring-boot-starter)。如果一个项目创建时仅使用了你自定义的 starter,则 Spring Boot 的核心特性将通过核心 starter 的存在而被执行。

3.7 一个简单的自定义 Starter 例子

参考:Starter 案例
主要是参考其中的2个模块:config-spring-boot-starterconfig-spring-boot-starter-test

小结: 一个 starter 模块/组件 的代码,主要由5部分组成:

  1. resources/META-INF/spring.factories 文件(必需),设置启用的 AutoConfiguration 类列表;

  2. auto-configuration 配置类(必需),有如下功能:

    1. @EnableConfigurationProperties(Xxx.class):设置启用的 Properties 属性类,该属性类中的属性值将被加载到上下文中(如果客户没有自定义属性,则使用 Properties 属性类中的默认值);
    2. 可以构建多个 @Bean 方法,该方法的返回值将被加载到上下文中;
  3. Properties 属性类(必需),存放各种自定义的属性值,还可以配置默认值,另外,使用 @ConfigurationProperties(prefix = "spring.redis") 配置属性名的前缀;

  4. 必要的依赖;

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    
        <!-- 将被 @ConfigurationProperties 注解的类的属性注入到元数据 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure-processor</artifactId>
            <optional>true</optional>
        </dependency>
        
        <build>
          <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <!-- 排除 spring-boot-autoconfigure-processor 依赖 -->
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-autoconfigure-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  5. 其他逻辑的代码,如实体类、Service类等(非必需)。

3.8 单元测试

以下是上面参考的 Starter 案例 的一个单元测试:
注意:starter 是没有 启动类 Application.java 的,所以要以下面的方式测试:

package com.ian.test;

import com.ian.config.ConfigAutoConfiguration;
import com.ian.entity.ConfigInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * starter 测试类
 *   JUnit 测试注意:
 *      1. 类修饰符需要为 public
 *      2. 方法修饰符中不能有 static
 *      3. 测试方法无返回值
 *
 * @author Witt
 * @version 1.0.0
 * @date 2022/7/22
 */
@RunWith(SpringRunner.class)
@ImportAutoConfiguration({ConfigAutoConfiguration.class})
/*
@PropertySource(value = {"classpath:application.yml"})
注:
1. 这里可以用注解 @PropertySource 代替 @TestPropertySource
2. 但是一定要注意:其value属性 classpath:application.yml,可以被正常读取,但无法被加载到上下文中!
    目前,只能解析 application.properties。
    之前遇到过坑,因为这个原因无法使用自定义的 redis 的 starter。
 */
@TestPropertySource("classpath:application.properties")
public class StarterTest {
    @Autowired
    private ConfigInfo configInfo;

    @Test
    public void test1() {
        System.out.println(configInfo);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值