Spring Boot核心功能(三)_mvc

8.3.21自动配置的数据 JDBC 测试

​@DataJdbcTest​​类似于​​@JdbcTest​​但适用于使用 Spring Data JDBC 存储库的测试。默认情况下,它配置内存中的嵌入式数据库、a​​JdbcTemplate​​和 Spring Data JDBC 存储库。使用注解时只​​AbstractJdbcConfiguration​​扫描子类​​@DataJdbcTest​​,不扫描常规​​@Component​​和​​@ConfigurationProperties​​bean。 ​​@EnableConfigurationProperties​​可用于包含​​@ConfigurationProperties​​bean。

默认情况下,Data JDBC 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分​。如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC 示例中所示。

如果您希望您的测试针对真实数据库运行,您可以使用与​​@AutoConfigureTestDatabase​​for 相同的方式使用注解​​DataJpaTest​​。(请参阅“自动配置的数据 JPA 测试”。)

8.3.22自动配置的 jOOQ 测试

您可以以与jOOQ 相关测试​​@JooqTest​​​类似的方式使用。​​@JdbcTest​​​由于 jOOQ 严重依赖与数据库模式相对应的基于 Java 的模式,因此​​DataSource​​​使用现有模式。如果您想用内存数据库替换它,您可以使用​​@AutoConfigureTestDatabase​​​覆盖这些设置。(有关在 Spring Boot 中使用 jOOQ 的更多信息,请参阅“ data.html ”。)使用注解 时不会扫描常规​​@Component​​​和bean。可用于包含bean。​​@ConfigurationProperties​​​​@JooqTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

​@JooqTest​​​配置一个​​DSLContext​​​. 以下示例显示了​​@JooqTest​​正在使用的注释:

@JooqTest
class MyJooqTests {

@Autowired
private DSLContext dslContext;

// ...

}

JOOQ 测试是事务性的,默认情况下会在每个测试结束时回滚。如果这不是您想要的,您可以禁用测试或整个测试类的事务管理,如JDBC 示例中所示。

8.3.23自动配置数据 MongoDB 测试

您可以使用它​​@DataMongoTest​​来测试 MongoDB 应用程序。默认情况下,它配置一个内存中的嵌入式 MongoDB(如果可用),配置一个​​MongoTemplate​​,扫描​​@Document​​类,并配置 Spring Data MongoDB 存储库。使用注解 时不会扫描常规​​@Component​​和bean。可用于包含bean。(有关在 Spring Boot 中使用 MongoDB 的更多信息,请参阅“ data.html ”。)​​@ConfigurationProperties​​​​@DataMongoTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

下面的类显示了​​@DataMongoTest​​正在使用的注解:

@DataMongoTest
class MyDataMongoDbTests {

@Autowired
private MongoTemplate mongoTemplate;

// ...

}

内存中嵌入式 MongoDB 通常适用于测试,因为它速度快且不需要任何开发人员安装。但是,如果您更喜欢针对真正的 MongoDB 服务器运行测试,则应排除嵌入式 MongoDB 自动配置,如以下示例所示:

@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class MyDataMongoDbTests {

// ...

}

8.3.24自动配置的数据 Neo4j 测试

您可以​​@DataNeo4jTest​​​用来测试 Neo4j 应用程序。默认情况下,它会扫描​​@Node​​​类并配置 Spring Data Neo4j 存储库。使用注解 时不会扫描常规​​@Component​​​和bean。可用于包含bean。(有关在 Spring Boot 中使用 Neo4J 的更多信息,请参阅“ data.html ”。)​​@ConfigurationProperties​​​​@DataNeo4jTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

以下示例显示了在 Spring Boot 中使用 Neo4J 测试的典型设置:

@DataNeo4jTest
class MyDataNeo4jTests {

@Autowired
private SomeRepository repository;

// ...

}

默认情况下,Data Neo4j 测试是事务性的,并在每次测试结束时回滚。有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class MyDataNeo4jTests {

}

8.3.25自动配置的数据 Redis 测试

您可以​​@DataRedisTest​​​用来测试 Redis 应用程序。默认情况下,它会扫描​​@RedisHash​​​类并配置 Spring Data Redis 存储库。使用注解 时不会扫描常规​​@Component​​​和bean。可用于包含bean。(有关在 Spring Boot 中使用 Redis 的更多信息,请参阅“ data.html ”。)​​@ConfigurationProperties​​​​@DataRedisTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

