下一篇[未完待续]
英文原文:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-developing-auto-configuration.html
GitHub:https://github.com/jijicai/Spring/tree/master/spring-boot
49.4、测试你的自动配置
自动配置可能受到许多因素的影响:用户配置(@Bean 定义和 Environment 自定义)、条件评估(特定库的存在)和其他因素。具体来说,每个测试都应该创建一个定义良好的 ApplicationContext,它表示这些定制的组合。ApplicationContextRunner 提供了一个实现这一点的好方法。
ApplicationContextRunner 通常定义为测试类的一个字段,用于收集基本的公共配置。以下示例确保始终调用 UserServiceAutoConfiguration:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(UserServiceAutoConfiguration.class));
提示:如果必须定义多个自动配置,则无需对其声明进行排序,因为它们的调用顺序与运行应用程序时完全相同。
每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置(UserConfiguration)并检查自动配置是否正确后退。调用 run 提供了一个可与 Assert4J 一起使用的回调上下文。
@Test
public void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class))
.isSameAs(context.getBean(UserConfiguration.class).myUserService());
});
}
@Configuration
static class UserConfiguration {
@Bean
public UserService myUserService() {
return new UserService(\"mine\");
}
}
也可以轻松地自定义 Environment,如下面示例所示:
@Test
public void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues(\"user.name=test123\").run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class).getName()).isEqualTo(\"test123\");
});
}
该运行器还可用于显示 ConditionEvaluationReport。该报告可以在 INFO 或 DEBUG 级别打印。下面的示例演示如何使用 ConditionEvaluationReportLoggingListener 在自动配置测试中打印报表。
@Test
public void autoConfigTest {
ConditionEvaluationReportLoggingListener initializer = new ConditionEvaluationReportLoggingListener(
LogLevel.INFO);
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(initializer).run((context) -> {
// Do something...
});
}
49.4.1、模拟 Web 上下文
如果需要测试仅在 Servlet 或 反应式 web 应用程序上下文中运行的自动配置,请分别使用 WebApplicationContextRunner 或 ReactiveWebApplicationContextRunner。
49.4.2、覆盖 Classpath
也可以测试当特定类和/或包在运行时不存在时会发生什么。Spring Boot 附带一个 FilteredClassLoader,可以很容易地被运行器使用。在以下示例中,我们断言,如果不存在 UserService,则会正确禁用自动配置:
@Test
public void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(UserService.class))
.run((context) -> assertThat(context).doesNotHaveBean(\"userService\"));
}
49.5、创建自己的 Starter
库的完整 Spring Boot starter 可能包含以下组件:
(1)包含自动配置代码的 autoconfigure 模块。
(2)提供对 autoconfigure 模块、库和任何其他通常有用的依赖项的依赖的 starter 模块。简而言之,添加 starter 应该提供开始使用该库所需的一切。
提示:如果不需要将自动配置代码和依赖项管理分离开来,那么可以将它们合并到一个模块中。
49.5.1、命名
你应该确保为 starter 提供正确的名称空间。不要用 spring-boot 启动模块名,即使使用不同的 Maven groupId。我们未来可能会为你自动配置的东西提供官方支持。
根据经验,你应该在 starter 后面命名一个组合模块。例如, 假设您正在为 “acme” 创建 starter,并且你将自动配置模块命名为 acme-spring-boot-autoconfigure 和 starter acme-spring-boot-starter。如果你只有一个将两者结合在一起的模块,请将其命名为 acme-spring-boot-starter。
此外,如果你的 starter 提供配置键,请为它们使用唯一的命名空间。特别是,不要在 Spring Boot 使用的命名空间 (如 server、management、 spring 等) 中包含你的键。如果你使用相同的命名空间,我们可能会在将来以破坏你的模块的方式修改这些命名空间。
确保触发元数据生成,以便你的键也可以使用 IDE 帮助。你可能要查看生成元数据 (meta-inf/spring-configuration-metadata.json) ,以确保键正确记录。(https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/configuration-metadata.html#configuration-metadata-annotation-processor )
49.5.2、autoconfigure 模块
autoconfigure 模块包含开始使用库所需的一切。它还可能包含配置键定义(如 @ConfigurationProperties)和任何回调接口,这些接口可用于进一步自定义组件的初始化方式。
提示:你应该将库的依赖项标记为可选,以便可以更轻松地在项目中包含 autoconfigure 模块。如果这样做,则默认情况下不提供库,并且 Spring Boot 会退出。
Spring Boot 使用注解处理器采集元数据文件(META-INF/spring-autoconfigure-metadata.properties)中自动配置的条件。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将提高启动时间。建议在包含自动配置的模块中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
对于 Gradle 4.5 和更早版本,应该在 compileOnly 配置中声明依赖项,如下面示例所示:
dependencies {
compileOnly \"org.springframework.boot:spring-boot-autoconfigure-processor\"
}
对于 Gradle4.6 及更高版本,应该在 annotationProcessor 配置中声明依赖项,如下面示例所示:
dependencies {
annotationProcessor \"org.springframework.boot:spring-boot-autoconfigure-processor\"
}
49.5.3、Starter 模块
starter 实际上是一个空 jar。它的唯一目的是提供使用库所必要的依赖项。你可以把它看作是开始需要做的事情的一种固执己见的观点。
不要对添加了 starter 的项目做出假设。如果你正在自动配置的库通常需要其他 starter,请也提及它们。如果可选依赖项的数量很高,提供一组合适的默认依赖项可能会很困难,因为你应该避免包含对库的典型使用不必要的依赖项。换句话说,你不应该包括可选的依赖项。
注释:无论哪种方式,starter 都必须直接或间接地引用核心 Spring Boot starter(spring-boot-starter)(即,如果 starter 依赖于另一个 starter,则无需添加)。如果一个项目只使用你的自定义 starter 创建,那么 Spring Boot 的核心功能将受到核心 starter 的支持。
下一篇[未完待续]