以下示例显示了​​@DataRedisTest​​正在使用的注释:

@DataRedisTest
class MyDataRedisTests {

@Autowired
private SomeRepository repository;

// ...

}

8.3.26自动配置的数据 LDAP 测试

您可以使用它​​@DataLdapTest​​​来测试 LDAP 应用程序。默认情况下,它配置内存中的嵌入式 LDAP(如果可用)、配置​​LdapTemplate​​​、扫描​​@Entry​​​类并配置 Spring Data LDAP 存储库。使用注解 时不会扫描常规​​@Component​​​和bean。可用于包含bean。(有关在 Spring Boot 中使用 LDAP 的更多信息,请参阅“ data.html ”。)​​@ConfigurationProperties​​​​@DataLdapTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

以下示例显示了​​@DataLdapTest​​正在使用的注释:

@DataLdapTest
class MyDataLdapTests {

@Autowired
private LdapTemplate ldapTemplate;

// ...

}

内存中嵌入式 LDAP 通常适用于测试,因为它速度快且不需要任何开发人员安装。但是,如果您更喜欢针对真实的 LDAP 服务器运行测试,则应排除嵌入式 LDAP 自动配置,如以下示例所示:

@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class MyDataLdapTests {

// ...

}

8.3.27自动配置的 REST 客户端

您可以使用​​@RestClientTest​​​注释来测试 REST 客户端。默认情况下,它会自动配置 Jackson、GSON 和 Jsonb 支持,配置 a​​RestTemplateBuilder​​​并添加对​​MockRestServiceServer​​​. 使用注解 时不会扫描常规​​@Component​​​和bean。可用于包含bean。​​@ConfigurationProperties​​​​@RestClientTest​​​​@EnableConfigurationProperties​​​​@ConfigurationProperties​

您要测试的特定 bean 应使用 的​​value​​​or​​components​​​属性指定​​@RestClientTest​​,如以下示例所示:

@RestClientTest(RemoteVehicleDetailsService.class)
class MyRestClientTests {

@Autowired
private RemoteVehicleDetailsService service;

@Autowired
private MockRestServiceServer server;

@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() {
this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}

}

8.3.28自动配置的 Spring REST 文档测试

您可以使用​​@AutoConfigureRestDocs​​注解在您的测试中使用Spring REST Docs与 Mock MVC、REST Assured 或 WebTestClient。它消除了 Spring REST Docs 中对 JUnit 扩展的需求。

​@AutoConfigureRestDocs​​可用于覆盖默认输出目录(​​target/generated-snippets​​如果您使用 Maven 或​​build/generated-snippets​​使用 Gradle)。它还可用于配置出现在任何记录的 URI 中的主机、方案和端口。

使用 Mock MVC 自动配置 Spring REST 文档测试

​@AutoConfigureRestDocs​​在测试基于 servlet 的 Web 应用程序时自定义​​MockMvc​​bean 以使用 Spring REST Docs。您可以​​@Autowired​​像使用 Mock MVC 和 Spring REST Docs 时一样在测试中使用和使用它,如以下示例所示:

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

@Autowired
private MockMvc mvc;

@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}

}

如果您需要对 Spring REST Docs 配置的控制比 的属性提供的更多​​@AutoConfigureRestDocs​​,则可以使用​​RestDocsMockMvcConfigurationCustomizer​​bean,如以下示例所示:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsMockMvcConfigurationCustomizer {

@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}

}

如果你想利用 Spring REST Docs 对参数化输出目录的支持,你可以创建一个​​RestDocumentationResultHandler​​bean。​​alwaysDo​​使用此结果处理程序进行自动配置调用,从而导致每个​​MockMvc​​调用自动生成默认片段。下面的例子显示了一个​​RestDocumentationResultHandler​​被定义的:

@TestConfiguration(proxyBeanMethods = false)
public class MyResultHandlerConfiguration {

@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}

}
使用 WebTestClient 自动配置 Spring REST 文档测试

​@AutoConfigureRestDocs​​也可以​​WebTestClient​​在测试响应式 Web 应用程序时使用。您可以​​@Autowired​​像使用 Spring REST Docs 时一样在测试中使用和使用它​​@WebFluxTest​​,如以下示例所示:

@WebFluxTest
@AutoConfigureRestDocs
class MyUsersDocumentationTests {

@Autowired
private WebTestClient webTestClient;

@Test
void listUsers() {
this.webTestClient
.get().uri("/")
.exchange()
.expectStatus()
.isOk()
.expectBody()
.consumeWith(document("list-users"));
}

}

如果您需要对 Spring REST Docs 配置的控制比 的属性提供的更多​​@AutoConfigureRestDocs​​,则可以使用​​RestDocsWebTestClientConfigurationCustomizer​​bean,如以下示例所示:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer {

@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}

}

如果您想利用 Spring REST Docs 对参数化输出目录的支持,您可以使用 a​​WebTestClientBuilderCustomizer​​为每个实体交换结果配置消费者。以下示例显示了这样的​​WebTestClientBuilderCustomizer​​定义:

@TestConfiguration(proxyBeanMethods = false)
public class MyWebTestClientBuilderCustomizerConfiguration {

@Bean
public WebTestClientBuilderCustomizer restDocumentation() {
return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}"));
}

}
带有 REST 保证的自动配置 Spring REST 文档测试

​@AutoConfigureRestDocs​​使​​RequestSpecification​​预配置为使用 Spring REST Docs 的 bean 可用于您的测试。您可以​​@Autowired​​像使用 REST Assured 和 Spring REST Docs 时一样在测试中使用和使用它,如以下示例所示:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class MyUserDocumentationTests {

@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec)
.filter(document("list-users"))
.when()
.port(port)
.get("/")
.then().assertThat()
.statusCode(is(200));
}

}

如果您需要对 Spring REST Docs 配置的控制比 的属性提供的更多​​@AutoConfigureRestDocs​​,​​RestDocsRestAssuredConfigurationCustomizer​​则可以使用 bean,如以下示例所示:

@TestConfiguration(proxyBeanMethods = false)
public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer {

@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}

}

8.3.29自动配置的 Spring Web 服务测试

自动配置的 Spring Web 服务客户端测试

您可以使用​​@WebServiceClientTest​​Spring Web Services 项目来测试调用 Web 服务的应用程序。默认情况下,它配置一个模拟​​WebServiceServer​​bean 并自动自定义您的​​WebServiceTemplateBuilder​​. (有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅“ io.html ”。)

以下示例显示了​​@WebServiceClientTest​​正在使用的注释:

@WebServiceClientTest(SomeWebService.class)
class MyWebServiceClientTests {

@Autowired
private MockWebServiceServer server;

@Autowired
private SomeWebService someWebService;

@Test
void mockServerCall() {
this.server
.expect(payload(new StringSource("<request/>")))
.andRespond(withPayload(new StringSource("<response><status>200</status></response>")));
assertThat(this.someWebService.test())
.extracting(Response::getStatus)
.isEqualTo(200);
}

}
自动配置的 Spring Web 服务服务器测试

您可以使用​​@WebServiceServerTest​​​Spring Web Services 项目来测试实现 Web 服务的应用程序。默认情况下,它配置一个​​MockWebServiceClient​​可用于调用 Web 服务端点的 bean。(有关在 Spring Boot 中使用 Web 服务的更多信息,请参阅“ io.html ”。)

以下示例显示了​​@WebServiceServerTest​​正在使用的注释:

@WebServiceServerTest(ExampleEndpoint.class)
class MyWebServiceServerTests {

@Autowired
private MockWebServiceClient client;

@Test
void mockServerCall() {
this.client
.sendRequest(RequestCreators.withPayload(new StringSource("<ExampleRequest/>")))
.andExpect(ResponseMatchers.payload(new StringSource("<ExampleResponse>42</ExampleResponse>")));
}

}

8.3.30额外的自动配置和切片

每个切片提供一个或多个​​@AutoConfigure…​​注释,即定义应作为切片一部分包含的自动配置。​​@AutoConfigure…​​通过创建自定义注释或添加到测试中,可以逐个测试添加其他自动配置​​@ImportAutoConfiguration​​,如以下示例所示:

@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class MyJdbcTests {

}

或者,可以为切片注释的任何使用添加额外的自动配置,方法是将它们注册到存储的文件中,​​META-INF/spring​​如以下示例所示:

META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports

com.example.IntegrationAutoConfiguration

在此示例中,在​​com.example.IntegrationAutoConfiguration​​​使用 注释的每个测试上都启用了​​@JdbcTest​​。

8.3.31。用户配置和切片

如果您以合理的方式构建代码,则默认情况下​​@SpringBootApplication​​您的类将用作测试的配置。

然后,重要的是不要在应用程序的主类中乱扔特定于其功能特定区域的配置设置。

假设您使用的是 Spring Batch,并且您依赖于它的自动配置。您可以定义​​@SpringBootApplication​​如下:

@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {

// ...

}

因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启动 Spring Batch,这绝对不是你想要做的。推荐的方法是将特定于区域的配置移动到与您的应用程序处于同一级别的单独​​@Configuration​​类中,如以下示例所示:

@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class MyBatchConfiguration {

// ...

}

测试切片将​​@Configuration​​​类排除在扫描之外。例如,对于 a ​​@WebMvcTest​​​,以下配置不会​​WebMvcConfigurer​​在测试切片加载的应用程序上下文中包含给定的 bean:

@Configuration(proxyBeanMethods = false)
public class MyWebConfiguration {

@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
// ...
};
}

}

但是,下面的配置将导致自定义​​WebMvcConfigurer​​被测试切片加载。

@Component
public class MyWebMvcConfigurer implements WebMvcConfigurer {

// ...

}

另一个混淆来源是类路径扫描。假设,当您以合理的方式构建代码时,您需要扫描一个额外的包。您的应用程序可能类似于以下代码:

@SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

// ...

}

这样做有效地覆盖了默认的组件扫描指令,其副作用是扫描这两个包,而不管您选择的切片是什么。例如,a​​@DataJpaTest​​似乎突然扫描应用程序的组件和用户配置。同样,将自定义指令移动到单独的类是解决此问题的好方法。

8.3.32使用 Spock 测试 Spring Boot 应用程序

Spock 2.x 可用于测试 Spring Boot 应用程序。为此,请将 Spock​​spock-spring​​模块的依赖项添加到应用程序的构建中。 ​​spock-spring​​将 Spring 的测试框架集成到 Spock 中。有关详细信息,请参阅Spock 的 Spring 模块的文档。

8.4. 测试工具

测试应用程序时通常有用的一些测试实用程序类被打包为​​spring-boot​​.

8.4.1. ConfigDataApplicationContextInitializer

​ConfigDataApplicationContextInitializer​​是一个​​ApplicationContextInitializer​​你可以应用到你的测试来加载 Spring Boot​​application.properties​​文件的工具。当您不需要 提供的全套功能时,您可以使用它,​​@SpringBootTest​​如下例所示:

@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class)
class MyConfigFileTests {

// ...

}

8.4.2. 测试属性值

​TestPropertyValues​​让您快速将属性添加到 a​​ConfigurableEnvironment​​或​​ConfigurableApplicationContext​​. 您可以使用​​key=value​​字符串调用它,如下所示:

class MyEnvironmentTests {

@Test
void testPropertySources() {
MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(environment);
assertThat(environment.getProperty("name")).isEqualTo("Boot");
}

}

8.4.3. 输出捕获

​OutputCapture​​​​Extension​​是一个可用于捕获​​System.out​​和​​System.err​​输出的 JUnit 。要使用 add​​@ExtendWith(OutputCaptureExtension.class)​​和 injection​​CapturedOutput​​作为测试类构造函数或测试方法的参数,如下所示:

@ExtendWith(OutputCaptureExtension.class)
class MyOutputCaptureTests {

@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}

}

8.4.4. 测试休息模板

​TestRestTemplate​​是 Spring 的一种方便替代方案,​​RestTemplate​​在集成测试中很有用。您可以获得一个普通模板或发送基本 HTTP 身份验证(带有用户名和密码)的模板。在任何一种情况下,模板都是容错的。这意味着它以一种测试友好的方式运行,不会在 4xx 和 5xx 错误上抛出异常。相反,可以通过返回​​ResponseEntity​​的及其状态码检测此类错误。

建议使用 Apache HTTP 客户端(版本 4.3.2 或更高版本),但不是强制性的。如果您的类路径中有它,则​​TestRestTemplate​​通过适当地配置客户端来响应。如果您确实使用 Apache 的 HTTP 客户端,则会启用一些额外的测试友好功能:

  • 不遵循重定向(因此您可以断言响应位置)。
  • Cookie 被忽略(因此模板是无状态的)。

​TestRestTemplate​​可以在集成测试中直接实例化,如下例所示:

class MyTests {

private final TestRestTemplate template = new TestRestTemplate();

@Test
void testRequest() {
ResponseEntity<String> headers = this.template.getForEntity("https://myhost.example.com/example", String.class);
assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com");
}

}

或者,如果您将​​@SpringBootTest​​注释与​​WebEnvironment.RANDOM_PORT​​or一起使用​​WebEnvironment.DEFINED_PORT​​,您可以注入一个完全配置的​​TestRestTemplate​​并开始使用它。如有必要,可以通过​​RestTemplateBuilder​​bean 应用额外的定制。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如下例所示:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MySpringBootTests {

@Autowired
private TestRestTemplate template;

@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}

@TestConfiguration(proxyBeanMethods = false)
static class RestTemplateBuilderConfiguration {

@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}

}

}

9. 创建您自己的自动配置

如果您在一家开发共享库的公司工作,或者如果您在开源或商业库中工作,您可能想要开发自己的自动配置。自动配置类可以捆绑在外部 jar 中,并且仍然可以被 Spring Boot 拾取。

自动配置可以与提供自动配置代码以及您将使用的典型库的“启动器”相关联。我们首先介绍了构建您自己的自动配置所需了解的内容,然后我们继续介绍创建自定义启动器所需的典型步骤。

9.1了解自动配置的 Bean

实现自动配置的类用​​@AutoConfiguration​​. 这个注释本身是用 元注释的​​@Configuration​​,使自动配置成为标准​​@Configuration​​类。附加​​@Conditional​​注释用于限制何时应用自动配置。通常,自动配置类使用​​@ConditionalOnClass​​和​​@ConditionalOnMissingBean​​注释。这确保了自动配置仅在找到相关类并且您没有声明自己的类时适用​​@Configuration​​。

您可以浏览源代码spring-boot-autoconfigure以查看​​@AutoConfiguration​​Spring 提供的类(参见META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件)。

9.2. 定位自动配置候选

​META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports​​Spring Boot 检查发布的 jar中是否存在文件。该文件应列出您的配置类,每行一个类名,如以下示例所示:

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

如果您的配置需要按特定顺序应用,您可以使用注解上的​​before​​​、​​beforeName​​​和属性或专用的和​​after​​​注解。例如,如果您提供特定于 Web 的配置,您的类可能需要在.​​afterName​​@AutoConfiguration@AutoConfigureBefore@AutoConfigureAfter​​WebMvcAutoConfiguration​

如果您想订购某些彼此不应该有任何直接了解的自动配置,您也可以使用​​@AutoConfigureOrder​​. 该注释与常规注释具有相同的语义,​​@Order​​但为自动配置类提供了专用顺序。

与标准​​@Configuration​​类一样,应用自动配置类的顺序只影响定义它们的 bean 的顺序。随后创建这些 bean 的顺序不受影响,由每个 bean 的依赖关系和任何​​@DependsOn​​关系决定。

9.3. 条件注释

您几乎总是希望​​@Conditional​​在您的自动配置类中包含一个或多个注释。注解是一个常见的​​@ConditionalOnMissingBean​​例子,如果开发人员对你的默认设置不满意,它可以让他们覆盖自动配置。

Spring Boot 包含许多注释,您可以通过注释类或单个方法​​@Conditional​​在自己的代码中重用它们。这些注释包括:​​@Configuration​​​​@Bean​

  • 班级条件
  • 豆条件
  • 物业条件
  • 资源条件
  • 网络申条件
  • SpEL 表达条件

9.3.1班级条件

​@ConditionalOnClass​​和​​@ConditionalOnMissingClass​​注释允许根据​​@Configuration​​特定类的存在与否来包含类。由于注释元数据是使用ASM解析的,因此您可以使用该​​value​​属性来引用真实的类,即使该类实际上可能不会出现在正在运行的应用程序类路径中。​​name​​如果您更喜欢使用值指定类名,也可以使用该属性​​String​​。

此机制不适用于​​@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();
}

}

}

9.3.2. 豆条件

​@ConditionalOnBean​​和注释允许根据​​@ConditionalOnMissingBean​​特定 bean 的存在或不存在来包含 bean。您可以使用该​​value​​属性按类型​​name​​指定bean 或按名称指定bean。该​​search​​属性允许您限制​​ApplicationContext​​在搜索 bean 时应考虑的层次结构。

放置在​​@Bean​​方法上时,目标类型默认为方法的返回类型,如下例所示:

@AutoConfiguration
public class MyAutoConfiguration {

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

}

在前面的示例中,​​someService​​如果.​​SomeService​​​​ApplicationContext​

9.3.3. 物业条件

​@ConditionalOnProperty​​注释允许基于 Spring Environment 属性包含配置。使用​​prefix​​和​​name​​属性指定应检查的属性。默认情况下,​​false​​匹配任何存在但不等于的属性。您还可以使用​​havingValue​​和​​matchIfMissing​​属性创建更高级的检查。

9.3.4资源条件

​@ConditionalOnResource​​注释允许仅在存在特定资源时才包含配置。可以使用通常的 Spring 约定来指定资源,如下例所示​​file:/home/user/test.dat​​:

9.3.5网络申请条件

​@ConditionalOnWebApplication​​和注释允许根据​​@ConditionalOnNotWebApplication​​应用程序是否为“Web 应用程序”来包含配置。基于 servlet 的 Web 应用程序是任何使用 Spring ​​WebApplicationContext​​、定义​​session​​范围或具有​​ConfigurableWebEnvironment​​. 反应式 Web 应用程序是任何使用​​ReactiveWebApplicationContext​​或具有​​ConfigurableReactiveWebEnvironment​​.

注释允许根据​​@ConditionalOnWarDeployment​​应用程序是否是部署到容器的传统 WAR 应用程序来包含配置。对于使用嵌入式服务器运行的应用程序,此条件将不匹配。

9.3.6SpEL 表达条件

​@ConditionalOnExpression​​注释允许基于SpEL 表达式的结果包含配置。

9.4。测试您的自动配置

自动配置可能受到许多因素的影响:用户配置(​​@Bean​​定义和​​Environment​​定制)、条件评估(特定库的存在)等。具体来说,每个测试都应该创建一个定义良好​​ApplicationContext​​的,代表这些定制的组合。 ​​ApplicationContextRunner​​提供了实现这一目标的好方法。

​ApplicationContextRunner​​通常被定义为测试类的一个字段,用于收集基本的、通用的配置。以下示例确保​​MyServiceAutoConfiguration​​始终调用它:

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

每个测试都可以使用运行器来表示特定的用例。例如,下面的示例调用用户配置 ( ​​UserConfiguration​​​) 并检查自动配置是否正确退出。Invoking​​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");
});
}

跑步者也可用于显示​​ConditionEvaluationReport​​. 报告可以在​​INFO​​或​​DEBUG​​水平打印。以下示例显示了如何使用​​ConditionEvaluationReportLoggingListener​​打印自动配置测试中的报告。

class MyConditionEvaluationReportingTests {

@Test
void autoConfigTest() {
new ApplicationContextRunner()
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
.run((context) -> {
// Test something...
});
}

}

9.4.1模拟 Web 上下文

如果您需要测试仅在 servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用​​WebApplicationContextRunner​​或​​ReactiveWebApplicationContextRunner​​。

9.4.2. 覆盖类路径

还可以测试在运行时不存在特定类和/或包时会发生什么。Spring Boot 附带一个​​FilteredClassLoader​​runner 可以轻松使用的。在以下示例中,我们断言如果​​MyService​​不存在,则自动配置被正确禁用:

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

9.5创建自己的启动器

一个典型的 Spring Boot 启动器包含自动配置和自定义给定技术的基础设施的代码,我们称之为“acme”。为了使其易于扩展,可以将专用命名空间中的许多配置键暴露给环境。最后,提供了一个“starter”依赖项来帮助用户尽可能轻松地开始。

具体来说,自定义启动器可以包含以下内容:

  • ​autoconfigure​​包含“acme”的自动配置代码的模块。
  • 提供对模块starter的依赖关系的autoconfigure模块以及“acme”和通常有用的任何其他依赖关系。简而言之,添加启动器应该提供开始使用该库所需的一切。

两个模块中的这种分离绝不是必要的。如果“acme”有多种风格、选项或可选功能,那么最好将自动配置分开,因为您可以清楚地表达某些功能是可选的事实。此外,您还可以制作一个启动器来提供有关这些可选依赖项的意见。同时,其他人只能依靠​​autoconfigure​​模块,制作自己的不同意见的starter。

如果自动配置相对简单并且没有可选功能,那么在启动器中合并两个模块绝对是一种选择。

9.5.1。命名

您应该确保为您的启动器提供适当的命名空间。不要以 . 开头的模块名称​​spring-boot​​,即使您使用不同的 Maven ​​groupId​​。我们将来可能会为您自动配置的内容提供官方支持。

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

9.5.2. 配置键

如果您的启动器提供配置键,请为它们使用唯一的命名空间。特别是,不要将您的键包含在 Spring Boot 使用的命名空间中(例如​​server​​、​​management​​、​​spring​​等)。如果您使用相同的命名空间,我们将来可能会以破坏您的模块的方式修改这些命名空间。根据经验,在所有键前面加上您拥有的命名空间(例如​​acme​​)。

确保通过为每个属性添加字段 javadoc 来记录配置键,如以下示例所示:

@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);

// getters/setters ...

}

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

  • 不要以“The”或“A”开始描述。
  • 对于boolean类型,以“是否”或“启用”开始描述。
  • 对于基于集合的类型,以“逗号分隔列表”开始描述
  • 如果默认单位与毫秒不同,则使用java.time.Duration而不是long描述默认单位,例如“如果未指定持续时间后缀,则将使用秒”。
  • 除非必须在运行时确定,否则不要在描述中提供默认值。

确保触发元数据生成,以便您的密钥也可以使用 IDE 帮助。您可能需要查看生成的元数据 ( ​​META-INF/spring-configuration-metadata.json​​) 以确保正确记录您的密钥。在兼容的 IDE 中使用您自己的启动器也是验证元数据质量的好主意。

9.5.3“自动配置”模块

该​​autoconfigure​​模块包含开始使用该库所需的一切。它还可能包含配置键定义(例如​​@ConfigurationProperties​​)和任何回调接口,可用于进一步自定义组件的初始化方式。

Spring Boot 使用注解处理器来收集元数据文件 ( ​​META-INF/spring-autoconfigure-metadata.properties​​) 中的自动配置条件。如果该文件存在,它将用于急切地过滤不匹配的自动配置,这将缩短启动时间。

使用 Maven 构建时,建议在包含自动配置的模块中添加以下依赖项:

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

如果您在应用程序中直接定义了自动配置,请确保配置​​spring-boot-maven-plugin​​以防止​​repackage​​目标将依赖项添加到 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,应在​​annotationProcessor​​配置中声明依赖项,如以下示例所示:

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

9.5.4入门模块

启动器实际上是一个空罐子。它的唯一目的是提供必要的依赖项以使用该库。您可以将其视为对入门所需内容的固执己见。

不要对添加启动器的项目做出假设。如果您要自动配置的库通常需要其他启动器,请同时提及它们。如果可选依赖项的数量很高,则提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对于库的典型使用而言不必要的依赖项。换句话说,您不应该包含可选依赖项。

10. Kotlin 支持

Kotlin是一种针对 JVM(和其他平台)的静态类型语言,它允许编写简洁优雅的代码,同时提供与用 Java 编写的现有库的互操作性。

Spring Boot 通过利用其他 Spring 项目(如 Spring Framework、Spring Data 和 Reactor)中的支持来提供 Kotlin 支持。有关更多信息,请参阅Spring Framework Kotlin 支持文档。

开始使用 Spring Boot 和 Kotlin 的最简单方法是遵循这个综合教程。您可以使用start.spring.io创建新的 Kotlin 项目。如果您需要支持,请随时加入Kotlin Slack的#spring 频道,或者在Stack Overflow​​spring​​上使用和​​kotlin​​标签提出问题。

10.1要求

Spring Boot 至少需要 Kotlin 1.3.x,并通过依赖管理来管理合适的 Kotlin 版本。要使用 Kotlin,​​org.jetbrains.kotlin:kotlin-stdlib​​并且​​org.jetbrains.kotlin:kotlin-reflect​​必须存在于类路径中。也可以使用变​​kotlin-stdlib​​体​​kotlin-stdlib-jdk7​​和。​​kotlin-stdlib-jdk8​

由于Kotlin 类默认为 final,您可能需要配置kotlin-spring插件以自动打开带有 Spring 注释的类,以便它们可以被代理。

在 Kotlin中序列化/反序列化 JSON 数据需要Jackson 的 Kotlin 模块。在类路径中找到它时会自动注册。如果 Jackson 和 Kotlin 存在但 Jackson Kotlin 模块不存在,则会记录一条警告消息。

10.2. 零安全

Kotlin 的主要功能之一是null-safety。它在编译时处理​​null​​值,而不是将问题推迟到运行时并遇到​​NullPointerException​​. 这有助于消除常见的错误来源,而无需支付​​Optional​​. Kotlin 还允许使用具有可为空值的函数构造,如本Kotlin 中空安全综合指南中所述。

尽管 Java 不允许在其类型系统中表达 null 安全性,但 Spring Framework、Spring Data 和 Reactor 现在通过工具友好的注解为其 API 提供 null 安全性。默认情况下,Kotlin 中使用的 Java API 的类型被识别为放宽空检查的 平台类型。Kotlin 对 JSR 305 注释和可空性注释的支持为 Kotlin 中的相关 Spring API 提供了空值安全性。

可以通过添加​​-Xjsr305​​带有以下选项的编译器标志来配置 JSR 305 检查:​​-Xjsr305={strict|warn|ignore}​​. 默认行为与 相同​​-Xjsr305=warn​​。该​​strict​​值需要在从 Spring API 推断的 Kotlin 类型中考虑空安全性,但应在知道 Spring API 可空性声明甚至在次要版本之间演变并且将来可能会添加更多检查的情况下使用该值)。

10.3. Kotlin API

10.3.1. 运行应用程序

Spring Boot 提供了一种惯用的方式来运行应用程序,​​runApplication<MyApplication>(*args)​​如以下示例所示:

@SpringBootApplication
class MyApplication

fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}

这是​​SpringApplication.run(MyApplication::class.java, *args)​​. 它还允许自定义应用程序,如以下示例所示:

runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}

10.3.2. 扩展

Kotlin扩展提供了使用附加功能扩展现有类的能力。Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利。

​TestRestTemplate​​提供了类似于 Spring Framework 为​​RestOperations​​Spring Framework 提供的扩展。除其他外,这些扩展使利用 Kotlin 实体化类型参数成为可能。

10.4. 依赖管理

为了避免在类路径上混合不同版本的 Kotlin 依赖项,Spring Boot 导入了 Kotlin BOM。

使用 Maven,可以通过设置​​kotlin.version​​属性来自定义 Kotlin 版本,并为​​kotlin-maven-plugin​​. 使用 Gradle,Spring Boot 插件会自动与​​kotlin.version​​Kotlin 插件的版本对齐。

Spring Boot 还通过导入 Kotlin Coroutines BOM 来管理 Coroutines 依赖项的版本。可以通过设置​​kotlin-coroutines.version​​属性来自定义版本。

10.5。@ConfigurationProperties

​@ConfigurationProperties​​当与@ConstructorBinding具有不可变属性的支持类结合使用时​​val​​,如下例所示:

@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {

data class MyService(
val apiToken: String,
val uri: URI
)
}

10.6测试

虽然可以使用 JUnit 4 来测试 Kotlin 代码,但 JUnit 5 默认提供并推荐使用。JUnit 5 允许一个测试类被实例化一次,并被重用于该类的所有测试。这使得在非静态方法上使用​​@BeforeAll​​和​​@AfterAll​​注释成为可能,这非常适合 Kotlin。

要模拟 Kotlin 类,建议使用MockK。如果您需要​​Mockk​​等效的 Mockito 特定@MockBean和@SpyBean注释,您可以使用提供类似和注释的SpringMockK。​​@MockkBean​​​​@SpykBean​

10.7资源

10.7.1. 进一步阅读

  • Kotlin 语言参考
  • Kotlin Slack(带有专用的#spring 频道)
  • Stackoverflowspring和kotlin标签
  • 在浏览器中试用 Kotlin
  • 科特林博客
  • 很棒的科特林
  • 教程:使用 Spring Boot 和 Kotlin 构建 Web 应用程序
  • 使用 Kotlin 开发 Spring Boot 应用程序
  • 带有 Kotlin、Spring Boot 和 PostgreSQL 的地理空间信使
  • 在 Spring Framework 5.0 中引入 Kotlin 支持
  • Spring Framework 5 Kotlin API,函数式方式

10.7.2. 例子

  • spring-boot-kotlin-demo:常规 Spring Boot + Spring Data JPA 项目
  • mixit:Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
  • spring-kotlin-fullstack:WebFlux Kotlin fullstack 示例,前端使用 Kotlin2js 而不是 JavaScript 或 TypeScript
  • spring-petclinic-kotlin:Spring PetClinic 示例应用程序的 Kotlin 版本
  • spring-kotlin-deepdive:从 Boot 1.0 + Java 到 Boot 2.0 + Kotlin 的逐步迁移
  • spring-boot-coroutines-demo : 协程示例项目

11.接下来要读什么

如果您想了解有关本节中讨论的任何类的更多信息,请参阅Spring Boot API 文档,或者您可以直接浏览源代码。如果您有具体问题,请参阅操作方法部分。

如果您对 Spring Boot 的核心功能感到满意,您可以继续阅读有关生产就绪功能的信息。