diff --git a/docs/spring-boot/README.md b/docs/spring-boot/README.md index 98f6ceb2e5de2f79b479255ff266b95cef0fee76..8fdcf54f47a3daa8c72fe12ed4cf89b49c2d66b2 100644 --- a/docs/spring-boot/README.md +++ b/docs/spring-boot/README.md @@ -1 +1 @@ -# Spring Boot \ No newline at end of file +# Spring 引导 \ No newline at end of file diff --git a/docs/spring-boot/actuator.md b/docs/spring-boot/actuator.md index 7087f948a2b6436ef25146513acce2dc57308c8c..57978f0f72bac1cd3c77071fed2b9fdf1f75802f 100644 --- a/docs/spring-boot/actuator.md +++ b/docs/spring-boot/actuator.md @@ -1 +1,2936 @@ -# actuator \ No newline at end of file +# 可投入生产的功能 + +Spring 启动包括许多额外的功能,以帮助你在将应用程序推向生产时监视和管理该应用程序。你可以选择通过使用 HTTP 端点或使用 JMX 来管理和监视你的应用程序。审核、健康和度量收集也可以自动应用到你的应用程序中。 + +## 1. 启用可用于生产的功能 + +[`spring-boot-actuator`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator)模块提供了 Spring Boot 的所有可用于生产的功能。推荐的启用这些功能的方法是在`spring-boot-starter-actuator`“starter”上添加一个依赖项。 + +执行器的定义 + +执行器是一个制造术语,指用于移动或控制某物的机械设备。执行器可以从微小的变化中产生大量的运动。 + +要将执行器添加到基于 Maven 的项目中,请添加以下“starter”依赖项: + +``` + + + org.springframework.boot + spring-boot-starter-actuator + + +``` + +对于 Gradle,使用以下声明: + +``` +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-actuator' +} +``` + +## 2. 端点 + +执行器端点允许你监视应用程序并与其交互。 Spring 启动包括许多内置的端点,并允许你添加自己的端点。例如,`health`端点提供了基本的应用程序健康信息。 + +你可以[启用或禁用](#actuator.endpoints.enabling)每个单独的端点和[通过 HTTP 或 JMX 公开它们(使它们可以远程访问)](#actuator.endpoints.exposing)。当端点被启用和公开时,它被认为是可用的。内置端点只有在可用时才会自动配置。大多数应用程序选择的是公开而不是 HTTP,在 HTTP 中,端点的 ID 和`/actuator`的前缀被映射到一个 URL。例如,默认情况下,`health`端点映射到`/actuator/health`。 + +| |要了解有关执行器端点及其请求和响应格式的更多信息,请参见单独的 API 文档([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/htmlsingle)或[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/pdf/spring-boot-actuator-web-api.pdf))。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +以下是与技术无关的端点: + +| ID |说明| +|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`auditevents`|公开当前应用程序的审计事件信息。
需要`AuditEventRepository` Bean。| +|`beans`|显示应用程序中所有 Spring bean 的完整列表。| +|`caches`|公开可用的缓存。| +|`conditions`|显示在配置和自动配置类上评估的条件,以及它们匹配或不匹配的原因。| +|`configprops`|显示所有`@Configuration属性`的已整理列表。| +|`env`|公开 Spring 的`ConfigurableEnvironment`中的属性。| +|`flyway`|显示已应用的任何 Flyway 数据库迁移。
需要一个或多个`Flyway`bean。| +|`health`|显示应用程序的健康信息。| +|`httptrace`|显示 HTTP 跟踪信息(默认情况下,最近 100 次 HTTP 请求-响应交换)。
需要`HttpTraceRepository` Bean。| +|`info`|显示任意的应用程序信息。| +|`integrationgraph`|显示 Spring 积分图。
需要对`spring-integration-core`的依赖关系。| +|`loggers`|显示并修改应用程序中记录器的配置。| +|`liquibase`|显示已应用的任何 Liquibase 数据库迁移。
需要一个或多个`Liquibase`bean。| +|`metrics`|显示当前应用程序的“度量”信息。| +|`mappings`|显示所有`@RequestMapping`路径的已整理列表。| +|`quartz`|显示有关 Quartz 调度程序作业的信息。| +|`scheduledtasks`|显示应用程序中的计划任务。| +|`sessions`|允许从 Spring 会话支持的会话存储中检索和删除用户会话。
需要使用 Spring 会话的基于 Servlet 的 Web 应用程序。| +|`shutdown`|让应用程序优雅地关闭。
默认禁用。| +|`startup`|显示由`ApplicationStartup`收集的[启动步骤数据](features.html#features.spring-application.startup-tracking)。
需要将`SpringApplication`配置为`BufferingApplicationStartup`。| +|`threaddump`|执行线程转储。| + +如果你的应用程序是一个 Web 应用程序( Spring MVC、 Spring WebFlux 或 Jersey),则可以使用以下附加端点: + +| ID |说明| +|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `heapdump` |返回堆转储文件。
在热点 JVM 上,将返回一个`HPROF`-format 文件。
在 OpenJ9JVM 上,将返回一个`PHD`-format 文件。| +| `jolokia` |当 Jolokia 在 Classpath 上时,通过 HTTP 公开 JMX Bean(WebFlux 不可用)。
需要对`jolokia-core`的依赖关系。| +| `logfile` |返回日志文件的内容(如果设置了`logging.file.name`或`logging.file.path`属性)。
支持使用 http`Range`头来检索日志文件的部分内容。| +|`prometheus`|以 Prometheus 服务器可以抓取的格式公开度量数据。
需要对`micrometer-registry-prometheus`具有依赖关系。| + +### 2.1.启用端点 + +默认情况下,除了`shutdown`之外的所有端点都已启用。要配置端点的启用,请使用其`management.endpoint..enabled`属性。下面的示例启用`shutdown`端点: + +属性 + +``` +management.endpoint.shutdown.enabled=true +``` + +Yaml + +``` +management: + endpoint: + shutdown: + enabled: true +``` + +如果你希望端点使能是 OPT 输入而不是 OPT 输出,那么将`management.endpoints.enabled-by-default`属性设置为`false`,并使用单独的端点`enabled`属性来 OPT 返回。下面的示例启用`info`端点并禁用所有其他端点: + +属性 + +``` +management.endpoints.enabled-by-default=false +management.endpoint.info.enabled=true +``` + +Yaml + +``` +management: + endpoints: + enabled-by-default: false + endpoint: + info: + enabled: true +``` + +| |禁用的端点将从应用程序上下文中完全删除。
如果你只想更改公开端点的技术,请使用[`include` and `exclude` properties](#actuator.endpoints.exposing)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.2.公开端点 + +由于端点可能包含敏感信息,因此你应该仔细考虑何时公开它们。下表显示了内建端点的默认暴露: + +| ID |JMX|Web| +|------------------|---|---| +| `auditevents` |Yes|No | +| `beans` |Yes|No | +| `caches` |Yes|No | +| `conditions` |Yes|No | +| `configprops` |Yes|No | +| `env` |Yes|No | +| `flyway` |Yes|No | +| `health` |Yes|Yes| +|`heapdump`|N/A|No | +| `httptrace` |Yes|No | +| `info` |Yes|No | +|`integrationgraph`|Yes|No | +|`jolokia`|N/A|No | +|`logfile`|N/A|No | +| `loggers` |Yes|No | +| `liquibase` |Yes|No | +| `metrics` |Yes|No | +| `mappings` |Yes|No | +|`prometheus`|N/A|No | +| `quartz` |Yes|No | +| `scheduledtasks` |Yes|No | +| `sessions` |Yes|No | +| `shutdown` |Yes|No | +| `startup` |Yes|No | +| `threaddump` |Yes|No | + +要更改暴露的端点,请使用以下特定于技术的`include`和`exclude`属性: + +|财产|Default | +|-------------------------------------------|--------| +|`management.endpoints.jmx.exposure.exclude`| | +|`management.endpoints.jmx.exposure.include`| `*` | +|`management.endpoints.web.exposure.exclude`| | +|`management.endpoints.web.exposure.include`|`health`| + +`include`属性列出了公开的端点的 ID。`exclude`属性列出了不应该公开的端点的 ID。`exclude`属性优先于`include`属性。你可以使用端点 ID 列表来配置`include`和`exclude`属性。 + +例如,要停止在 JMX 上公开所有端点,而只公开`health`和`info`端点,请使用以下属性: + +属性 + +``` +management.endpoints.jmx.exposure.include=health,info +``` + +Yaml + +``` +management: + endpoints: + jmx: + exposure: + include: "health,info" +``` + +`*`可用于选择所有端点。例如,要在 HTTP 上公开除了`env`和`beans`端点之外的所有内容,请使用以下属性: + +属性 + +``` +management.endpoints.web.exposure.include=* +management.endpoints.web.exposure.exclude=env,beans +``` + +Yaml + +``` +management: + endpoints: + web: + exposure: + include: "*" + exclude: "env,beans" +``` + +| |`*`在 YAML 中具有特殊的含义,因此,如果你想包含(或排除)所有端点,请务必添加引号。| +|---|-----------------------------------------------------------------------------------------------------------------------| + +| |如果你的应用程序公开,我们强烈建议你也[保护你的端点](#actuator.endpoints.security)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------| + +| |如果你希望实现自己的端点公开策略,则可以注册`EndpointFilter` Bean。| +|---|---------------------------------------------------------------------------------------------------------------------| + +### 2.3.安全 + +出于安全目的,除`/health`以外的所有执行器都默认禁用。你可以使用`management.endpoints.web.exposure.include`属性来启用执行器。 + +| |在设置`management.endpoints.web.exposure.include`之前,请确保暴露的执行器不包含敏感信息,通过将它们放置在防火墙后面来进行安全保护,或者通过 Spring 安全性之类的方法进行安全保护。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果 Spring 安全性在 Classpath 上,并且不存在其他`WebSecurityConfigurerAdapter`或`SecurityFilterChain` Bean,则除`/health`以外的所有致动器都由 Spring 引导自动配置来保护。如果你定义了自定义的`WebSecurityConfigurerAdapter`或`SecurityFilterChain` Bean, Spring 引导自动配置就会后退,并允许你完全控制执行器访问规则。 + +如果你希望为 HTTP 端点配置自定义安全性(例如,仅允许具有特定角色的用户访问它们), Spring Boot 提供了一些方便的`RequestMatcher`对象,你可以将这些对象与 Spring 安全性结合使用。 + +Spring 典型的安全配置可能类似于以下示例: + +``` +import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration(proxyBeanMethods = false) +public class MySecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()) + .authorizeRequests((requests) -> requests.anyRequest().hasRole("ENDPOINT_ADMIN")); + http.httpBasic(); + return http.build(); + } + +} + +``` + +前面的示例使用`EndpointRequest.toAnyEndpoint()`将请求匹配到任何端点,然后确保所有请求都具有`ENDPOINT_ADMIN`角色。在`EndpointRequest`上也可以使用其他几种匹配方法。有关详细信息,请参见 API 文档([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/htmlsingle)或[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/pdf/spring-boot-actuator-web-api.pdf))。 + +如果你将应用程序部署在防火墙之后,那么你可能希望你的所有执行器端点都可以访问,而无需进行身份验证。你可以通过更改`management.endpoints.web.exposure.include`属性来实现此目的,如下所示: + +属性 + +``` +management.endpoints.web.exposure.include=* +``` + +Yaml + +``` +management: + endpoints: + web: + exposure: + include: "*" +``` + +此外,如果存在 Spring 安全性,则需要添加自定义安全配置,该配置允许对端点进行未经身份验证的访问,如下例所示: + +``` +import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration(proxyBeanMethods = false) +public class MySecurityConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()) + .authorizeRequests((requests) -> requests.anyRequest().permitAll()); + return http.build(); + } + +} + +``` + +| |在上述两个示例中,配置仅适用于执行器端点。
由于 Spring boot 的安全配置在存在任何`SecurityFilterChain` Bean 的情况下完全退缩,你需要配置一个附加的`SecurityFilterChain` Bean,其规则适用于应用程序的其余部分。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.3.1.跨站点请求伪造保护 + +由于 Spring 启动依赖于 Spring 安全性的默认值,因此 CSRF 保护在默认情况下是打开的。这意味着要求执行器端点`POST`(关机和记录器端点)、`PUT`或`DELETE`的端点在使用默认安全配置时会得到 403(禁止)错误。 + +| |我们建议仅当你正在创建一个由非浏览器客户端使用的服务时,才完全禁用 CSRF 保护。| +|---|-------------------------------------------------------------------------------------------------------------------------| + +你可以在[Spring Security Reference Guide](https://docs.spring.io/spring-security/reference/5.6.2/features/exploits/csrf.html)中找到有关 CSRF 保护的其他信息。 + +### 2.4.配置端点 + +端点自动缓存响应,以读取不接受任何参数的操作。要配置端点缓存响应的时间长度,请使用其`cache.time-to-live`属性。下面的示例将`beans`端点缓存的持续时间设置为 10 秒: + +属性 + +``` +management.endpoint.beans.cache.time-to-live=10s +``` + +Yaml + +``` +management: + endpoint: + beans: + cache: + time-to-live: "10s" +``` + +| |`management.endpoint.`前缀唯一地标识正在配置的端点。| +|---|--------------------------------------------------------------------------------------------------| + +### 2.5.用于执行器 Web 端点的超媒体 + +添加了一个“发现页面”,其中包含指向所有端点的链接。默认情况下,“发现页面”在`/actuator`上可用。 + +要禁用“发现页面”,请将以下属性添加到应用程序属性中: + +属性 + +``` +management.endpoints.web.discovery.enabled=false +``` + +Yaml + +``` +management: + endpoints: + web: + discovery: + enabled: false +``` + +在配置自定义管理上下文路径时,“发现页”会自动从`/actuator`移动到管理上下文的根。例如,如果管理上下文路径是`/management`,则可以从`/management`获取发现页面。当管理上下文路径设置为`/`时,将禁用发现页,以防止与其他映射发生冲突的可能性。 + +### 2.6.CORS 支持 + +[跨源资源共享](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)是一个[W3C 规范](https://www.w3.org/TR/cors/),它允许你以灵活的方式指定授权哪种类型的跨域请求。如果使用 Spring MVC 或 Spring WebFlux,则可以配置 Actuator 的 Web 端点以支持此类场景。 + +默认情况下,CORS 支持是禁用的,并且只有在你设置了`management.endpoints.web.cors.allowed-origins`属性后才会启用。以下配置允许`GET`和`POST`来自`example.com`域的调用: + +属性 + +``` +management.endpoints.web.cors.allowed-origins=https://example.com +management.endpoints.web.cors.allowed-methods=GET,POST +``` + +Yaml + +``` +management: + endpoints: + web: + cors: + allowed-origins: "https://example.com" + allowed-methods: "GET,POST" +``` + +| |有关选项的完整列表,请参见[“CorsendPoint属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/endpoint/web/CorsEndpoint属性.java)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.7.实现自定义端点 + +如果添加带有`@Bean`注释的`@Endpoint`,则任何带有`@ReadOperation`、`@WriteOperation`或`@DeleteOperation`注释的方法都会在 JMX 上自动公开,在 Web 应用程序中,也会在 HTTP 上自动公开。可以通过使用 Jersey、 Spring MVC 或 Spring WebFlux 在 HTTP 上公开端点。如果球衣和 Spring MVC 都可用,则使用 Spring MVC。 + +下面的示例公开了一个返回自定义对象的读取操作: + +``` +@ReadOperation +public CustomData getData() { + return new CustomData("test", 5); +} + +``` + +你还可以通过使用`@JmxEndpoint`或`@WebEndpoint`来编写特定于技术的端点。这些端点仅限于各自的技术。例如,`@WebEndpoint`仅在 HTTP 上公开,而不在 JMX 上公开。 + +你可以使用`@EndpointWebExtension`和`@EndpointJmxExtension`来编写特定于技术的扩展。这些注释允许你提供特定于技术的操作,以增强现有的端点。 + +最后,如果需要访问特定于 Web Framework 的功能,则可以实现 Servlet 或 Spring `@Controller`和`@RestController`端点,代价是它们不能在 JMX 上或在使用不同的 Web 框架时可用。 + +#### 2.7.1.接收输入 + +端点上的操作通过其参数接收输入。当在 Web 上公开时,这些参数的值来自 URL 的查询参数和 JSON 请求主体。当通过 JMX 公开时,参数将映射到 MBean 操作的参数。默认情况下需要参数。可以通过使用`@javax.annotation.Nullable`或`@org.springframework.lang.Nullable`对它们进行注释来使它们成为可选的。 + +你可以将 JSON 请求主体中的每个根属性映射到端点的参数。考虑以下 JSON 请求主体: + +``` +{ + "name": "test", + "counter": 42 +} +``` + +你可以使用它来调用一个包含`String name`和`int counter`参数的写操作,如下例所示: + +``` +@WriteOperation +public void updateData(String name, int counter) { + // injects "test" and 42 +} + +``` + +| |由于端点与技术无关,因此只能在方法签名中指定简单的类型。
特别是,声明带有`CustomData`类型的单个参数,该类型定义了`name`和`counter`属性,不受支持。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |为了让输入映射到操作方法的参数,实现端点的 Java 代码应该使用`-parameters`进行编译, Kotlin 实现端点的代码应该用`-java-parameters`编译。
如果你使用 Spring boot 的 Gradle 插件,或者如果你使用 Maven 和`spring-boot-starter-parent`,这将自动发生。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### 输入类型转换 + +如果需要,将传递给端点操作方法的参数自动转换为所需的类型。在调用操作方法之前,通过使用`ApplicationConversionService`的实例以及`Converter`或`GenericConverter`的实例,将通过 JMX 或 HTTP 接收的输入转换为所需的类型。 + +#### 2.7.2.自定义 Web 端点 + +对`@Endpoint`、`@WebEndpoint`或`@EndpointWebExtension`的操作使用 Jersey、 Spring MVC 或 Spring WebFlux 在 HTTP 上自动公开。如果球衣和 Spring MVC 都可用,则使用 Spring MVC。 + +##### Web 端点请求谓词 + +对于公开 Web 的端点上的每个操作,都会自动生成一个请求谓词。 + +##### 路径 + +谓词的路径由端点的 ID 和公开 Web 的端点的基本路径决定。默认的基本路径是`/actuator`。例如,ID 为`sessions`的端点在谓词中使用`/actuator/sessions`作为其路径。 + +你可以通过使用`@Selector`注释操作方法的一个或多个参数来进一步自定义路径。将这样的参数作为路径变量添加到路径谓词中。当调用端点操作时,变量的值被传递到操作方法中。如果要捕获所有剩余的路径元素,可以将`@Selector(Match=ALL_REMAINING)`添加到最后一个参数,并使其成为与`String[]`转换兼容的类型。 + +##### HTTP 方法 + +谓词的 HTTP 方法由操作类型决定,如下表所示: + +|操作|HTTP method| +|------------------|-----------| +|`@ReadOperation`| `GET` | +|`@WriteOperation`| `POST` | +|`@DeleteOperation`| `DELETE` | + +##### 消耗 + +对于使用请求主体的`@WriteOperation`(http`POST`),谓词的`consumes`子句是`application/vnd.spring-boot.actuator.v2+json, application/json`。对于所有其他操作,`consumes`子句是空的。 + +##### 生产 + +谓词的`produces`子句可以由`@DeleteOperation`、`@ReadOperation`和`@WriteOperation`注释的`produces`属性确定。属性是可选的。如果不使用,则自动确定`produces`子句。 + +如果操作方法返回`void`或`Void`,则`produces`子句为空。如果操作方法返回一个`org.springframework.core.io.Resource`,则`produces`子句是`application/octet-stream`。对于所有其他操作,`produces`子句是`application/vnd.spring-boot.actuator.v2+json, application/json`。 + +##### Web 端点响应状态 + +端点操作的默认响应状态取决于操作类型(读、写或删除)以及操作返回的内容(如果有的话)。 + +如果`@ReadOperation`返回一个值,则响应状态将为 200(OK)。如果它不返回一个值,响应状态将是 404(未找到)。 + +如果`@WriteOperation`或`@DeleteOperation`返回一个值,则响应状态将为 200(确定)。如果它不返回一个值,响应状态将是 204(没有内容)。 + +如果调用的操作没有所需的参数或参数不能转换为所需的类型,则不调用该操作方法,响应状态将为 400(错误请求)。 + +##### Web 端点范围请求 + +你可以使用 HTTP 范围请求来请求 HTTP 资源的一部分。当使用 Spring MVC 或 Spring Web 流量时,返回`org.springframework.core.io.Resource`的操作自动支持范围请求。 + +| |使用 Jersey 时不支持范围请求。| +|---|---------------------------------------------------| + +##### Web 端点安全 + +在 Web 端点或特定于 Web 的端点扩展上的操作可以接收当前的`java.security.Principal`或`org.springframework.boot.actuate.endpoint.SecurityContext`作为方法参数。前者通常与`@Nullable`结合使用,以为经过身份验证的和未经身份验证的用户提供不同的行为。后者通常通过使用其`isUserInRole(String)`方法来执行授权检查。 + +#### 2.7.3. Servlet 端点 + +Servlet 可以通过实现一个用`@ServletEndpoint`注释的类公开为端点,该类也实现`Supplier`。 Servlet 端点提供了与 Servlet 容器的更深的集成,但以牺牲可移植性为代价。它们旨在用于公开现有的 Servlet 作为端点。对于新的端点,应尽可能使用`@Endpoint`和`@WebEndpoint`注释。 + +#### 2.7.4.控制器端点 + +可以使用`@ControllerEndpoint`和`@RestControllerEndpoint`来实现仅由 Spring MVC 或 Spring WebFlux 公开的端点。方法通过使用 Spring MVC 和 Spring WebFlux 的标准注释进行映射,例如`@RequestMapping`和`@GetMapping`,并使用端点的 ID 作为路径的前缀。控制器端点提供了与 Spring 的 Web 框架更深入的集成,但以牺牲可移植性为代价。只要有可能,就应该首选`@Endpoint`和`@WebEndpoint`注释。 + +### 2.8.健康信息 + +你可以使用健康信息来检查正在运行的应用程序的状态。当生产系统发生故障时,监控软件经常使用它来提醒某人。由`health`端点公开的信息取决于`management.endpoint.health.show-details`和`management.endpoint.health.show-components`属性,这些属性可以配置为以下值之一: + +| Name |说明| +|-----------------|-------------------------------------------------------------------------------------------------------------------------------| +| `never` |细节从未显示。| +|`when-authorized`|详细信息仅向授权用户显示。
授权角色可以通过使用`management.endpoint.health.roles`进行配置。| +| `always` |详细信息将向所有用户显示。| + +默认值为`never`。当用户处于端点的一个或多个角色中时,该用户被视为获得授权。如果端点没有配置的角色(默认值),则所有经过身份验证的用户都被认为是经过授权的。你可以使用`management.endpoint.health.roles`属性来配置角色。 + +| |如果你已经保护了你的应用程序,并且希望使用`always`,那么你的安全配置必须允许经过身份验证的和未经身份验证的用户访问健康端点。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +健康信息是从[` 健康贡献登记册’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributorRegistry.java)的内容中收集的(默认情况下,在你的`ApplicationContext`中定义的所有[“健康贡献者”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributor.java)实例)。 Spring 启动包括一些自动配置的`HealthContributors`,并且还可以编写自己的。 + +a`HealthContributor`可以是 a`HealthIndicator`,也可以是 a`CompositeHealthContributor`。a`HealthIndicator`提供实际的健康信息,包括 a`Status`。a`CompositeHealthContributor`提供了其它`HealthContributors`的复合。总的来说,贡献者形成了一个树状结构来表示整个系统的健康状况。 + +默认情况下,最终的系统健康状况是由`StatusAggregator`派生的,它根据状态的有序列表对每个`HealthIndicator`的状态进行排序。排序列表中的第一个状态被用作整体健康状态。如果没有`HealthIndicator`返回`StatusAggregator`已知的状态,则使用`UNKNOWN`状态。 + +| |你可以使用`HealthContributorRegistry`在运行时注册和取消注册健康指标。| +|---|----------------------------------------------------------------------------------------------------| + +#### 2.8.1.自动配置的健康指示器 + +在适当的时候, Spring 引导自动配置下表中列出的`HealthIndicators`。你还可以通过配置`management.health.key.enabled`来启用或禁用选定的指示器,下表列出了`key`: + +| Key |姓名| Description | +|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------| +| `cassandra` |[“CassandRadriverHealthindicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java)| Checks that a Cassandra database is up. | +| `couchbase` |[“CouchBaseHealthIndicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseHealthIndicator.java)| Checks that a Couchbase cluster is up. | +| `db` |[“数据来源健康指示器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.java)|Checks that a connection to `DataSource` can be obtained.| +| `diskspace` |[diskspacehealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/system/DiskSpaceHealthIndicator.java)| Checks for low disk space. | +|`elasticsearch`|[“弹性搜索算法”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchRestHealthIndicator.java)| Checks that an Elasticsearch cluster is up. | +| `hazelcast` |[Hazelcasthealthindicator](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/hazelcast/HazelcastHealthIndicator.java)| Checks that a Hazelcast server is up. | +| `influxdb` |[` 影响健康指标’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/influx/InfluxDbHealthIndicator.java)| Checks that an InfluxDB server is up. | +| `jms` |[JMSearthIndicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/jms/JmsHealthIndicator.java)| Checks that a JMS broker is up. | +| `ldap` |[ldaphealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/ldap/LdapHealthIndicator.java)| Checks that an LDAP server is up. | +| `mail` |[“MailHealthindicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mail/MailHealthIndicator.java)| Checks that a mail server is up. | +| `mongo` |[` 蒙古健康指数’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mongo/MongoHealthIndicator.java)| Checks that a Mongo database is up. | +| `neo4j` |[NEO4jhealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jHealthIndicator.java)| Checks that a Neo4j database is up. | +| `ping` |[Pinghealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/PingHealthIndicator.java)| Always responds with `UP`. | +| `rabbit` |[` 犹太教神职人员 `](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/amqp/RabbitHealthIndicator.java)| Checks that a Rabbit server is up. | +| `redis` |[`Redishealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisHealthIndicator.java)| Checks that a Redis server is up. | +| `solr` |[“SolrHealthindicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/solr/SolrHealthIndicator.java)| Checks that a Solr server is up. | + +| |你可以通过设置`management.health.defaults.enabled`属性来禁用它们。| +|---|--------------------------------------------------------------------------------------| + +其他`HealthIndicators`可用,但默认情况下未启用: + +| Key |姓名| Description | +|----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +|`livenessstate` |[“生活状态健康指示器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/LivenessStateHealthIndicator.java)|Exposes the “Liveness” application availability state. | +|`readinessstate`|[“ReadinessStateHealthIndicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/availability/ReadinessStateHealthIndicator.java)|Exposes the “Readiness” application availability state.| + +#### 2.8.2.编写自定义健康指示器 + +要提供自定义健康信息,你可以注册实现[` 健康指示器 `](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthIndicator.java)接口的 Spring bean。你需要提供`health()`方法的一个实现,并返回一个`Health`响应。`Health`响应应该包括一个状态,并且可以选择包括要显示的其他细节。下面的代码显示了`HealthIndicator`实现的示例: + +``` +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class MyHealthIndicator implements HealthIndicator { + + @Override + public Health health() { + int errorCode = check(); + if (errorCode != 0) { + return Health.down().withDetail("Error Code", errorCode).build(); + } + return Health.up().build(); + } + + private int check() { + // perform some specific health check + return ... + } + +} + +``` + +| |给定的`HealthIndicator`的标识符是不带`HealthIndicator`后缀的 Bean 的名称,如果它存在的话。
在前面的示例中,健康信息在名为`my`的条目中可用。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +除了 Spring boot 的预定义[`Status`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java)类型之外,`Health`还可以返回一个自定义的`Status`,它表示一个新的系统状态。在这种情况下,还需要提供[StatusAggregator](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/StatusAggregator.java)接口的自定义实现,或者必须通过使用`management.endpoint.health.status.order`配置属性来配置默认实现。 + +例如,假设一个新的`Status`代码为`FATAL`的`HealthIndicator`正在你的一个`HealthIndicator`实现中使用。要配置严重性顺序,请将以下属性添加到应用程序属性中: + +属性 + +``` +management.endpoint.health.status.order=fatal,down,out-of-service,unknown,up +``` + +Yaml + +``` +management: + endpoint: + health: + status: + order: "fatal,down,out-of-service,unknown,up" +``` + +响应中的 HTTP 状态代码反映了整体的健康状态。默认情况下,`OUT_OF_SERVICE`和`DOWN`映射到 503。任何未映射的健康状态,包括`UP`,都映射到 200。如果你通过 HTTP 访问健康端点,你可能还希望注册自定义状态映射。配置自定义映射会禁用`DOWN`和`OUT_OF_SERVICE`的默认映射。如果希望保留默认映射,则必须显式地配置它们以及任何自定义映射。例如,下面的属性将`FATAL`映射到 503(服务不可用),并保留`DOWN`和`OUT_OF_SERVICE`的默认映射: + +属性 + +``` +management.endpoint.health.status.http-mapping.down=503 +management.endpoint.health.status.http-mapping.fatal=503 +management.endpoint.health.status.http-mapping.out-of-service=503 +``` + +Yaml + +``` +management: + endpoint: + health: + status: + http-mapping: + down: 503 + fatal: 503 + out-of-service: 503 +``` + +| |如果需要更多的控制,可以定义自己的`HttpCodeStatusMapper` Bean。| +|---|------------------------------------------------------------------------------| + +下表显示了内置状态的默认状态映射: + +| Status |映射| +|----------------|----------------------------------------------| +| `DOWN` |`SERVICE_UNAVAILABLE`(`503’)| +|`OUT_OF_SERVICE`|`SERVICE_UNAVAILABLE`(`503’)| +| `UP` |默认情况下没有映射,所以 HTTP 状态是`200`| +| `UNKNOWN` |默认情况下没有映射,所以 HTTP 状态是`200`| + +#### 2.8.3.反应性健康指标 + +对于反应性应用程序,例如那些使用 Spring WebFlux 的应用程序,`ReactiveHealthContributor`提供了用于获取应用程序健康状态的非阻塞契约。与传统的`HealthContributor`类似,健康信息是从[“ReactiveHealthContributorRegistry”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthContributorRegistry.java)的内容中收集的(默认情况下,所有[“健康贡献者”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthContributor.java)和[“ReactiveHealthContributor”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthContributor.java)实例定义在你的`ApplicationContext`中)。常规的`HealthContributors`在弹性计划程序上执行,它们不会根据反应性 API 进行检查。 + +| |在反应性应用程序中,你应该使用`ReactiveHealthContributorRegistry`在运行时注册和取消注册健康指标。
如果你需要注册一个常规的`HealthContributor`,你应该用`ReactiveHealthContributor#adapt`包装它。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要从反应性 API 提供自定义健康信息,你可以注册实现[“恢复健康指示器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/ReactiveHealthIndicator.java)接口的 Spring bean。下面的代码显示了`ReactiveHealthIndicator`实现的示例: + +``` +import reactor.core.publisher.Mono; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.ReactiveHealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class MyReactiveHealthIndicator implements ReactiveHealthIndicator { + + @Override + public Mono health() { + return doHealthCheck().onErrorResume((exception) -> + Mono.just(new Health.Builder().down(exception).build())); + } + + private Mono doHealthCheck() { + // perform some specific health check + return ... + } + +} + +``` + +| |要自动处理该错误,请考虑从`AbstractReactiveHealthIndicator`扩展。| +|---|---------------------------------------------------------------------------------------------| + +#### 2.8.4.自动配置的 reactivehealthindicators### + +在适当的时候, Spring 引导自动配置以下`ReactiveHealthIndicators`: + +| Key |姓名| Description | +|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------| +| `cassandra` |[cassandradriverreactivehealthindicator’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverReactiveHealthIndicator.java)| Checks that a Cassandra database is up. | +| `couchbase` |[“CouchBaseReactiveHealthindicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/couchbase/CouchbaseReactiveHealthIndicator.java)| Checks that a Couchbase cluster is up. | +|`elasticsearch`|[“弹性搜索激活健康指示器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/elasticsearch/ElasticsearchReactiveHealthIndicator.java)|Checks that an Elasticsearch cluster is up.| +| `mongo` |[“MongoReactiveHealthindicator”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/mongo/MongoReactiveHealthIndicator.java)| Checks that a Mongo database is up. | +| `neo4j` |[“NEO4,恢复健康指标”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/neo4j/Neo4jReactiveHealthIndicator.java)| Checks that a Neo4j database is up. | +| `redis` |[“恢复活动健康指示器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/redis/RedisReactiveHealthIndicator.java)| Checks that a Redis server is up. | + +| |如果有必要,用反应性指示器代替常规指示器。
此外,任何未显式处理的`HealthIndicator`都会自动包装。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.5.健康团体 + +有时,将健康指标组织成可用于不同目的的组是有用的。 + +要创建健康指示剂组,可以使用`management.endpoint.health.group.`属性,并将健康指示剂 ID 的列表指定为`include`或`exclude`。例如,要创建一个只包含数据库指标的组,你可以定义以下内容: + +属性 + +``` +management.endpoint.health.group.custom.include=db +``` + +Yaml + +``` +management: + endpoint: + health: + group: + custom: + include: "db" +``` + +然后,你可以点击`[localhost:8080/actuator/health/custom](http://localhost:8080/actuator/health/custom)`检查结果。 + +类似地,要创建一个从组中排除数据库指标并包括所有其他指标的组,你可以定义以下内容: + +属性 + +``` +management.endpoint.health.group.custom.exclude=db +``` + +Yaml + +``` +management: + endpoint: + health: + group: + custom: + exclude: "db" +``` + +默认情况下,组继承与系统健康相同的`StatusAggregator`和`HttpCodeStatusMapper`设置。但是,你也可以在每个组的基础上定义这些。如果需要,还可以重写`show-details`和`roles`属性: + +属性 + +``` +management.endpoint.health.group.custom.show-details=when-authorized +management.endpoint.health.group.custom.roles=admin +management.endpoint.health.group.custom.status.order=fatal,up +management.endpoint.health.group.custom.status.http-mapping.fatal=500 +management.endpoint.health.group.custom.status.http-mapping.out-of-service=500 +``` + +Yaml + +``` +management: + endpoint: + health: + group: + custom: + show-details: "when-authorized" + roles: "admin" + status: + order: "fatal,up" + http-mapping: + fatal: 500 + out-of-service: 500 +``` + +| |如果需要注册自定义`StatusAggregator`或`HttpCodeStatusMapper`bean 以用于组,则可以使用`@Qualifier("groupname")`bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------| + +健康组也可以包括/排除`CompositeHealthContributor`。你也可以只包含/排除`CompositeHealthContributor`中的某个组件。这可以使用组件的完全限定名称来完成,如下所示: + +``` +management.endpoint.health.group.custom.include="test/primary" +management.endpoint.health.group.custom.exclude="test/primary/b" +``` + +在上面的示例中,`custom`组将包括名为`HealthContributor`的`primary`,这是组合`test`的组件。在这里,`primary`本身是一个组合,而名称为`b`的`HealthContributor`将被排除在`custom`组之外。 + +可以在主端口或管理端口的附加路径上提供健康组。这在 Kubernetes 等云环境中很有用,在这种环境中,出于安全目的,对执行器端点使用单独的管理端口是很常见的。拥有一个单独的端口可能会导致不可靠的健康检查,因为即使健康检查成功,主应用程序也可能无法正常工作。可以将健康组配置为以下附加路径: + +``` +management.endpoint.health.group.live.additional-path="server:/healthz" +``` + +这将使`live`健康组在主服务器端口`/healthz`上可用。前缀是强制性的,并且必须是`server:`(表示主服务器端口)或`management:`(表示管理端口,如果已配置的话。)路径必须是单个路径段。 + +#### 2.8.6.数据源健康 + +`DataSource`健康指示器显示标准数据源和路由数据源 bean 的健康状况。路由数据源的健康状况包括其每个目标数据源的健康状况。在健康端点的响应中,每个路由数据源的目标都是通过使用其路由密钥来命名的。如果不希望在指示器的输出中包含路由数据源,请将`management.health.db.ignore-routing-data-sources`设置为`true`。 + +### 2.9.Kubernetes 探测器 + +部署在 Kubernetes 上的应用程序可以用[容器探针](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes)提供有关其内部状态的信息。根据[你的 Kubernetes 配置](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/),Kubelet 调用这些探测并对结果做出反应。 + +默认情况下, Spring boot 管理你的[应用程序可用性状态](features.html#features.spring-application.application-availability)。如果部署在 Kubernetes 环境中,致动器将从`ApplicationAvailability`接口收集“活性”和“就绪”信息,并在专用[健康指标](#actuator.endpoints.health.auto-configured-health-indicators)中使用该信息:`LivenessStateHealthIndicator`和`ReadinessStateHealthIndicator`。这些指标显示在全球健康端点(“/执行器/健康”`)上。通过使用[health groups](#actuator.endpoints.health.groups):`"/actuator/health/liveness"`和`"/actuator/health/readiness"`,它们也可以作为单独的 HTTP 探针公开。 + +然后,你可以使用以下端点信息来配置 Kubernetes 基础架构: + +``` +livenessProbe: + httpGet: + path: "/actuator/health/liveness" + port: + failureThreshold: ... + periodSeconds: ... + +readinessProbe: + httpGet: + path: "/actuator/health/readiness" + port: + failureThreshold: ... + periodSeconds: ... +``` + +| |``应该设置为执行器端点可用的端口。
如果`"management.server.port"`属性已设置,它可以是主 Web 服务器端口或单独的管理端口。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +只有当应用程序[在 Kubernetes 环境中运行](deployment.html#deployment.cloud.kubernetes)时,这些健康组才会自动启用。你可以通过使用`management.endpoint.health.probes.enabled`配置属性在任何环境中启用它们。 + +| |如果一个应用程序的启动时间超过了配置的活动周期,Kubernetes 提到`"startupProbe"`是一个可能的解决方案。
这里不一定需要`"startupProbe"`,因为`"readinessProbe"`在完成所有启动任务之前都会失败。参见描述[探针在应用程序生命周期中的行为](#actuator.endpoints.kubernetes-probes.lifecycle)的部分。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你的执行器端点部署在单独的管理上下文中,那么端点不使用与主应用程序相同的 Web 基础设施(端口、连接池、框架组件)。在这种情况下,即使主应用程序不能正常工作(例如,它不能接受新的连接),探测检查也可能是成功的。出于这个原因,在主服务器端口上提供`liveness`和`readiness`健康组是一个好主意。这可以通过设置以下属性来完成: + +``` +management.endpoint.health.probes.add-additional-paths=true +``` + +这将使`liveness`在主服务器端口上的`/livez`和`readiness`上可用。 + +#### 2.9.1.用 Kubernetes 探测器检查外部状态 ### + +Actuator 将“活性”和“准备”探测配置为健康小组。这意味着所有的[健康团体功能](#actuator.endpoints.health.groups)对它们都是可用的。例如,你可以配置其他的健康指标: + +属性 + +``` +management.endpoint.health.group.readiness.include=readinessState,customCheck +``` + +Yaml + +``` +management: + endpoint: + health: + group: + readiness: + include: "readinessState,customCheck" +``` + +默认情况下, Spring Boot 不会向这些组添加其他健康指标。 + +“活性”调查不应依赖于外部系统的健康检查。如果[应用程序的活性状态](features.html#features.spring-application.application-availability.liveness)被破坏,Kubernetes 将尝试通过重新启动应用程序实例来解决该问题。这意味着,如果外部系统(例如数据库、Web API 或外部缓存)发生故障,Kubernetes 可能会重新启动所有应用程序实例并产生级联故障。 + +至于“就绪”调查,检查外部系统的选择必须由应用程序开发人员仔细做出。出于这个原因, Spring Boot 不包括在准备状态探测中的任何额外的健康检查。如果[应用程序实例的准备状态](features.html#features.spring-application.application-availability.readiness)未准备好,则 Kubernetes 不会将通信量路由到该实例。一些外部系统可能不会被应用程序实例共享,在这种情况下,它们可能会被包含在就绪状态探测中。其他外部系统可能对应用程序不是必需的(应用程序可能有断路器和后备),在这种情况下,它们绝对不应该被包含。不幸的是,所有应用程序实例共享的外部系统是常见的,你必须做出判断:将其纳入准备状态调查,并期望当外部服务关闭或将其排除在外时,应用程序将退出服务,并处理堆栈中更高的故障,也许通过在呼叫者中使用断路器。 + +| |如果应用程序的所有实例都没有准备好,那么带有`type=ClusterIP`或`NodePort`的 Kubernetes 服务不接受任何传入连接。
没有 HTTP 错误响应(503 等),因为没有连接。
带有`type=LoadBalancer`的服务可能接受连接,也可能不接受连接,取决于提供者。
具有显式[ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)的服务也以某种方式响应这取决于实现——Ingress 服务本身必须决定如何处理来自下游的“拒绝连接”。
HTTP503 在负载均衡器和 Ingress 两种情况下都很有可能发生。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此外,如果一个应用程序使用 Kubernetes[autoscaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/),它可能会对从负载均衡器取出的应用程序做出不同的反应,这取决于它的自动定标器配置。 + +#### 2.9.2.应用程序生命周期和探测状态 + +Kubernetes Probes 支持的一个重要方面是它与应用程序生命周期的一致性。在`AvailabilityState`(这是应用程序在内存中的内部状态)和实际的探测(公开该状态)之间存在显著差异。根据应用程序生命周期的阶段,探测可能不可用。 + +Spring 引导发布[启动和关闭过程中的应用程序事件](features.html#features.spring-application.application-events-and-listeners),并且探测可以侦听这样的事件并公开`AvailabilityState`信息。 + +下表显示了`AvailabilityState`和 HTTP 连接器在不同阶段的状态。 + +当 Spring 启动应用程序启动时: + +|Startup phase|LivenessState| ReadinessState | HTTP server |笔记| +|-------------|-------------|-------------------|----------------|--------------------------------------------------------------------------------------------------------------| +| Starting | `BROKEN` |`REFUSING_TRAFFIC` | Not started |Kubernetes 检查“活性”探测,如果时间太长,则重新启动应用程序。| +| Started | `CORRECT` |`REFUSING_TRAFFIC` |Refuses requests|将刷新应用程序上下文。应用程序执行启动任务,但尚未接收到流量。| +| Ready | `CORRECT` |`ACCEPTING_TRAFFIC`|Accepts requests|启动任务完成。应用程序正在接收流量。| + +当 Spring 引导应用程序关闭时: + +| Shutdown phase |Liveness State| Readiness State | HTTP server |笔记| +|-----------------|--------------|-------------------|-------------------------|---------------------------------------------------------------------------------------------| +| Running | `CORRECT` |`ACCEPTING_TRAFFIC`| Accepts requests |已请求关闭。| +|Graceful shutdown| `CORRECT` |`REFUSING_TRAFFIC` |New requests are rejected|如果启用,[优雅的关机处理飞行中的请求](web.html#web.graceful-shutdown)。| +|Shutdown complete| N/A | N/A | Server is shut down |应用程序上下文是关闭的,应用程序是关闭的。| + +| |有关 Kubernetes 部署的更多信息,请参见[Kubernetes 容器生命周期部分](deployment.html#deployment.cloud.kubernetes.container-lifecycle)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.10.应用程序信息 + +应用程序信息公开从你的`ApplicationContext`中定义的所有[“信息贡献者”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoContributor.java)bean 中收集的各种信息。 Spring 引导包括许多自动配置的`InfoContributor`bean,并且可以编写自己的 bean。 + +#### 2.10.1.自动配置的信息贡献者 + +在适当的时候, Spring 自动配置以下`InfoContributor`bean: + +| ID |姓名| Description | Prerequisites | +|-------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|--------------------------------------------| +|`build`|[“建立贡献者”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/BuildInfoContributor.java)| Exposes build information. |A `META-INF/build-info.properties` resource.| +| `env` |[` 环境保护者’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/EnvironmentInfoContributor.java)|Exposes any property from the `Environment` whose name starts with `info.`.| None. | +| `git` |[“GitInfoContributor”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/GitInfoContributor.java)| Exposes git information. | A `git.properties` resource. | +|`java` |[“JavaInfoContributor”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/JavaInfoContributor.java)| Exposes Java runtime information. | None. | + +是否启用单个贡献者由其`management.info..enabled`属性控制。不同的贡献者对此属性有不同的默认值,这取决于他们的先决条件和他们公开的信息的性质。 + +由于没有先决条件来表明应该启用它们,`env`和`java`贡献者默认情况下是禁用的。你可以通过将`management.info.env.enabled`或`management.info.java.enabled`属性设置为`true`来启用它们。 + +默认情况下启用`build`和`git`信息贡献者。可以通过将其`management.info..enabled`属性设置为`false`来禁用每个属性。或者,要禁用每个默认情况下通常启用的贡献者,请将`management.info.defaults.enabled`属性设置为`false`。 + +#### 2.10.2.自定义应用程序信息 + +当启用`env`Contributor 时,可以通过设置`info.*` Spring 属性来定制由`info`端点公开的数据。在`info`键下的所有`Environment`属性都会自动公开。例如,你可以向`application.properties`文件添加以下设置: + +属性 + +``` +info.app.encoding=UTF-8 +info.app.java.source=11 +info.app.java.target=11 +``` + +Yaml + +``` +info: + app: + encoding: "UTF-8" + java: + source: "11" + target: "11" +``` + +| |而不是硬编码这些值,你也可以[在构建时展开信息属性](howto.html#howto.properties-and-configuration.expand-properties),

假设你使用 Maven,你可以将前面的示例改写如下:

属性

``
[电子邮件受保护]@
[电子邮件受保护]@
<
<>><<<<>>>>><<590">>>><<<<>>>>>>>><<<<<>>>>>>>>>><<<<<<<<>>>>>>>>>>>>>>>><<<<<<| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.10.3.Git 提交信息 + +`info`端点的另一个有用的特性是,它能够在构建项目时发布有关你的`git`源代码库状态的信息。如果`Git属性` Bean 是可用的,则可以使用`info`端点来公开这些属性。 + +| |如果`Git属性` Bean 的根目录下有`git.properties`文件,则`Git属性`是自动配置的。
有关更多详细信息,请参见“[如何生成 Git 信息](howto.html#howto.build.generate-git-info)”。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,端点公开`git.branch`、`git.commit.id`和`git.commit.time`属性(如果存在的话)。如果不希望在端点响应中包含任何这些属性,则需要将它们从`git.properties`文件中排除。如果要显示完整的 Git 信息(即`git.properties`的完整内容),请使用`management.info.git.mode`属性,如下所示: + +属性 + +``` +management.info.git.mode=full +``` + +Yaml + +``` +management: + info: + git: + mode: "full" +``` + +要完全禁用来自`info`端点的 Git 提交信息,请将`management.info.git.enabled`属性设置为`false`,如下所示: + +属性 + +``` +management.info.git.enabled=false +``` + +Yaml + +``` +management: + info: + git: + enabled: false +``` + +#### 2.10.4.建立信息 + +如果`Build属性` Bean 是可用的,那么`info`端点也可以发布有关你的构建的信息。如果`META-INF/build-info.properties`文件在 Classpath 中可用,就会发生这种情况。 + +| |Maven 和 Gradle 插件都可以生成该文件。
有关更多详细信息,请参见“[如何生成构建信息](howto.html#howto.build.generate-info)”。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.10.5.Java 信息 + +`info`端点发布有关你的 Java 运行时环境的信息,有关更多详细信息,请参见[`JavaInfo`](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/info/JavaInfo.html)。 + +#### 2.10.6.编写自定义信息贡献者 + +要提供自定义应用程序信息,你可以注册实现[“信息贡献者”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/info/InfoContributor.java)接口的 Spring bean。 + +下面的示例以单个值贡献一个`example`条目: + +``` +import java.util.Collections; + +import org.springframework.boot.actuate.info.Info; +import org.springframework.boot.actuate.info.InfoContributor; +import org.springframework.stereotype.Component; + +@Component +public class MyInfoContributor implements InfoContributor { + + @Override + public void contribute(Info.Builder builder) { + builder.withDetail("example", Collections.singletonMap("key", "value")); + } + +} + +``` + +如果你到达`info`端点,你应该会看到一个包含以下附加条目的响应: + +``` +{ + "example": { + "key" : "value" + } +} +``` + +## 3. 基于 HTTP 的监控和管理 + +如果你正在开发一个 Web 应用程序, Spring Boot Actuator 会自动配置所有启用的端点,以便通过 HTTP 公开。默认的约定是使用前缀`/actuator`的端点的`id`作为 URL 路径。例如,`health`被暴露为`/actuator/health`。 + +| |Spring MVC、 Spring WebFlux 和 Jersey 原生地支持致动器。如果 Jersey 和 Spring MVC 都可用,则使用 Spring MVC。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Jackson 是一个必需的依赖项,以便获得 API 文档中记录的正确的 JSON 响应([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/htmlsingle)或[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/pdf/spring-boot-actuator-web-api.pdf))。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.1.自定义管理端点路径 ### + +有时,定制管理端点的前缀是有用的。例如,你的应用程序可能已经将`/actuator`用于其他目的。可以使用`management.endpoints.web.base-path`属性更改管理端点的前缀,如下例所示: + +属性 + +``` +management.endpoints.web.base-path=/manage +``` + +Yaml + +``` +management: + endpoints: + web: + base-path: "/manage" +``` + +前面的`application.properties`示例将端点从`/actuator/{id}`更改为`/manage/{id}`(例如,`/manage/info`)。 + +| |除非管理端口已被配置为[使用不同的 HTTP 端口公开端点](#actuator.monitoring.customizing-management-server-port),否则`management.endpoints.web.base-path`相对于`server.servlet.context-path`(对于 Servlet Web 应用程序)或`spring.webflux.base-path`(对于反应性 Web 应用程序)。
如果配置了`management.endpoints.web.base-path`,则`management.endpoints.web.base-path`相对于`management.server.base-path`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果希望将端点映射到不同的路径,可以使用`management.endpoints.web.path-mapping`属性。 + +下面的示例将`/actuator/health`重新映射到`/healthcheck`: + +属性 + +``` +management.endpoints.web.base-path=/ +management.endpoints.web.path-mapping.health=healthcheck +``` + +Yaml + +``` +management: + endpoints: + web: + base-path: "/" + path-mapping: + health: "healthcheck" +``` + +### 3.2.自定义管理服务器端口 + +对于基于云的部署,使用默认的 HTTP 端口公开管理端点是一个明智的选择。但是,如果你的应用程序在你自己的数据中心内运行,那么你可能更喜欢使用不同的 HTTP 端口来公开端点。 + +你可以设置`management.server.port`属性来更改 HTTP 端口,如下例所示: + +属性 + +``` +management.server.port=8081 +``` + +Yaml + +``` +management: + server: + port: 8081 +``` + +| |在 Cloud Foundry 上,默认情况下,应用程序仅在端口 8080 上接收用于 HTTP 和 TCP 路由的请求。
如果你想在 Cloud Foundry 上使用自定义管理端口,则需要显式地设置应用程序的路由以将流量转发到自定义端口。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.3.配置特定于管理的 SSL + +当配置为使用自定义端口时,你还可以通过使用各种`management.server.ssl.*`属性来配置具有自己的 SSL 的管理服务器。例如,这样做可以让管理服务器在 HTTP 上可用,而主应用程序使用 HTTPS,如下列属性设置所示: + +属性 + +``` +server.port=8443 +server.ssl.enabled=true +server.ssl.key-store=classpath:store.jks +server.ssl.key-password=secret +management.server.port=8080 +management.server.ssl.enabled=false +``` + +Yaml + +``` +server: + port: 8443 + ssl: + enabled: true + key-store: "classpath:store.jks" + key-password: "secret" +management: + server: + port: 8080 + ssl: + enabled: false +``` + +或者,主服务器和管理服务器都可以使用 SSL,但使用不同的密钥存储,如下所示: + +属性 + +``` +server.port=8443 +server.ssl.enabled=true +server.ssl.key-store=classpath:main.jks +server.ssl.key-password=secret +management.server.port=8080 +management.server.ssl.enabled=true +management.server.ssl.key-store=classpath:management.jks +management.server.ssl.key-password=secret +``` + +Yaml + +``` +server: + port: 8443 + ssl: + enabled: true + key-store: "classpath:main.jks" + key-password: "secret" +management: + server: + port: 8080 + ssl: + enabled: true + key-store: "classpath:management.jks" + key-password: "secret" +``` + +### 3.4.自定义管理服务器地址 + +你可以通过设置`management.server.address`属性来定制管理端点可用的地址。如果你只想监听内部或面向 Ops 的网络,或者只监听来自`localhost`的连接,那么这样做是有用的。 + +| |只有当端口与主服务器端口不同时,你才可以在不同的地址上进行侦听。| +|---|-------------------------------------------------------------------------------------------| + +以下示例`application.properties`不允许远程管理连接: + +属性 + +``` +management.server.port=8081 +management.server.address=127.0.0.1 +``` + +Yaml + +``` +management: + server: + port: 8081 + address: "127.0.0.1" +``` + +### 3.5.禁用 HTTP 端点 + +如果不想在 HTTP 上公开端点,可以将管理端口设置为`-1`,如下例所示: + +属性 + +``` +management.server.port=-1 +``` + +Yaml + +``` +management: + server: + port: -1 +``` + +你也可以通过使用`management.endpoints.web.exposure.exclude`属性来实现这一点,如下例所示: + +属性 + +``` +management.endpoints.web.exposure.exclude=* +``` + +Yaml + +``` +management: + endpoints: + web: + exposure: + exclude: "*" +``` + +## 4. JMX 的监控与管理 + +Java 管理扩展提供了一种标准的机制来监视和管理应用程序。默认情况下,不启用此功能。你可以通过将`spring.jmx.enabled`配置属性设置为`true`来打开它。 Spring 引导将最合适的`MBeanServer`公开为 ID 为`mbeanServer`的 Bean。你的任何带有 Spring JMX 注释(@managedresource`,`@ManagedAttribute`,或`@ManagedOperation`)的 bean 都会暴露给它。 + +如果你的平台提供了一个标准的`MBeanServer`, Spring boot 将使用它,并在必要时默认为 VM`MBeanServer`。如果所有这些都失败了,将创建一个新的`MBeanServer`。 + +有关更多详细信息,请参见[JMXAutoConfiguration’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java)类。 + +默认情况下, Spring 引导还将管理端点作为`org.springframework.boot`域下的 JMXMBeans 公开。要完全控制 JMX 域中的端点注册,请考虑注册你自己的`EndpointObjectNameFactory`实现。 + +### 4.1.自定义 MBean 名称 + +MBean 的名称通常是从端点的`id`生成的。例如,将`health`端点暴露为`org.springframework.boot:type=Endpoint,name=Health`。 + +如果你的应用程序包含多个 Spring `ApplicationContext`,则可能会发现名称冲突。要解决此问题,可以将`spring.jmx.unique-names`属性设置为`true`,这样 MBean 名称就始终是唯一的。 + +你还可以自定义公开端点的 JMX 域。以下设置在`application.properties`中显示了这样做的示例: + +属性 + +``` +spring.jmx.unique-names=true +management.endpoints.jmx.domain=com.example.myapp +``` + +Yaml + +``` +spring: + jmx: + unique-names: true +management: + endpoints: + jmx: + domain: "com.example.myapp" +``` + +### 4.2.禁用 JMX 端点 + +如果不想在 JMX 上公开端点,可以将`management.endpoints.jmx.exposure.exclude`属性设置为`*`,如下例所示: + +属性 + +``` +management.endpoints.jmx.exposure.exclude=* +``` + +Yaml + +``` +management: + endpoints: + jmx: + exposure: + exclude: "*" +``` + +### 4.3.在 HTTP 上使用 Jolokia 进行 JMX + +Jolokia 是一个 JMX-HTTP 桥,它提供了一种访问 JMX Bean 的替代方法。若要使用 Jolokia,请包含对`org.jolokia:jolokia-core`的依赖关系。例如,在 Maven 中,你将添加以下依赖项: + +``` + + org.jolokia + jolokia-core + +``` + +然后,你可以通过向`management.endpoints.web.exposure.include`属性添加`jolokia`或`*`来公开 Jolokia 端点。然后,你可以使用管理 HTTP 服务器上的`/actuator/jolokia`访问它。 + +| |Jolokia 端点将 Jolokia 的 Servlet 公开为执行器端点。结果,它是特定于 Servlet 环境的,例如 Spring MVC 和 Jersey。端点在 WebFlux 应用程序中是不可用的。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 4.3.1.定制 Jolokia + +Jolokia 有许多设置,你通常会通过设置 Servlet 参数来配置这些设置。通过 Spring 引导,你可以使用`application.properties`文件。要这样做,请在参数前缀`management.endpoint.jolokia.config.`,如下例所示: + +属性 + +``` +management.endpoint.jolokia.config.debug=true +``` + +Yaml + +``` +management: + endpoint: + jolokia: + config: + debug: true +``` + +#### 4.3.2.禁用 Jolokia + +如果你使用 Jolokia,但不希望 Spring Boot 对其进行配置,请将`management.endpoint.jolokia.enabled`属性设置为`false`,如下所示: + +属性 + +``` +management.endpoint.jolokia.enabled=false +``` + +Yaml + +``` +management: + endpoint: + jolokia: + enabled: false +``` + +## 5. 伐木工 + +Spring 启动执行器包括在运行时查看和配置应用程序的日志级别的能力。你可以查看整个列表,也可以查看单个日志程序的配置,该配置由显式配置的日志级别以及日志框架赋予它的有效日志级别组成。这些级别可以是: + +* `TRACE` + +* `DEBUG` + +* `INFO` + +* `WARN` + +* `ERROR` + +* `FATAL` + +* `OFF` + +* `null` + +`null`表示没有显式配置。 + +### 5.1.配置记录器 + +要配置给定的记录器,`POST`将一个部分实体配置到资源的 URI,如下例所示: + +``` +{ + "configuredLevel": "DEBUG" +} +``` + +| |要“重置”记录器的特定级别(并使用默认配置),你可以将值`null`传递为`configuredLevel`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------| + +## 6. 度量 + +Spring Boot Actuator 为[Micrometer](https://micrometer.io)提供依赖管理和自动配置,这是一个支持[众多的监测系统](https://micrometer.io/docs)的应用程序度量外观,包括: + +* [AppOptics](#actuator.metrics.export.appoptics) + +* [Atlas](#actuator.metrics.export.atlas) + +* [Datadog](#actuator.metrics.export.datadog) + +* [Dynatrace](#actuator.metrics.export.dynatrace) + +* [Elastic](#actuator.metrics.export.elastic) + +* [Ganglia](#actuator.metrics.export.ganglia) + +* [Graphite](#actuator.metrics.export.graphite) + +* [Humio](#actuator.metrics.export.humio) + +* [Influx](#actuator.metrics.export.influx) + +* [JMX](#actuator.metrics.export.jmx) + +* [KairosDB](#actuator.metrics.export.kairos) + +* [New Relic](#actuator.metrics.export.newrelic) + +* [Prometheus](#actuator.metrics.export.prometheus) + +* [SignalFx](#actuator.metrics.export.signalfx) + +* [简单(内存中)](#actuator.metrics.export.simple) + +* [Stackdriver](#actuator.metrics.export.stackdriver) + +* [StatsD](#actuator.metrics.export.statsd) + +* [Wavefront](#actuator.metrics.export.wavefront) + +| |要了解有关千分尺功能的更多信息,请参见其[参考文献](https://micrometer.io/docs),特别是[概念部分](https://micrometer.io/docs/concepts)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 6.1.开始 + +Spring 引导自动配置组合`MeterRegistry`,并为其在 Classpath 上找到的每个受支持的实现向组合添加注册表。在运行时 Classpath 中对`micrometer-registry-{system}`具有依赖关系就足以使 Spring 引导配置注册表。 + +大多数注册中心都有共同的特征。例如,可以禁用特定的注册中心,即使在 Classpath 上实现了微米注册中心。以下示例禁用 Datadog: + +属性 + +``` +management.metrics.export.datadog.enabled=false +``` + +Yaml + +``` +management: + metrics: + export: + datadog: + enabled: false +``` + +除非特定于注册中心的属性另有说明,否则你也可以禁用所有注册中心,如下例所示: + +属性 + +``` +management.metrics.export.defaults.enabled=false +``` + +Yaml + +``` +management: + metrics: + export: + defaults: + enabled: false +``` + +Spring Boot 还将任何自动配置的注册中心添加到`Metrics`类上的全局静态复合注册中心,除非你明确地告诉它不要: + +Properties + +``` +management.metrics.use-global-registry=false +``` + +Yaml + +``` +management: + metrics: + use-global-registry: false +``` + +你可以注册任意数量的`MeterRegistryCustomizer`bean,以进一步配置注册表,例如在向注册表注册任何米之前应用公共标记: + +``` +import io.micrometer.core.instrument.MeterRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyMeterRegistryConfiguration { + + @Bean + public MeterRegistryCustomizer metricsCommonTags() { + return (registry) -> registry.config().commonTags("region", "us-east-1"); + } + +} + +``` + +你可以通过更具体地说明泛型类型来对特定的注册中心实现应用自定义: + +``` +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.config.NamingConvention; +import io.micrometer.graphite.GraphiteMeterRegistry; + +import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyMeterRegistryConfiguration { + + @Bean + public MeterRegistryCustomizer graphiteMetricsNamingConvention() { + return (registry) -> registry.config().namingConvention(this::name); + } + + private String name(String name, Meter.Type type, String baseUnit) { + return ... + } + +} + +``` + +Spring 还可以启动[配置内置的仪表](#actuator.metrics.supported),你可以通过配置或专用的注释标记对其进行控制。 + +### 6.2.支持的监测系统 + +这一节简要介绍了每个支持的监控系统。 + +#### 6.2.1.APPATICS + +默认情况下,AppOptics 注册中心会定期将指标推到`[api.appoptics.com/v1/measurements](https://api.appoptics.com/v1/measurements)`。要将度量导出到 SaaS[AppOptics](https://micrometer.io/docs/registry/appOptics),必须提供你的 API 令牌: + +Properties + +``` +management.metrics.export.appoptics.api-token=YOUR_TOKEN +``` + +Yaml + +``` +management: + metrics: + export: + appoptics: + api-token: "YOUR_TOKEN" +``` + +#### 6.2.2.阿特拉斯 + +默认情况下,指标导出到在本地机器上运行的[Atlas](https://micrometer.io/docs/registry/atlas)。你可以提供[Atlas server](https://github.com/Netflix/atlas)的位置: + +Properties + +``` +management.metrics.export.atlas.uri=https://atlas.example.com:7101/api/v1/publish +``` + +Yaml + +``` +management: + metrics: + export: + atlas: + uri: "https://atlas.example.com:7101/api/v1/publish" +``` + +#### 6.2.3.Datadog + +Datadog 注册中心定期将指标推到[datadoghq](https://www.datadoghq.com)。要将指标导出到[Datadog](https://micrometer.io/docs/registry/datadog),你必须提供你的 API 键: + +Properties + +``` +management.metrics.export.datadog.api-key=YOUR_KEY +``` + +Yaml + +``` +management: + metrics: + export: + datadog: + api-key: "YOUR_KEY" +``` + +你还可以更改将指标发送到 Datadog 的时间间隔: + +Properties + +``` +management.metrics.export.datadog.step=30s +``` + +Yaml + +``` +management: + metrics: + export: + datadog: + step: "30s" +``` + +#### 6.2.4.Dynatrace + +Dynatrace 提供了两个吸收 API 的指标,这两个指标都是为[Micrometer](https://micrometer.io/docs/registry/dynatrace)实现的。`v1`名称空间中的配置属性仅在导出到[TimeSeries V1API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/)时才应用。`v2`名称空间中的配置属性仅在导出到[Metrics V2API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/)时才应用。请注意,这种集成一次只能导出到 API 的`v1`或`v2`版本。如果`device-id`(v1 需要,但在 v2 中不使用)在`v1`命名空间中设置,则将度量导出到`v1`端点。否则,假定`v2`。 + +##### v2 API + +你可以通过两种方式使用 V2API。 + +如果本地 OneAgent 在主机上运行,则度量指标将自动导出到[本地一个代理摄取端点](https://www.dynatrace.com/support/help/how-to-use-dynatrace/metrics/metric-ingestion/ingestion-methods/local-api/)。摄取端点将指标转发到 Dynatrace 后端。这是默认的行为,除了对`io.micrometer:micrometer-registry-dynatrace`的依赖外,不需要特殊的设置。 + +如果没有运行本地 OneAgent,则需要[Metrics v2 API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v2/post-ingest-metrics/)的端点和一个 API 令牌。[API token](https://www.dynatrace.com/support/help/dynatrace-api/basics/dynatrace-api-authentication/)必须具有“ingest metrics”(“metrics.ingest”)权限集。我们建议将令牌的范围限制为这个权限。你必须确保端点 URI 包含以下路径(例如,`/api/v2/metrics/ingest`): + +根据你的部署选项,Metrics API v2Ingest 端点的 URL 是不同的: + +* SaaS:`https://{your-environment-id}.live.dynatrace.com/api/v2/metrics/ingest` + +* 托管部署:`https://{your-domain}/e/{your-environment-id}/api/v2/metrics/ingest` + +下面的示例使用`example`环境 ID 配置指标导出: + +Properties + +``` +management.metrics.export.dynatrace.uri=https://example.live.dynatrace.com/api/v2/metrics/ingest +management.metrics.export.dynatrace.api-token=YOUR_TOKEN +``` + +Yaml + +``` +management: + metrics: + export: + dynatrace: + uri: "https://example.live.dynatrace.com/api/v2/metrics/ingest" + api-token: "YOUR_TOKEN" +``` + +在使用 Dynatrace V2API 时,可以使用以下可选特性: + +* 公制密钥前缀:设置一个前缀,该前缀用于所有导出的公制密钥。 + +* 丰富 Dynatrace 元数据:如果正在运行一个 Agent 或 Dynatrace 操作符,则使用额外的元数据(例如,关于主机、进程或 POD)丰富度量。 + +* 默认维度:指定添加到所有导出度量中的键-值对。如果使用 Micrometer 指定了具有相同键值的标记,则它们会覆盖默认的尺寸。 + +可以不指定 URI 和 API 令牌,如下面的示例所示。在此场景中,使用本地 OneAgent 端点: + +Properties + +``` +management.metrics.export.dynatrace.v2.metric-key-prefix=your.key.prefix +management.metrics.export.dynatrace.v2.enrich-with-dynatrace-metadata=true +management.metrics.export.dynatrace.v2.default-dimensions.key1=value1 +management.metrics.export.dynatrace.v2.default-dimensions.key2=value2 +``` + +Yaml + +``` +management: + metrics: + export: + dynatrace: + # Specify uri and api-token here if not using the local OneAgent endpoint. + v2: + metric-key-prefix: "your.key.prefix" + enrich-with-dynatrace-metadata: true + default-dimensions: + key1: "value1" + key2: "value2" +``` + +##### V1API(旧版) + +Dynatrace V1API metrics 注册中心通过使用[TimeSeries V1API](https://www.dynatrace.com/support/help/dynatrace-api/environment-api/metric-v1/)定期将指标推送到配置的 URI。对于与现有设置的向后兼容性,当设置`device-id`时(对于 V1 是必需的,但在 V2 中不使用),度量值将导出到 TimeSeries V1 端点。要将指标导出到[Dynatrace](https://micrometer.io/docs/registry/dynatrace),必须提供你的 API 令牌、设备 ID 和 URI: + +属性 + +``` +management.metrics.export.dynatrace.uri=https://{your-environment-id}.live.dynatrace.com +management.metrics.export.dynatrace.api-token=YOUR_TOKEN +management.metrics.export.dynatrace.v1.device-id=YOUR_DEVICE_ID +``` + +Yaml + +``` +management: + metrics: + export: + dynatrace: + uri: "https://{your-environment-id}.live.dynatrace.com" + api-token: "YOUR_TOKEN" + v1: + device-id: "YOUR_DEVICE_ID" +``` + +对于 V1API,你必须指定没有路径的基本环境 URI,因为 V1 端点路径是自动添加的。 + +##### 与版本无关的设置 + +除了 API 端点和令牌,你还可以更改将指标发送到 Dynatrace 的时间间隔。默认的导出间隔是`60s`。以下示例将导出间隔设置为 30 秒: + +属性 + +``` +management.metrics.export.dynatrace.step=30s +``` + +Yaml + +``` +management: + metrics: + export: + dynatrace: + step: "30s" +``` + +你可以在[千分尺文档](https://micrometer.io/docs/registry/dynatrace)中找到有关如何设置 Dynatrace 千分尺出口商的更多信息。 + +#### 6.2.5.弹性 + +默认情况下,指标导出到在本地机器上运行的[Elastic](https://micrometer.io/docs/registry/elastic)。可以使用以下属性提供要使用的 Elastic 服务器的位置: + +属性 + +``` +management.metrics.export.elastic.host=https://elastic.example.com:8086 +``` + +Yaml + +``` +management: + metrics: + export: + elastic: + host: "https://elastic.example.com:8086" +``` + +#### 6.2.6.神经节 + +默认情况下,指标导出到在本地机器上运行的[Ganglia](https://micrometer.io/docs/registry/ganglia)。你可以提供[Ganglia server](http://ganglia.sourceforge.net)主机和端口,如下例所示: + +属性 + +``` +management.metrics.export.ganglia.host=ganglia.example.com +management.metrics.export.ganglia.port=9649 +``` + +Yaml + +``` +management: + metrics: + export: + ganglia: + host: "ganglia.example.com" + port: 9649 +``` + +#### 6.2.7.石墨 + +默认情况下,指标导出到在本地机器上运行的[Graphite](https://micrometer.io/docs/registry/graphite)。你可以提供[Graphite server](https://graphiteapp.org)主机和端口,如下例所示: + +属性 + +``` +management.metrics.export.graphite.host=graphite.example.com +management.metrics.export.graphite.port=9004 +``` + +Yaml + +``` +management: + metrics: + export: + graphite: + host: "graphite.example.com" + port: 9004 +``` + +Micrometer 提供了一个默认的`HierarchicalNameMapper`,它控制一个维度表 ID 是[映射到平面层次结构名称](https://micrometer.io/docs/registry/graphite#_hierarchical_name_mapping)的方式。 + +| |来控制这种行为,定义你的`GraphiteMeterRegistry`并提供你自己的`HierarchicalNameMapper`。
自动配置的`GraphiteConfig`而`Clock`bean 是提供的,除非你自己定义:

``
Import io.Micrometer.Core.Instrument.Clock;
Import io.Micrometer.Core.Core.Instrument.Config.NamingConvention;Import778.Micrometer.Micrometer.Core.Core.Core.Core.Core.Importor.ArchalNameMaMaMaMaMaMaMe;返回新的 GraphiteMeterRegistry(配置,时钟,this::TohierarchicalName);


私有字符串 TohierarchicalName{
return.....
<”<<<<<>>| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.2.8.Humio + +默认情况下,Humio 注册中心会定期将指标推到[cloud.humio.com](https://cloud.humio.com)。要将指标导出到 SaaS[Humio](https://micrometer.io/docs/registry/humio),你必须提供你的 API 令牌: + +属性 + +``` +management.metrics.export.humio.api-token=YOUR_TOKEN +``` + +Yaml + +``` +management: + metrics: + export: + humio: + api-token: "YOUR_TOKEN" +``` + +你还应该配置一个或多个标记,以标识将指标推送到的数据源: + +属性 + +``` +management.metrics.export.humio.tags.alpha=a +management.metrics.export.humio.tags.bravo=b +``` + +Yaml + +``` +management: + metrics: + export: + humio: + tags: + alpha: "a" + bravo: "b" +``` + +#### 6.2.9.涌入 + +默认情况下,指标将导出到使用默认配置在本地计算机上运行的[Influx](https://micrometer.io/docs/registry/influx)V1 实例。要将指标导出到 InfluxDB V2,请配置`org`、`bucket`以及用于编写指标的身份验证`token`。你可以通过以下方式提供要使用的[Influx server](https://www.influxdata.com)的位置: + +属性 + +``` +management.metrics.export.influx.uri=https://influx.example.com:8086 +``` + +Yaml + +``` +management: + metrics: + export: + influx: + uri: "https://influx.example.com:8086" +``` + +#### 6.2.10.JMX + +Micrometer 提供了到[JMX](https://micrometer.io/docs/registry/jmx)的层次映射,主要是作为一种本地查看度量的廉价且便携的方式。默认情况下,指标导出到`metrics`JMX 域。你可以通过以下方式提供要使用的域: + +属性 + +``` +management.metrics.export.jmx.domain=com.example.app.metrics +``` + +Yaml + +``` +management: + metrics: + export: + jmx: + domain: "com.example.app.metrics" +``` + +Micrometer 提供了一个默认的`HierarchicalNameMapper`,它控制着维度表 ID 的大小[映射到平面层次结构名称](https://micrometer.io/docs/registry/jmx#_hierarchical_name_mapping)。 + +| |来控制这种行为,定义你的`JmxMeterRegistry`并提供你自己的`HierarchicalNameMapper`。
自动配置的`JmxConfig`而`Clock`bean 是提供的,除非你自己定义:

``
Import io.Micrometer.Core.Instrument.Clock;
Import io.Micrometer.Core.Instrument.Meter; + io.prometheus + simpleclient_pushgateway + +``` + +当 Classpath 上存在 Prometheus PushGateway 依赖项并且`management.metrics.export.prometheus.pushgateway.enabled`属性设置为`true`时,将自动配置`PrometheusPushGatewayManager` Bean。这管理了将指标推送到 Prometheus PushGateway 的过程。 + +你可以通过使用`management.metrics.export.prometheus.pushgateway`下的属性来调优`PrometheusPushGatewayManager`。对于高级配置,还可以提供自己的`PrometheusPushGatewayManager` Bean。 + +#### 6.2.14.SignalFX + +SignalFX Registry 定期将指标推到[SignalFx](https://micrometer.io/docs/registry/signalFx)。要将指标导出到[SignalFx](https://www.signalfx.com),你必须提供访问令牌: + +属性 + +``` +management.metrics.export.signalfx.access-token=YOUR_ACCESS_TOKEN +``` + +Yaml + +``` +management: + metrics: + export: + signalfx: + access-token: "YOUR_ACCESS_TOKEN" +``` + +你还可以更改将指标发送到 SignalFX 的间隔时间: + +属性 + +``` +management.metrics.export.signalfx.step=30s +``` + +Yaml + +``` +management: + metrics: + export: + signalfx: + step: "30s" +``` + +#### 6.2.15.简单 + +Micrometer 附带了一个简单的内存后端,如果没有配置其他注册中心,该后端将自动用作后备。这让你看到在[指标端点](#actuator.metrics.endpoint)中收集了哪些指标。 + +一旦你使用任何其他可用的后端,内存中的后端就会禁用它自己。你还可以显式地禁用它: + +属性 + +``` +management.metrics.export.simple.enabled=false +``` + +Yaml + +``` +management: + metrics: + export: + simple: + enabled: false +``` + +#### 6.2.16.Stackdriver + +StackDriver 注册表定期将指标推到[Stackdriver](https://cloud.google.com/stackdriver/)。要将指标导出到 SaaS[Stackdriver](https://micrometer.io/docs/registry/stackdriver),你必须提供你的 Google Cloud 项目 ID: + +属性 + +``` +management.metrics.export.stackdriver.project-id=my-project +``` + +Yaml + +``` +management: + metrics: + export: + stackdriver: + project-id: "my-project" +``` + +你还可以更改将指标发送到 StackDriver 的间隔时间: + +属性 + +``` +management.metrics.export.stackdriver.step=30s +``` + +Yaml + +``` +management: + metrics: + export: + stackdriver: + step: "30s" +``` + +#### 6.2.17.统计 d + +STATSD 注册中心急切地将 UDP 上的指标推送到 STATSD 代理。默认情况下,指标被导出到在本地机器上运行的[StatsD](https://micrometer.io/docs/registry/statsD)代理。你可以使用以下方法提供要使用的 STATSD 代理主机、端口和协议: + +属性 + +``` +management.metrics.export.statsd.host=statsd.example.com +management.metrics.export.statsd.port=9125 +management.metrics.export.statsd.protocol=udp +``` + +Yaml + +``` +management: + metrics: + export: + statsd: + host: "statsd.example.com" + port: 9125 + protocol: "udp" +``` + +你也可以将 STATSD 行协议更改为使用(默认为 Datadog): + +属性 + +``` +management.metrics.export.statsd.flavor=etsy +``` + +Yaml + +``` +management: + metrics: + export: + statsd: + flavor: "etsy" +``` + +#### 6.2.18.波阵面 + +Wavefront 注册中心定期将指标推到[Wavefront](https://micrometer.io/docs/registry/wavefront)。如果要直接将指标导出到[Wavefront](https://www.wavefront.com/),则必须提供你的 API 令牌: + +属性 + +``` +management.metrics.export.wavefront.api-token=YOUR_API_TOKEN +``` + +Yaml + +``` +management: + metrics: + export: + wavefront: + api-token: "YOUR_API_TOKEN" +``` + +或者,你可以在你的环境中使用 Wavefront 侧车或内部代理将度量数据转发到 Wavefront API 主机: + +属性 + +``` +management.metrics.export.wavefront.uri=proxy://localhost:2878 +``` + +Yaml + +``` +management: + metrics: + export: + wavefront: + uri: "proxy://localhost:2878" +``` + +| |如果将指标发布到 Wavefront 代理(如[Wavefront 文档](https://docs.wavefront.com/proxies_installing.html)中所述),则主机必须为`proxy://HOST:PORT`格式。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你还可以更改将指标发送到 Wavefront 的间隔时间: + +属性 + +``` +management.metrics.export.wavefront.step=30s +``` + +Yaml + +``` +management: + metrics: + export: + wavefront: + step: "30s" +``` + +### 6.3.支持的度量和计量器 + +Spring Boot 为各种各样的技术提供了自动仪表注册。在大多数情况下,默认值提供了合理的度量,可以将其发布到任何受支持的监视系统。 + +#### 6.3.1.JVM 度量 + +自动配置通过使用核心 Micrometer 类来实现 JVM 度量。JVM 度量以`jvm.`度量名称发布。 + +提供了以下 JVM 指标: + +* 各种内存和缓冲池详细信息 + +* 与垃圾收集相关的统计信息 + +* 线程利用率 + +* 加载和卸载的类的数量 + +#### 6.3.2.系统度量 + +自动配置通过使用核心微米表类来实现系统度量。系统度量在`system.`、`process.`和`disk.`表名称下发布。 + +提供了以下系统指标: + +* CPU 度量 + +* 文件描述符度量 + +* 正常运行时间指标(应用程序运行的时间和绝对启动时间的固定指标) + +* 可用磁盘空间 + +#### 6.3.3.应用程序启动度量 + +自动配置公开应用程序启动时间指标: + +* `application.started.time`:启动应用程序所需的时间。 + +* `application.ready.time`:应用程序准备好服务请求所需的时间。 + +度量由应用程序类的完全限定名称标记。 + +#### 6.3.4.记录器指标 + +自动配置可以为 logback 和 log4j2 启用事件度量。详细信息在`log4j2.events.`或`logback.events.`表名称下发布。 + +#### 6.3.5.任务执行和调度指标 + +自动配置允许对所有可用的`ThreadPoolTaskExecutor`和`ThreadPoolTaskScheduler`bean 进行插装,只要下面的`ThreadPoolExecutor`可用。度量由执行器的名称标记,该名称来自 Bean 名称。 + +#### 6.3.6. Spring MVC 度量 + +自动配置允许检测由 Spring MVC 控制器和功能处理程序处理的所有请求。默认情况下,度量值以`http.server.requests`的名称生成。你可以通过设置`management.metrics.web.server.request.metric-name`属性来定制名称。 + +`@Timed`批注在`@Controller`类和`@RequestMapping`方法上都是支持的(详见[@ 定时注释支持](#actuator.metrics.supported.timed-annotation))。如果不想记录所有 Spring MVC 请求的度量,可以将`management.metrics.web.server.request.autotime.enabled`设置为`false`,并专门使用`@Timed`注释。 + +默认情况下, Spring MVC 相关度量标记有以下信息: + +| Tag |说明| +|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`exception`|处理请求时引发的任何异常的简单类名。| +| `method` |请求的方法(例如,`GET`或`POST`)| +| `outcome` |请求的结果,基于响应的状态码。
1xx 是`INFORMATIONAL`,2xx 是`SUCCESS`,3xx 是`REDIRECTION`,4xx 是`CLIENT_ERROR`,5xx 是`SERVER_ERROR`| +| `status` |响应的 HTTP 状态代码(例如,`200`或`500`)| +| `uri` |如果可能的话,在变量替换之前提供请求的 URI 模板(例如,`/api/person/{id}`)| + +要添加到默认标记中,请提供一个或多个实现`@Bean`的`WebMvcTagsContributor`s。要替换默认标记,请提供一个实现`WebMvcTagsProvider`的`@Bean`。 + +| |在某些情况下,在 Web 控制器中处理的异常不被记录为请求度量标记。
应用程序可以通过[将已处理的异常设置为请求属性](web.html#web.servlet.spring-mvc.error-handling)OPT 并记录异常。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.3.7. Spring WebFlux 度量 + +自动配置允许检测由 Spring WebFlux 控制器和功能处理程序处理的所有请求。默认情况下,度量值以`http.server.requests`的名称生成。你可以通过设置`management.metrics.web.server.request.metric-name`属性来定制名称。 + +`@Timed`在`@Controller`类和`@RequestMapping`方法上支持`@Timed`注释(有关详细信息,请参见[@ 定时注释支持](#actuator.metrics.supported.timed-annotation))。如果不想记录所有 Spring WebFlux 请求的度量,则可以将`management.metrics.web.server.request.autotime.enabled`设置为`false`,并专门使用`@Timed`注释。 + +默认情况下,WebFlux 相关度量标记有以下信息: + +| Tag |说明| +|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`exception`|处理请求时引发的任何异常的简单类名。| +| `method` |请求的方法(例如,`GET`或`POST`)| +| `outcome` |请求的结果,基于响应的状态代码。
1xx 是`INFORMATIONAL`,2xx 是`SUCCESS`,3xx 是`REDIRECTION`,4xx 是`CLIENT_ERROR`,5xx 是`SERVER_ERROR`| +| `status` |响应的 HTTP 状态代码(例如,`200`或`500`)| +| `uri` |如果可能的话,在变量替换之前提供请求的 URI 模板(例如,`/api/person/{id}`)| + +要添加到默认标记中,请提供一个或多个实现`WebFluxTagsContributor`的 bean。要替换默认标记,请提供实现`WebFluxTagsProvider`的 Bean。 + +| |在某些情况下,在控制器和处理程序函数中处理的异常不被记录为请求度量标记。
应用程序可以通过[将已处理的异常设置为请求属性](web.html#web.reactive.webflux.error-handling)OPT 并记录异常。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.3.8.泽西服务器指标 + +自动配置支持对 Jersey JAX-RS 实现处理的所有请求进行检测。默认情况下,度量值以`http.server.requests`的名称生成。你可以通过设置`management.metrics.web.server.request.metric-name`属性来定制名称。 + +`@Timed`在请求处理类和方法上支持注释(有关详细信息,请参见[@ 定时注释支持](#actuator.metrics.supported.timed-annotation))。如果不想记录所有 Jersey 请求的度量,可以将`management.metrics.web.server.request.autotime.enabled`设置为`false`,并专门使用`@Timed`注释。 + +默认情况下,Jersey Server Metrics 使用以下信息进行标记: + +| Tag |说明| +|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`exception`|处理请求时引发的任何异常的简单类名。| +| `method` |请求的方法(例如,`GET`或`POST`)| +| `outcome` |请求的结果,基于响应的状态代码。
1xx 是`INFORMATIONAL`,2xx 是`SUCCESS`,3xx 是`REDIRECTION`,4xx 是`CLIENT_ERROR`,5xx 是`SERVER_ERROR`| +| `status` |响应的 HTTP 状态代码(例如,`200`或`500`)| +| `uri` |如果可能的话,在变量替换之前提供请求的 URI 模板(例如,`/api/person/{id}`)| + +要定制标记,请提供一个实现`@Bean`的`JerseyTagsProvider`。 + +#### 6.3.9.HTTP 客户端度量 + +Spring 引导致动器管理`RestTemplate`和`WebClient`两者的仪表。为此,你必须插入自动配置的生成器,并使用它创建实例: + +* `RestTemplateBuilder`表示`RestTemplate` + +* `WebClient.Builder`表示`WebClient` + +你还可以手动应用负责此检测的定制程序,即`MetricsRestTemplateCustomizer`和`Cache`。 + +默认情况下,度量值以`http.client.requests`的名称生成。你可以通过设置`management.metrics.web.client.request.metric-name`属性来定制名称。 + +默认情况下,由检测过的客户机生成的度量值被标记为以下信息: + +| Tag |说明| +|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`clientName`|URI 的主机部分| +| `method` |请求的方法(例如,`GET`或`POST`)| +| `outcome` |请求的结果,基于响应的状态代码。
1xx 是`INFORMATIONAL`,2xx 是`SUCCESS`,3xx 是`REDIRECTION`,4xx 是`CLIENT_ERROR`,5xx 是`SERVER_ERROR`。否则,它是`UNKNOWN`。| +| `status` |响应的 HTTP 状态代码(如果可用)(例如,`200`或`500`)或`IO_ERROR`在 I/O 问题的情况下。否则,它是`CLIENT_ERROR`。| +| `uri` |如果可能的话,在变量替换之前提供请求的 URI 模板(例如,`/api/person/{id}`)| + +为了自定义标记,并且根据你对客户机的选择,你可以提供一个`@Bean`,它实现`RestTemplateExchangeTagsProvider`或`WebClientExchangeTagsProvider`。在`RestTemplateExchangeTags`和`WebClientExchangeTags`中有方便的静态函数。 + +#### 6.3.10. Tomcat 指标 + +只有在启用`MBeanRegistry`时,自动配置才能启用 Tomcat 的插装。默认情况下,`MBeanRegistry`是禁用的,但是你可以通过将`server.tomcat.mbeanregistry.enabled`设置为`true`来启用它。 + +Tomcat 度量在`tomcat.`度量名称下发布。 + +#### 6.3.11.缓存度量 + +自动配置允许在启动时检测所有可用的`Cache`实例,其度量值前缀为`cache`。缓存检测是针对一组基本的度量标准进行标准化的。另外,特定于缓存的指标也是可用的。 + +支持以下缓存库: + +* 咖啡因 + +* Ehcache2 + +* 黑泽尔卡斯特 + +* 任何兼容的 JCache(JSR-107)实现 + +* 雷迪斯 + +度量由缓存的名称和`CacheManager`的名称标记,该名称来自 Bean 名称。 + +| |只有在启动时配置的缓存才绑定到注册中心。
对于在缓存配置中未定义的缓存,例如在运行中或在启动阶段之后以编程方式创建的缓存,需要进行显式注册。
还提供了`CacheMetricsRegistrar` Bean,以使该过程更容易。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.3.12.数据源度量 + +自动配置允许对所有可用的`DataSource`对象进行插装,这些对象的度量前缀为`jdbc.connections`。数据源插装产生的量规表示池中当前活动的、空闲的、最大允许的和最小允许的连接。 + +度量值也由基于 Bean 名称计算的`DataSource`的名称标记。 + +| |默认情况下, Spring Boot 为所有支持的数据源提供元数据。
如果不支持你喜欢的数据源,你可以添加额外的`DataSourcePoolMetadataProvider`bean。
参见`DataSourcePoolMetadataProvidersConfiguration`示例。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此外,特定于光的度量以`hikaricp`前缀公开。每个度量都由池的名称标记(你可以使用`spring.datasource.name`来控制它)。 + +#### 6.3.13. Hibernate 指标 + +如果`org.hibernate:hibernate-micrometer`在 Classpath 上,则所有启用了统计信息的可用 Hibernate `EntityManagerFactory`实例都将使用一个名为`hibernate`的度量指标进行检测。 + +度量指标还被`EntityManagerFactory`的名称标记,该名称来自 Bean 名称。 + +要启用统计信息,标准 JPA 属性`hibernate.generate_statistics`必须设置为`true`。你可以在自动配置的
上启用该功能: + +属性 + +``` +spring.jpa.properties[hibernate.generate_statistics]=true +``` + +Yaml + +``` +spring: + jpa: + properties: + "[hibernate.generate_statistics]": true +``` + +#### 6.3.14. Spring 数据存储库度量 + +自动配置使所有 Spring 数据`Repository`方法调用的检测成为可能。默认情况下,度量值以`spring.data.repository.invocations`的名称生成。你可以通过设置`management.metrics.data.repository.metric-name`属性来定制名称。 + +`@Timed`在`Repository`类和方法上支持[@ 定时注释支持](#actuator.metrics.supported.timed-annotation)注释(有关详细信息,请参见[@ 定时注释支持](#actuator.metrics.supported.timed-annotation))。如果不想记录所有`Repository`调用的度量,则可以将`management.metrics.data.repository.autotime.enabled`设置为`false`,并专门使用`@Timed`注释。 + +默认情况下,存储库调用相关的度量被标记为以下信息: + +| Tag |说明| +|------------|---------------------------------------------------------------------------| +|`repository`|源的简单类名`Repository`。| +| `method` |调用的`Repository`方法的名称。| +| `state` |结果状态(’success’,`ERROR`,`CANCELED`,或`RUNNING`)。| +|`exception` |从调用中抛出的任何异常的简单类名。| + +要替换默认标记,请提供一个实现`RepositoryTagsProvider`的`@Bean`。 + +#### 6.3.15.RabbitMQ 度量 + +自动配置允许使用名为`rabbitmq`的度量来检测所有可用的 RabbitMQ 连接工厂。 + +#### 6.3.16. Spring 集成度量 + +Spring 只要`MeterRegistry` Bean 可用,集成就会自动提供[千分尺支持](https://docs.spring.io/spring-integration/docs/5.5.9/reference/html/system-management.html#micrometer-integration)。度量以`spring.integration.`度量名称发布。 + +#### 6.3.17.卡夫卡指标 + +自动配置分别为自动配置的消费者工厂和生产者工厂注册了`MicrometerConsumerListener`和`MicrometerProducerListener`。它还为`StreamsBuilderFactoryBean`注册了`KafkaStreamsMicrometerListener`。有关更多详细信息,请参见 Spring Kafka 文档的[千分尺本机度量](https://docs.spring.io/spring-kafka/docs/2.8.3/reference/html/#micrometer-native)部分。 + +#### 6.3.18.MongoDB 度量 + +本节简要介绍 MongoDB 的可用度量。 + +##### MongoDB 命令度量 + +自动配置寄存器`MongoMetricsCommandListener`和自动配置的。 + +为发出给底层 MongoDB 驱动程序的每个命令创建一个名为`mongodb.driver.commands`的计时器度量。默认情况下,每个度量标准都带有以下信息: + +| Tag |说明| +|----------------|------------------------------------------------------------| +| `command` |发出的命令的名称。| +| `cluster.id` |将命令发送到的集群的标识符。| +|`server.address`|该命令被发送到的服务器的地址。| +| `status` |命令的结果(“success”或`FAILED`)。| + +要替换默认的度量标记,请定义`MongoCommandTagsProvider` Bean,如下例所示: + +``` +import io.micrometer.core.instrument.binder.mongodb.MongoCommandTagsProvider; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyCommandTagsProviderConfiguration { + + @Bean + public MongoCommandTagsProvider customCommandTagsProvider() { + return new CustomCommandTagsProvider(); + } + +} + +``` + +要禁用自动配置的命令度量,请设置以下属性: + +属性 + +``` +management.metrics.mongo.command.enabled=false +``` + +Yaml + +``` +management: + metrics: + mongo: + command: + enabled: false +``` + +##### MongoDB 连接池指标 + +自动配置寄存器`MongoMetricsConnectionPoolListener`和自动配置的`MongoClient`。 + +为连接池创建了以下度量指标: + +* `mongodb.driver.pool.size`报告连接池的当前大小,包括空闲成员和正在使用的成员。 + +* `false`报告当前正在使用的连接数。 + +* `mongodb.driver.pool.waitqueuesize`报告池中连接的等待队列的当前大小。 + +默认情况下,每个度量标准都带有以下信息: + +| Tag |说明| +|----------------|-----------------------------------------------------------------------| +| `cluster.id` |连接池对应的群集的标识符。| +|`server.address`|连接池对应的服务器的地址。| + +要替换默认的度量标记,请定义`MongoConnectionPoolTagsProvider` Bean: + +``` +import io.micrometer.core.instrument.binder.mongodb.MongoConnectionPoolTagsProvider; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyConnectionPoolTagsProviderConfiguration { + + @Bean + public MongoConnectionPoolTagsProvider customConnectionPoolTagsProvider() { + return new CustomConnectionPoolTagsProvider(); + } + +} + +``` + +要禁用自动配置的连接池指标,请设置以下属性: + +属性 + +``` +management.metrics.mongo.connectionpool.enabled=false +``` + +Yaml + +``` +management: + metrics: + mongo: + connectionpool: + enabled: false +``` + +#### 6.3.19. Jetty 指标 + +通过使用 Micrometer 的`JettyServerThreadPoolMetrics`,自动配置绑定了 Jetty 的`ThreadPool`的度量。 Jetty 的`Connector`实例的指标通过使用千分尺的`JettyConnectionMetrics`绑定,并且,当`server.ssl.enabled`设置为`true`时,千分尺的`JettySslHandshakeMetrics`。 + +#### 6.3.20.@ 定时注释支持 + +你可以将`@Timed`包中的`io.micrometer.core.annotation`注释与前面介绍的几种受支持的技术一起使用。如果支持,可以在类级别或方法级别使用注释。 + +例如,下面的代码显示了如何使用注释来检验`@RestController`中的所有请求映射: + +``` +import java.util.List; + +import io.micrometer.core.annotation.Timed; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Timed +public class MyController { + + @GetMapping("/api/addresses") + public List
listAddress() { + return ... + } + + @GetMapping("/api/people") + public List listPeople() { + return ... + } + +} + +``` + +如果你只想使用一个映射,那么可以使用方法上的注释,而不是类: + +``` +import java.util.List; + +import io.micrometer.core.annotation.Timed; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MyController { + + @GetMapping("/api/addresses") + public List
listAddress() { + return ... + } + + @GetMapping("/api/people") + @Timed + public List listPeople() { + return ... + } + +} + +``` + +如果你想更改特定方法的时间细节,还可以合并类级和方法级注释: + +``` +import java.util.List; + +import io.micrometer.core.annotation.Timed; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Timed +public class MyController { + + @GetMapping("/api/addresses") + public List
listAddress() { + return ... + } + + @GetMapping("/api/people") + @Timed(extraTags = { "region", "us-east-1" }) + @Timed(value = "all.people", longTask = true) + public List listPeople() { + return ... + } + +} + +``` + +| |带有`@Timed`注释的`longTask = true`将为该方法启用一个长任务计时器。
长任务计时器需要一个单独的度量名称,并且可以堆叠一个短任务计时器。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.3.21.Redis 度量 + +自动配置为自动配置的`LettuceConnectionFactory`寄存器 a`MicrometerCommandLatencyRecorder`。有关更多详细信息,请参见生菜文档的[千分尺计量部分](https://lettuce.io/core/6.1.6.RELEASE/reference/index.html#command.latency.metrics.micrometer)。 + +### 6.4.注册自定义度量 + +要注册自定义度量,请将`MeterRegistry`插入到组件中: + +``` +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final Dictionary dictionary; + + public MyBean(MeterRegistry registry) { + this.dictionary = Dictionary.load(); + registry.gauge("dictionary.size", Tags.empty(), this.dictionary.getWords().size()); + } + +} + +``` + +如果你的指标依赖于其他 bean,我们建议你使用`MeterBinder`来注册它们: + +``` +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.binder.MeterBinder; + +import org.springframework.context.annotation.Bean; + +public class MyMeterBinderConfiguration { + + @Bean + public MeterBinder queueSize(Queue queue) { + return (registry) -> Gauge.builder("queueSize", queue::size).register(registry); + } + +} + +``` + +使用`MeterBinder`可以确保设置了正确的依赖关系,并且在检索到度量值时 Bean 是可用的。如果你发现你在组件或应用程序之间反复测试一套度量标准,那么`MeterBinder`实现也会很有用。 + +| |默认情况下,来自所有`Connector`bean 的指标都会自动绑定到 Spring 管理的。| +|---|---------------------------------------------------------------------------------------------------------------| + +### 6.5.自定义单个指标 + +如果需要对特定的`Meter`实例应用自定义,则可以使用`io.micrometer.core.instrument.config.MeterFilter`接口。 + +例如,如果要将`percentiles-histogram`标记重命名为`mytag.area`,以`com.example`开头的所有仪表 ID,可以执行以下操作: + +``` +import io.micrometer.core.instrument.config.MeterFilter; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyMetricsFilterConfiguration { + + @Bean + public MeterFilter renameRegionTagMeterFilter() { + return MeterFilter.renameTag("com.example", "mytag.region", "mytag.area"); + } + +} + +``` + +| |默认情况下,所有`MeterFilter`bean 都自动绑定到 Spring 管理的`MeterRegistry`。
确保通过使用 Spring 管理的`MeterRegistry`来注册你的度量,而不是使用`Metrics`上的任何静态方法。
这些方法使用的是不是 Spring 管理的全局注册中心。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.5.1.公共标签 + +常见的标记通常用于对操作环境(如主机、实例、区域、堆栈等)进行维度下钻。Commons 标记应用于所有仪表,并且可以进行配置,如下例所示: + +属性 + +``` +management.metrics.tags.region=us-east-1 +management.metrics.tags.stack=prod +``` + +Yaml + +``` +management: + metrics: + tags: + region: "us-east-1" + stack: "prod" +``` + +前面的示例将`region`和`stack`标记分别添加到所有值为`us-east-1`和`prod`的仪表上。 + +| |如果使用 Graphite,公共标记的顺序很重要。
由于使用这种方法无法保证公共标记的顺序,建议 Graphite 用户定义一个自定义的`MeterFilter`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.5.2.每米属性 + +除了`MeterFilter`bean 之外,你还可以通过使用属性在每米的基础上应用一组有限的定制。每表自定义应用于以给定名称开始的任何表 ID。下面的示例禁用 ID 为`server.ssl.enabled`的任何仪表。 + +属性 + +``` +management.metrics.enable.example.remote=false +``` + +Yaml + +``` +management: + metrics: + enable: + example: + remote: false +``` + +以下属性允许按米定制: + +| Property |说明| +|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `management.metrics.enable` |是否防止仪表发出任何度量值.| +| `management.metrics.distribution.percentiles-histogram` |是否发布适合于计算可聚集(跨维度)百分位近似的直方图.| +|`management.metrics.distribution.minimum-expected-value`, `management.metrics.distribution.maximum-expected-value`|通过限制期望值的范围,发布更少的直方图桶。| +| `management.metrics.distribution.percentiles` |发布在应用程序中计算的百分位值| +| `management.metrics.distribution.expiry`, `management.metrics.distribution.buffer-length` |通过在可配置到期后旋转的环形缓冲区中积累最近的样本,并使用
可配置缓冲区长度,从而赋予它们更大的权重。| +| `management.metrics.distribution.slo` |使用由你的服务级别目标定义的桶发布累积直方图。| + +有关`percentiles-histogram`、`percentiles`和`percentiles`背后的概念的更多详细信息,请参见千分尺文档的[“直方图和百分位”部分](https://micrometer.io/docs/concepts#_histograms_and_percentiles)。 + +### 6.6.指标端点 + +Spring Boot 提供了一个`metrics`端点,你可以诊断地使用该端点来检查应用程序收集的指标。默认情况下端点不可用,必须公开。有关更多详细信息,请参见[公开端点](#actuator.endpoints.exposing)。 + +导航到`/actuator/metrics`将显示可用的仪表名称列表。你可以向下钻取以查看有关特定仪表的信息,方法是将其名称提供为选择器——例如,[公开端点](#actuator.endpoints.exposing)。 + +| |你在这里使用的名称应该与代码中使用的名称相匹配,而不是与它已被用于所运到的监视系统的命名约定规范化之后的名称相匹配,换句话说,在前面的示例中,返回的`Value`统计值是“代码缓存”、“压缩类空间”的最大内存足迹的总和,以及堆的“元空间”区域。
如果你只想看到“元空间”的最大大小,则可以添加一个额外的`tag=id:Metaspace`,即`/actuator/metrics/jvm.memory.max?tag=area:nonheap&tag=id:Metaspace`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 7. 审计 + +一旦 Spring 安全性发挥作用, Spring 启动执行器具有灵活的审计框架,该框架发布事件(默认情况下,“身份验证成功”、“失败”和“拒绝访问”异常)。这个特性对于报告和实现基于身份验证失败的锁定策略非常有用。 + +你可以通过在应用程序的配置中提供类型的 Bean 来启用审核。 Spring 为了方便起见,Boot 提供了`InMemoryAuditEventRepository`。InmemoryAuditeVentrepository’的功能有限,我们建议仅在开发环境中使用它。对于生产环境,考虑创建你自己的替代`AuditEventRepository`实现。 + +### 7.1.自定义审计 + +要定制已发布的安全事件,你可以提供你自己的`AbstractAuthenticationAuditListener`和`AbstractAuthorizationAuditListener`的实现。 + +你还可以为自己的业务事件使用审计服务。要做到这一点,可以将`AuditEventRepository` Bean 注入到你自己的组件中并直接使用它,或者使用 Spring `AuditApplicationEvent`发布`AuditApplicationEvent`(通过实现`ApplicationEventPublisherAware`)。 + +## 8. HTTP 追踪 + +你可以通过在应用程序的配置中提供`ApplicationEventPublisherAware`类型的 Bean 来启用 HTTP 跟踪。为了方便起见, Spring Boot 提供了`InMemoryHttpTraceRepository`,它存储了最近 100 次(默认)请求-响应交换的跟踪。与其他跟踪解决方案相比,InmemoryHttpTraceRepository’是有限的,我们建议仅在开发环境中使用它。对于生产环境,我们建议使用生产就绪的跟踪或可观察性解决方案,例如 Zipkin 或 Spring Cloud Sleuth。或者,你可以创建自己的`HttpTraceRepository`。 + +你可以使用`httptrace`端点来获取有关存储在`HttpTraceRepository`中的请求-响应交换的信息。 + +### 8.1.自定义 HTTP 跟踪 + +要自定义每个跟踪中包含的项,请使用`management.trace.http.include`配置属性。对于高级定制,可以考虑注册自己的`HttpExchangeTracer`实现。 + +## 9. 过程监控 + +在模块中,你可以找到两个类来创建通常对过程监控有用的文件: + +* `ApplicationPidFileWriter`创建一个包含应用程序 PID 的文件(默认情况下,在文件名为`application.pid`的应用程序目录中)。 + +* `WebServerPortFileWriter`创建一个包含运行中的 Web 服务器端口的文件(或多个文件)(默认情况下,在文件名为`application.port`的应用程序目录中)。 + +默认情况下,这些编写器不会被激活,但你可以启用它们: + +* [通过扩展配置](#actuator.process-monitoring.configuration) + +* [以编程方式启用过程监控](#actuator.process-monitoring.programmatically) + +### 9.1.扩展配置 + +在`META-INF/spring.factories`文件中,你可以激活写入 PID 文件的侦听器(或多个侦听器): + +``` +org.springframework.context.ApplicationListener=\ +org.springframework.boot.context.ApplicationPidFileWriter,\ +org.springframework.boot.web.context.WebServerPortFileWriter +``` + +### 9.2.以编程方式启用过程监控 + +还可以通过调用`SpringApplication.addListeners(…​)`方法并传递适当的`Writer`对象来激活侦听器。这个方法还允许你在`Writer`构造函数中自定义文件名和路径。 + +## 10. Cloud Foundry 支持 + +Spring Boot 的致动器模块包括额外的支持,该支持在部署到兼容的 Cloud Foundry 实例时被激活。`/cloudfoundryapplication`路径为所有`@Endpoint`bean 提供了一个可选的安全路由。 + +Spring 启动执行器信息增强了扩展的支持,使 Cloud Foundry 管理 UIS(例如你可以用于查看已部署的应用程序的 Web 应用程序)得到增强。例如,应用程序状态页面可以包含完整的健康信息,而不是典型的“运行”或“停止”状态。 + +| |普通用户无法直接访问`/cloudfoundryapplication`路径。
要使用端点,必须与请求一起传递有效的 UAA 令牌。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 10.1.禁用扩展的 Cloud Foundry 执行器支持 + +如果要完全禁用`/cloudfoundryapplication`端点,可以在`application.properties`文件中添加以下设置: + +属性 + +``` +management.cloudfoundry.enabled=false +``` + +Yaml + +``` +management: + cloudfoundry: + enabled: false +``` + +### 10.2.Cloud Foundry 自签名证书 + +默认情况下,`/cloudfoundryapplication`Endpoints 的安全验证使 SSL 调用到各种 Cloud Foundry 服务。如果你的 Cloud Foundry UAA 或 Cloud Controller 服务使用自签名证书,则需要设置以下属性: + +属性 + +``` +management.cloudfoundry.skip-ssl-validation=true +``` + +Yaml + +``` +management: + cloudfoundry: + skip-ssl-validation: true +``` + +### 10.3.自定义上下文路径 + +如果服务器的上下文路径已被配置为`/`以外的任何内容,则在应用程序的根目录下,Cloud Foundry 端点是不可用的。例如,如果`server.servlet.context-path=/app`,则可在`/app/cloudfoundryapplication/*`上获得 Cloud Foundry 端点。 + +如果你希望 Cloud Foundry 端点始终在`/cloudfoundryapplication/*`上可用,那么无论服务器的上下文路径如何,你都需要在应用程序中显式地配置它。根据使用的 Web 服务器的不同,配置也会有所不同。对于 Tomcat,你可以添加以下配置: + +``` +import java.io.IOException; +import java.util.Collections; + +import javax.servlet.GenericServlet; +import javax.servlet.Servlet; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.catalina.Host; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyCloudFoundryConfiguration { + + @Bean + public TomcatServletWebServerFactory servletWebServerFactory() { + return new TomcatServletWebServerFactory() { + + @Override + protected void prepareContext(Host host, ServletContextInitializer[] initializers) { + super.prepareContext(host, initializers); + StandardContext child = new StandardContext(); + child.addLifecycleListener(new Tomcat.FixContextListener()); + child.setPath("/cloudfoundryapplication"); + ServletContainerInitializer initializer = getServletContextInitializer(getContextPath()); + child.addServletContainerInitializer(initializer, Collections.emptySet()); + child.setCrossContext(true); + host.addChild(child); + } + + }; + } + + private ServletContainerInitializer getServletContextInitializer(String contextPath) { + return (classes, context) -> { + Servlet servlet = new GenericServlet() { + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + ServletContext context = req.getServletContext().getContext(contextPath); + context.getRequestDispatcher("/cloudfoundryapplication").forward(req, res); + } + + }; + context.addServlet("cloudfoundry", servlet).addMapping("/*"); + }; + } + +} + +``` + +## 11. 接下来读什么? + +你可能想了解有关绘图工具的信息,例如[Graphite](https://graphiteapp.org)。 + +否则,你可以继续阅读有关[“部署选项”](deployment.html#deployment)的内容,或者提前查看有关 Spring Boot 的[构建工具插件](build-tool-plugins.html#build-tool-plugins)的一些深入信息。 diff --git a/docs/spring-boot/build-tool-plugins.md b/docs/spring-boot/build-tool-plugins.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..02aae6acc87f070f8e34261531c6cf1d4ecd766c 100644 --- a/docs/spring-boot/build-tool-plugins.md +++ b/docs/spring-boot/build-tool-plugins.md @@ -0,0 +1,192 @@ +# 构建工具插件 + +Spring Boot 为 Maven 和 Gradle 提供了构建工具插件。这些插件提供了各种特性,包括可执行 JAR 的打包。本节提供了有关这两个插件的更多详细信息,以及在需要扩展不受支持的构建系统时提供的一些帮助。如果你刚刚开始,你可能需要先阅读“[using.html](using.html#using.build-systems)”一节中的“[using.html](using.html#using)”。 + +## 1.1 Spring 引导 Maven 插件 + +Spring boot Maven 插件在 Maven 中提供了 Spring boot 支持,允许你打包可执行文件 jar 或战争归档文件并“就地”运行应用程序。要使用它,你必须使用 Maven 3.2(或更高版本)。 + +查看该插件的文档以了解更多信息: + +* 引用([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/)和[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/pdf/spring-boot-maven-plugin-reference.pdf)) + +* [API](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/api/) + +## 1.2. Spring 引导 Gradle 插件 + +Spring boot Gradle 插件在 Gradle 中提供 Spring 引导支持,允许你打包可执行文件 jar 或战争归档文件,运行 Spring 引导应用程序,并使用`spring-boot-dependencies`提供的依赖管理。它需要 Gradle 6.8、6.9 或 7.x。查看该插件的文档以了解更多信息: + +* 参考文献([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/)和[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/pdf/spring-boot-gradle-plugin-reference.pdf)) + +* [API](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/api/) + +## 3. Spring 启动 Antlib 模块 + +Spring 启动 Antlib 模块为 Apache Ant 提供了基本的 Spring 启动支持。你可以使用该模块创建可执行 JAR。要使用该模块,你需要在你的`build.xml`中声明一个额外的`spring-boot`名称空间,如以下示例所示: + +``` + + ... + +``` + +你需要记住使用`-lib`选项启动 Ant,如以下示例所示: + +``` +$ ant -lib +``` + +> “使用 Spring 引导”部分包括[using Apache Ant with `spring-boot-antlib`](using.html#using.build-systems.ant)的更完整示例。 + +### 3.1. Spring 引导 Ant 任务 + +一旦声明了`spring-boot-antlib`名称空间,就可以执行以下附加任务: + +* [使用“exejar”任务](#build-tool-plugins.antlib.tasks.exejar) + +* [使用“FindMainclass”任务](#build-tool-plugins.antlib.findmainclass) + +#### 3.1.1.使用“exejar”任务 + +你可以使用`exejar`任务来创建 Spring 引导可执行文件 jar。该任务支持以下属性: + +| Attribute | Description |必需的| +|-------------|--------------------------------------|-------------------------------------------------------------------------| +| `destfile` | The destination jar file to create |是的| +| `classes` |The root directory of Java class files|是的| +|`start-class`| The main application class to run |否*(the default is the first class found that declares a `main` method)*| + +以下嵌套元素可与任务一起使用: + +| Element |说明| +|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|`resources`|一个或多个[资源收集](https://ant.apache.org/manual/Types/resources.html#collection),描述一组[Resources](https://ant.apache.org/manual/Types/resources.html),该集合应被添加到所创建的 jar 文件的内容中。| +| `lib` |应该将一个或多个[资源收集](https://ant.apache.org/manual/Types/resources.html#collection)添加到构成应用程序运行时依赖项 Classpath 的 jar 库集合中。| + +#### 3.1.2.例子 + +本节展示了 Ant 任务的两个示例。 + +指定起始类 + +``` + + + + + + + + +``` + +检测起始类 + +``` + + + + + +``` + +### 3.2.使用“FindMainclass”任务 + +`findmainclass`任务由`exejar`内部使用,用于定位声明`main`的类。如果有必要,你也可以在构建中直接使用此任务。支持以下属性: + +| Attribute |说明| Required | +|-------------|----------------------------------------------------|-------------------------------------------| +|`classesroot`|Java 类文件的根目录| Yes *(unless `mainclass` is specified)* | +| `mainclass` |可用于短路`main`类搜索| No | +| `property` |应与结果一起设置的 Ant 属性|No *(result will be logged if unspecified)*| + +#### 3.2.1.例子 + +本节包含使用`findmainclass`的三个示例。 + +查找并记录 + +``` + +``` + +查找并设置 + +``` + +``` + +覆盖和设置 + +``` + +``` + +## 4. 支持其他构建系统 + +如果你想使用 Maven、 Gradle 或 Ant 以外的构建工具,那么你可能需要开发自己的插件。可执行 JAR 需要遵循特定的格式,并且某些条目需要以未压缩的形式编写(有关详细信息,请参见附录中的“[executable jar format](executable-jar.html#appendix.executable-jar)”部分)。 + +Spring boot Maven 和 Gradle 插件都使用`spring-boot-loader-tools`来实际生成 JAR。如果需要,可以直接使用这个库。 + +### 4.1.重新包装档案 + +要重新打包现有的归档文件,使其成为一个自包含的可执行归档文件,请使用`org.springframework.boot.loader.tools.Repackager`。`Repackager`类接受一个构造函数参数,该参数引用现有的 jar 或 WAR 归档。使用两个可用的`repackage()`方法中的一个来替换原始文件或写入新的目标。在运行 Repackager 之前,还可以在其上配置各种设置。 + +### 4.2.嵌套库 + +在重新打包归档文件时,可以使用`org.springframework.boot.loader.tools.Libraries`接口来包含对依赖项文件的引用。这里我们不提供`Libraries`的任何具体实现,因为它们通常是特定于构建系统的。 + +如果你的归档文件已经包含库,那么你可以使用`Libraries.NONE`。 + +### 4.3.寻找主类 + +如果你不使用`Repackager.setMainClass()`来指定主类,则 Repackager 将使用[ASM](https://asm.ow2.io/)来读取类文件,并尝试使用`public static void main(String[] args)`方法找到合适的类。如果发现一个以上的候选者,将抛出一个异常。 + +### 4.4.示例重新打包实现 + +下面的示例展示了一个典型的重新打包实现: + +``` +import java.io.File; +import java.io.IOException; +import java.util.List; + +import org.springframework.boot.loader.tools.Library; +import org.springframework.boot.loader.tools.LibraryCallback; +import org.springframework.boot.loader.tools.LibraryScope; +import org.springframework.boot.loader.tools.Repackager; + +public class MyBuildTool { + + public void build() throws IOException { + File sourceJarFile = ... + Repackager repackager = new Repackager(sourceJarFile); + repackager.setBackupSource(false); + repackager.repackage(this::getLibraries); + } + + private void getLibraries(LibraryCallback callback) throws IOException { + // Build system specific implementation, callback for each dependency + for (File nestedJar : getCompileScopeJars()) { + callback.library(new Library(nestedJar, LibraryScope.COMPILE)); + } + // ... + } + + private List getCompileScopeJars() { + return ... + } + +} + +``` + +## 5. 接下来读什么? + +如果你对构建工具插件的工作方式感兴趣,可以查看 Github 上的[`spring-boot-tools`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-tools)模块。可执行 jar 格式的更多技术细节在[the appendix](executable-jar.html#appendix.executable-jar)中进行了介绍。 + +如果你有特定的与构建相关的问题,请参阅“[how-to](howto.html#howto)”指南。 + diff --git a/docs/spring-boot/cli.md b/docs/spring-boot/cli.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ded130be9f70936134fde8b630e530c9815c0cd4 100644 --- a/docs/spring-boot/cli.md +++ b/docs/spring-boot/cli.md @@ -0,0 +1,338 @@ +# Spring 引导 CLI + +Spring 引导 CLI 是一个命令行工具,如果你想快速开发 Spring 应用程序,可以使用它。它允许你运行 Groovy 脚本,这意味着你有一个熟悉的类似 Java 的语法,而不需要那么多样板代码。你还可以引导一个新项目,或者为它编写自己的命令。 + +## 1. 安装 CLI + +可以使用 SDKMAN 手动安装 Spring boot cli(命令行界面)!(SDK 管理器),或者如果你是 OSX 用户,则使用 Homebrew 或 MacPorts。有关全面的安装说明,请参见“开始”部分中的*[getting-started.html](getting-started.html#getting-started.installing.cli)*。 + +## 2. 使用 CLI + +一旦安装了 CLI,就可以通过输入`spring`并在命令行按 Enter 来运行它。如果你在没有任何参数的情况下运行`spring`,将显示一个帮助屏幕,如下所示: + +``` +$ spring +usage: spring [--help] [--version] + [] + +Available commands are: + + run [options] [--] [args] + Run a spring groovy script + + _... more command help is shown here_ +``` + +你可以键入`spring help`来获得有关任何支持的命令的更多详细信息,如以下示例所示: + +``` +$ spring help run +spring run - Run a spring groovy script + +usage: spring run [options] [--] [args] + +Option Description +------ ----------- +--autoconfigure [Boolean] Add autoconfigure compiler + transformations (default: true) +--classpath, -cp Additional classpath entries +--no-guess-dependencies Do not attempt to guess dependencies +--no-guess-imports Do not attempt to guess imports +-q, --quiet Quiet logging +-v, --verbose Verbose logging of dependency + resolution +--watch Watch the specified file for changes +``` + +`version`命令提供了一种快速的方法来检查你正在使用的 Spring 启动版本,如下所示: + +``` +$ spring version +Spring CLI v2.6.4 +``` + +### 2.1.使用 CLI 运行应用程序 + +你可以使用`run`命令编译和运行 Groovy 源代码。 Spring 引导 CLI 是完全自包含的,因此不需要任何外部 Groovy 安装。 + +下面的示例展示了一个用 Groovy 编写的“Hello World”Web 应用程序: + +你好。Groovy + +``` +@RestController +class WebApplication { + + @RequestMapping("/") + String home() { + "Hello World!" + } + +} + +``` + +要编译和运行应用程序,请键入以下命令: + +``` +$ spring run hello.groovy +``` + +要将命令行参数传递给应用程序,请使用`--`将命令与“ Spring”命令参数分开,如以下示例所示: + +``` +$ spring run hello.groovy -- --server.port=9000 +``` + +要设置 JVM 命令行参数,可以使用`JAVA_OPTS`环境变量,如以下示例所示: + +``` +$ JAVA_OPTS=-Xmx1024m spring run hello.groovy +``` + +| |在 Microsoft Windows 上设置`JAVA_OPTS`时,请确保引用整个指令,例如`set "JAVA_OPTS=-Xms256m -Xmx2048m"`。
这样做可以确保将值正确地传递给进程。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.1.推导出“抓取”依赖项 + +标准 Groovy 包含一个`@Grab`注释,它允许你声明对第三方库的依赖关系。这一有用的技术让 Groovy 以与 Maven 或 Gradle 相同的方式下载 JAR,但不需要使用构建工具。 + +Spring Boot 进一步扩展了这种技术,并试图根据你的代码推断出要“抓取”哪些库。例如,由于前面显示的`WebApplication`代码使用`@RestController`注释, Spring 引导抓取“ Tomcat”和“ Spring MVC”。 + +以下项目被用作“抓取提示”: + +|项目| Grabs | +|----------------------------------------------------------|------------------------------| +|`JdbcTemplate`, `NamedParameterJdbcTemplate`, `DataSource`| JDBC Application. | +|`@EnableJms`| JMS Application. | +|`@EnableCaching`| Caching abstraction. | +|`@Test`| JUnit. | +|`@EnableRabbit`| RabbitMQ. | +|扩展`Specification`| Spock test. | +|`@EnableBatchProcessing`| Spring Batch. | +|`@MessageEndpoint` `@EnableIntegration`| Spring Integration. | +|`@Controller` `@RestController` `@EnableWebMvc`|Spring MVC + Embedded Tomcat. | +|`@EnableWebSecurity`| Spring Security. | +|`@EnableTransactionManagement`|Spring Transaction Management.| + +| |请参阅 Spring Boot CLI 源代码中[“编译器自动配置”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/CompilerAutoConfiguration.java)的子类,以确切了解如何应用自定义。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.2.推导出的“抓取”坐标 + +Spring Boot 扩展了 Groovy 的标准`@Grab`支持,允许你指定一个没有组或版本的依赖项(例如,`@Grab('freemarker')`)。这样做会参考 Spring Boot 的默认依赖关系元数据来推断工件的组和版本。 + +| |默认的元数据与你使用的 CLI 版本绑定,
只有当你移动到新版本的 CLI 时,它才会发生变化,让你控制依赖项的版本何时可能更改。
在[appendix](dependency-versions.html#appendix.dependency-versions)中可以找到一个表,该表显示了默认元数据中包含的依赖项及其版本。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.3.默认导入语句 + +为了帮助减少 Groovy 代码的大小,会自动包含几个`import`语句。注意前面的示例是如何引用`@Component`、`@RestController`和`@RequestMapping`而不需要使用完全限定的名称或`import`语句的。 + +| |许多 Spring 注释在不使用`import`语句的情况下工作。
在添加导入之前,尝试运行你的应用程序以查看失败的原因。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.4.自动主法 + +与等效的 Java 应用程序不同,你不需要在`Groovy`脚本中包含`public static void main(String[] args)`方法。将自动创建`SpringApplication`,你的编译代码将充当`source`。 + +#### 2.1.5.自定义依赖管理 + +默认情况下,CLI 在解析`@Grab`依赖关系时使用在`spring-boot-dependencies`中声明的依赖关系管理。可以通过使用`@DependencyManagementBom`注释来配置额外的依赖管理,该管理覆盖了默认的依赖管理。注释的值应该指定一个或多个 Maven boms 的坐标(“groupid:artifactid:version”)。 + +例如,考虑以下声明: + +``` +@DependencyManagementBom("com.example.custom-bom:1.0.0") + +``` + +前面的声明在 Maven 存储库中的`com/example/custom-versions/1.0.0/`下获取`custom-bom-1.0.0.pom`。 + +当你指定多个 BOM 时,它们将按照你声明它们的顺序应用,如下例所示: + +``` +@DependencyManagementBom([ + "com.example.custom-bom:1.0.0", + "com.example.another-bom:1.0.0"]) + +``` + +前面的示例表明,`another-bom`中的依赖管理重写了`custom-bom`中的依赖管理。 + +你可以在任何可以使用`@DependencyManagementBom`的地方使用`@Grab`。然而,为了确保依赖管理的一致顺序,你可以在应用程序中最多使用`@DependencyManagementBom`一次。 + +### 2.2.具有多个源文件的应用程序 + +你可以在所有接受文件输入的命令中使用“shell globbing”。这样做可以让你从一个目录中使用多个文件,如下面的示例所示: + +``` +$ spring run *.groovy +``` + +### 2.3.打包你的应用程序 + +你可以使用`jar`命令将你的应用程序打包成一个自包含的可执行文件 jar,如下例所示: + +``` +$ spring jar my-app.jar *.groovy +``` + +jar 结果包含通过编译应用程序产生的类和应用程序的所有依赖项,这样就可以使用`java -jar`运行它。 jar 文件还包含来自应用程序 Classpath 的条目。可以通过使用`--include`和`--exclude`来添加和删除 jar 的显式路径。两者都是用逗号分隔的,并且都接受前缀,以“+”和“-”的形式表示它们应该从默认值中删除。默认值包括以下内容: + +``` +public/**, resources/**, static/**, templates/**, META-INF/**, * +``` + +默认排除如下: + +``` +.*, repository/**, build/**, target/**, **/*.jar, **/*.groovy +``` + +有关详细信息,请在命令行上键入`spring help jar`。 + +### 2.4.初始化一个新项目 + +`init`命令允许你在不离开 shell 的情况下使用[start.spring.io](https://start.spring.io)创建一个新项目,如下面的示例所示: + +``` +$ spring init --dependencies=web,data-jpa my-project +Using service at https://start.spring.io +Project extracted to '/Users/developer/example/my-project' +``` + +前面的示例使用基于 Maven 的项目创建`my-project`目录,该项目使用`spring-boot-starter-web`和`spring-boot-starter-data-jpa`。你可以使用`--list`标志列出服务的功能,如以下示例所示: + +``` +$ spring init --list +======================================= +Capabilities of https://start.spring.io +======================================= + +Available dependencies: +----------------------- +actuator - Actuator: Production ready features to help you monitor and manage your application +... +web - Web: Support for full-stack web development, including Tomcat and spring-webmvc +websocket - Websocket: Support for WebSocket development +ws - WS: Support for Spring Web Services + +Available project types: +------------------------ +gradle-build - Gradle Config [format:build, build:gradle] +gradle-project - Gradle Project [format:project, build:gradle] +maven-build - Maven POM [format:build, build:maven] +maven-project - Maven Project [format:project, build:maven] (default) + +... +``` + +`init`命令支持许多选项。有关更多详细信息,请参见`help`输出。例如,下面的命令创建了一个使用 Java8 和`war`打包的 Gradle 项目: + +``` +$ spring init --build=gradle --java-version=1.8 --dependencies=websocket --packaging=war sample-app.zip +Using service at https://start.spring.io +Content saved to 'sample-app.zip' +``` + +### 2.5.使用嵌入式外壳 + +Spring 引导包括用于 bash 和 zsh shell 的命令行完成脚本。如果不使用这两个 shell 中的任何一个(也许你是 Windows 用户),则可以使用`shell`命令来启动一个集成的 shell,如下例所示: + +``` +$ spring shell +Spring Boot (v2.6.4) +Hit TAB to complete. Type \'help' and hit RETURN for help, and \'exit' to quit. +``` + +在嵌入的 shell 中,你可以直接运行其他命令: + +``` +$ version +Spring CLI v2.6.4 +``` + +嵌入式外壳支持 ANSI 颜色输出以及`tab`补全。如果需要运行本机命令,可以使用`!`前缀。要退出嵌入的 shell,请按`ctrl-c`。 + +### 2.6.向 CLI 添加扩展 + +你可以使用`install`命令向 CLI 添加扩展。该命令以`group:artifact:version`的格式接受一组或多组工件坐标,如以下示例所示: + +``` +$ spring install com.example:spring-boot-cli-extension:1.0.0.RELEASE +``` + +除了安装由你提供的坐标标识的工件外,还安装了所有工件的依赖关系。 + +要卸载依赖项,请使用`uninstall`命令。与`install`命令一样,它以`group:artifact:version`的格式接受一组或多组工件坐标,如以下示例所示: + +``` +$ spring uninstall com.example:spring-boot-cli-extension:1.0.0.RELEASE +``` + +它卸载由你提供的坐标和它们的依赖关系所标识的工件。 + +要卸载所有附加依赖项,可以使用`--all`选项,如以下示例所示: + +``` +$ spring uninstall --all +``` + +## 3. 使用 Groovy Beans DSL 开发应用程序 + +Spring Framework4.0 对`beans{}`“DSL”(借用自[Grails](https://grails.org/))具有原生支持,并且你可以通过使用相同的格式在 Groovy 应用程序脚本中嵌入 Bean 定义。这有时是一种很好的方式,可以包含外部特性,比如中间件声明,如下例所示: + +``` +@Configuration(proxyBeanMethods = false) +class Application implements CommandLineRunner { + + @Autowired + SharedService service + + @Override + void run(String... args) { + println service.message + } + +} + +import my.company.SharedService + +beans { + service(SharedService) { + message = "Hello World" + } +} + +``` + +你可以在同一个文件中将类声明与`beans{}`混合在一起,只要它们保持在顶层,或者,如果你愿意,可以将 bean DSL 放在一个单独的文件中。 + +## 4. 使用 settings.xml 配置 CLI + +Spring 引导 CLI 使用 Maven resolver( Maven 的依赖项解析引擎)来解析依赖项。CLI 使用`~/.m2/settings.xml`中的 Maven 配置来配置 Maven 解析器。以下配置设置由 CLI 执行: + +* 离线 + +* 镜子 + +* 服务器 + +* 代理 + +* 配置文件 + + * 激活 + + * 存储库 + +* 活动配置文件 + +有关更多信息,请参见[Maven’s settings documentation](https://maven.apache.org/settings.html)。 + +## 5. 接下来读什么? + +GitHub 存储库中有一些[Groovy 脚本示例](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-cli/samples)可用,你可以使用它们来尝试 Spring 引导 CLI。在[source code](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-cli/src/main/java/org/springframework/boot/cli)中也有大量的 Javadoc。 + +如果你发现你已经达到了 CLI 工具的极限,那么你可能希望将你的应用程序转换为完整的 Gradle 或 Maven 构建的“Groovy 项目”。下一节将介绍 Spring boot 的“[构建工具插件](build-tool-plugins.html#build-tool-plugins)”,你可以在 Gradle 或 Maven 中使用它。 diff --git a/docs/spring-boot/container-images.md b/docs/spring-boot/container-images.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9c6391eca152a9203248a046dab24bd2c0d55e85 100644 --- a/docs/spring-boot/container-images.md +++ b/docs/spring-boot/container-images.md @@ -0,0 +1,135 @@ +# 容器图像 + +Spring 引导应用程序可以是容器化的[使用 DockerFiles](#container-images.dockerfiles),也可以是容器化的[使用本地云构建包创建优化的 Docker 兼容容器映像,可以在任何地方运行](#container-images.buildpacks)。 + +## 1. 高效的容器图像 + + +将 Spring 引导 fat jar 打包为 Docker 映像是很容易的。然而,复制和运行 Docker 映像中的 fat jar 有各种缺点。在不拆包的情况下运行 A fat jar 时,总会有一定的开销,在集装箱化的环境中,这一点是显而易见的。另一个问题是,将应用程序的代码及其所有依赖项放在 Docker 映像中的一个层中是次优的。由于你重新编译代码的次数可能比升级所使用的启动版本更多,所以更好的做法通常是多分离一些东西。如果将 jar 个文件放在应用程序类之前的层中,Docker 通常只需要更改最底层的文件,就可以从其缓存中获取其他文件。 + +### 1.1.打开 fat jar + +如果你是从一个容器运行你的应用程序,那么你可以使用一个可执行文件 jar,但通常也有一个优势,那就是将其分解并以不同的方式运行它。某些 PaaS 实现还可能选择在运行归档文件之前对其进行解包。例如,Cloud Foundry 就是这样运作的。运行未打包归档文件的一种方法是启动适当的启动器,如下所示: + +``` +$ jar -xf myapp.jar +$ java org.springframework.boot.loader.JarLauncher +``` + +这实际上在启动时(取决于 jar 的大小)比在未爆炸的归档文件中运行稍微快一些。在运行时,你不应该期望有任何差异。 + +打开 jar 文件后,还可以通过使用“自然”主方法(而不是`JarLauncher`)运行应用程序来增加启动时间。例如: + +``` +$ jar -xf myapp.jar +$ java -cp BOOT-INF/classes:BOOT-INF/lib/* com.example.MyApplication +``` + +| |在应用程序的主方法上使用`JarLauncher`具有可预测的 Classpath 顺序的附加好处。
jar 包含一个`classpath.idx`文件,该文件由`JarLauncher`在构造 Classpath 时使用。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.2.分层 Docker 图像 + +为了使创建优化的 Docker 映像更容易, Spring 启动支持向 jar 添加层索引文件。它提供了层的列表和 jar 中应该包含在其中的部分。索引中的层列表是根据将层添加到 Docker/OCI 图像中的顺序进行排序的。在开箱即用的情况下,支持以下层: + +* `dependencies`(用于常规发布的依赖项) + +* `spring-boot-loader`(对于`org/springframework/boot/loader`下的所有内容) + +* `snapshot-dependencies`(用于快照依赖关系) + +* `application`(用于应用程序类和资源) + +下面显示了`layers.idx`文件的示例: + +``` +- "dependencies": + - BOOT-INF/lib/library1.jar + - BOOT-INF/lib/library2.jar +- "spring-boot-loader": + - org/springframework/boot/loader/JarLauncher.class + - org/springframework/boot/loader/jar/JarEntry.class +- "snapshot-dependencies": + - BOOT-INF/lib/library3-SNAPSHOT.jar +- "application": + - META-INF/MANIFEST.MF + - BOOT-INF/classes/a/b/C.class +``` + +这种分层设计是为了根据在应用程序构建之间更改代码的可能性来分离代码。库代码在两次构建之间更改的可能性较小,因此将其放置在自己的层中,以允许工具重新使用缓存中的层。应用程序代码更有可能在构建之间进行更改,因此它被隔离在一个单独的层中。 + +Spring 在`layers.idx`的帮助下,引导还支持对 WAR 文件进行分层。 + +对于 Maven,请参阅[packaging layered jar or war section](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#repackage-layers)以获取有关向归档文件添加图层索引的更多详细信息。有关 Gradle,请参见 Gradle 插件文档的[packaging layered jar or war section](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#packaging-layered-archives)。 + +## 2. DockerFiles + +虽然只需在 Dockerfile 中的几行就可以将 Spring 启动 fat jar 转换为 Docker 映像,但我们将使用[分层特征](#container-images.efficient-images.layering)来创建优化的 Docker 映像。当你创建包含层索引文件的 jar 时,`spring-boot-jarmode-layertools` jar 将作为依赖项添加到 jar 中。有了这个 jar 在 Classpath 上,你可以以一种特殊的模式启动你的应用程序,该模式允许引导代码运行与你的应用程序完全不同的东西,例如,提取层的东西。 + +| |`layertools`模式不能与包含启动脚本的[fully executable Spring Boot archive](deployment.html#deployment.installing)一起使用。
在构建打算与`layertools`一起使用的 jar 文件时禁用启动脚本配置。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +以下是使用`layertools` jar 模式启动 jar 的方法: + +``` +$ java -Djarmode=layertools -jar my-app.jar +``` + +这将提供以下产出: + +``` +Usage: + java -Djarmode=layertools -jar my-app.jar + +Available commands: + list List layers from the jar that can be extracted + extract Extracts layers from the jar for image creation + help Help about any command +``` + +`extract`命令可以用来轻松地将应用程序分割成要添加到 DockerFile 中的层。下面是一个使用`jarmode`的 DockerFile 的示例。 + +``` +FROM adoptopenjdk:11-jre-hotspot as builder +WORKDIR application +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} application.jar +RUN java -Djarmode=layertools -jar application.jar extract + +FROM adoptopenjdk:11-jre-hotspot +WORKDIR application +COPY --from=builder application/dependencies/ ./ +COPY --from=builder application/spring-boot-loader/ ./ +COPY --from=builder application/snapshot-dependencies/ ./ +COPY --from=builder application/application/ ./ +ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] +``` + +假设上面的`Dockerfile`在当前目录中,你的 Docker 映像可以使用`docker build .`构建,或者可选地指定应用程序的路径 jar,如以下示例所示: + +``` +$ docker build --build-arg JAR_FILE=path/to/myapp.jar . +``` + +这是一个多阶段的 DockerFile。构建器阶段提取以后需要的目录。每个`COPY`命令都与 JARMODE 提取的层有关。 + +当然,可以在不使用 JARMODE 的情况下编写 DockerFile。你可以使用`unzip`和`mv`的一些组合来将事情移动到正确的层,但是 Jarmode 简化了这一点。 + +## 3. 云原生构建包 + +DockerFiles 只是构建 Docker 映像的一种方式。另一种构建 Docker 映像的方法是直接从 Maven 或 Gradle 插件中使用 buildpacks。如果你曾经使用过 Cloud Foundry 或 Heroku 之类的应用程序平台,那么你很可能使用过 BuildPack。构建包是平台的一部分,它接收应用程序并将其转换为平台可以实际运行的内容。例如,Cloud Foundry 的 Java BuildPack 会注意到,你正在推送一个`.jar`文件,并自动添加一个相关的 JRE。 + +使用 Cloud Native 构建包,你可以创建可以在任何地方运行的与 Docker 兼容的映像。 Spring 引导包括直接针对 Maven 和 Gradle 的 BuildPack 支持。这意味着你只需键入一个命令,就可以快速地在本地运行的 Docker 守护程序中获得一个合理的映像。 + +请参阅有关如何使用[Maven](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#build-image)和[Gradle](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#build-image)构建包的单个插件文档。 + +| |[Paketo Spring Boot buildpack](https://github.com/paketo-buildpacks/spring-boot)也已更新,以支持`layers.idx`文件,因此应用于该文件的任何定制都将反映在 BuildPack 创建的图像中。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |为了实现可重复的构建和容器映像缓存,构建包可以操作应用程序资源元数据(例如文件“上次修改”信息)。
你应该确保你的应用程序在运行时不依赖于该元数据。
Spring 引导在服务静态资源时可以使用该信息,但这可以用`spring.web.resources.cache.use-last-modified`禁用| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 4. 接下来读什么? + +一旦你了解了如何构建高效的容器映像,你就可以了解[将应用程序部署到云平台](deployment.html#deployment.cloud.kubernetes),比如 Kubernetes。 + diff --git a/docs/spring-boot/data.md b/docs/spring-boot/data.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ec15dc9483bda895d6a4b473d5073749c947966c 100644 --- a/docs/spring-boot/data.md +++ b/docs/spring-boot/data.md @@ -0,0 +1,1496 @@ +# 数据 + +Spring Boot 集成了许多数据技术,包括 SQL 和 NoSQL。 + +# 1. SQL 数据库 +---------- + +[Spring Framework](https://spring.io/projects/spring-framework)为使用 SQL 数据库提供了广泛的支持,从使用`JdbcTemplate`直接访问 JDBC 到完成诸如 Hibernate 之类的“对象关系映射”技术。[Spring Data](https://spring.io/projects/spring-data)提供了一个额外的功能级别:直接从接口创建`Repository`实现,并使用约定从你的方法名称生成查询。 + +### 1.1.配置数据源 + +Java 的`javax.sql.DataSource`接口提供了一种处理数据库连接的标准方法。传统上,“数据源”使用`URL`以及一些凭据来建立数据库连接。 + +| |有关更高级的示例,请参见[“操作指南”部分](howto.html#howto.data-access.configure-custom-datasource),通常用于完全控制数据源的配置。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.1.嵌入式数据库支持 + +使用内存式嵌入式数据库开发应用程序通常是很方便的。显然,内存数据库不提供持久存储。你需要在应用程序启动时填充数据库,并准备在应用程序结束时丢弃数据。 + +| |“how-to”部分包括[关于如何初始化数据库的一节](howto.html#howto.data-initialization)。| +|---|----------------------------------------------------------------------------------------------------------------| + +Spring 引导可以自动配置嵌入式[H2](https://www.h2database.com)、[HSQL](http://hsqldb.org/)和[Derby](https://db.apache.org/derby/)数据库。你不需要提供任何连接 URL。你只需要对要使用的嵌入式数据库包含一个构建依赖项。如果在 Classpath 上有多个嵌入式数据库,请设置`spring.datasource.embedded-database-connection`配置属性来控制使用哪个数据库。将属性设置为`none`将禁用嵌入式数据库的自动配置。 + +| |如果在测试中使用此功能,你可能会注意到,无论你使用的应用程序上下文的数量如何,整个测试套件都可以重用相同的数据库,如果你想确保每个上下文都有一个单独的嵌入式数据库,
,你应该将`spring.datasource.generate-unique-name`设置为`true`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +例如,典型的 POM 依赖关系如下: + +``` + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.hsqldb + hsqldb + runtime + +``` + +| |你需要对`spring-jdbc`有一个依赖关系,才能自动配置嵌入式数据库。
在本例中,它是通过`spring-boot-starter-data-jpa`传递地拉入的。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |无论出于何种原因,如果你确实为嵌入式数据库配置了连接 URL,请注意确保禁用数据库的自动关闭。
如果你使用 H2,你应该使用`DB_CLOSE_ON_EXIT=FALSE`来实现这一点。
如果你使用 HSQLDB,你应该确保不使用`shutdown=true`。
禁用数据库的自动关机可以在数据库关闭时进行引导控制,从而确保在不再需要访问数据库时发生这种情况。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.2.与生产数据库的连接 + +还可以通过使用池`DataSource`自动配置生产数据库连接。 + +#### 1.1.3.数据源配置 + +数据源配置由`spring.datasource.*`中的外部配置属性控制。例如,你可以在`application.properties`中声明以下部分: + +属性 + +``` +spring.datasource.url=jdbc:mysql://localhost/test +spring.datasource.username=dbuser +spring.datasource.password=dbpass +``` + +Yaml + +``` +spring: + datasource: + url: "jdbc:mysql://localhost/test" + username: "dbuser" + password: "dbpass" +``` + +| |你至少应该通过设置`spring.datasource.url`属性来指定 URL。
否则, Spring 引导将尝试自动配置嵌入式数据库。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Spring 引导可以从 URL 中推断出大多数数据库的 JDBC 驱动程序类。
如果需要指定特定的类,可以使用`spring.datasource.driver-class-name`属性。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |对于要创建的池`DataSource`,我们需要能够验证一个有效的`Driver`类是可用的,所以我们在做任何事情之前都要检查它。
换句话说,如果你设置`spring.datasource.driver-class-name=com.mysql.jdbc.Driver`,那么这个类必须是可加载的。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有关更多支持的选项,请参见[“数据资源属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSource属性.java)。无论[实际执行情况](features.html#data.sql.datasource.connection-pool)如何,这些都是标准选项。也可以通过使用它们各自的前缀(` Spring.datasource.hikari.*`,`spring.datasource.tomcat.*`,`spring.datasource.dbcp2.*`和`spring.datasource.oracleucp.*`)来微调实现特定的设置。有关更多详细信息,请参见你正在使用的连接池实现的文档。 + +例如,如果使用[Tomcat connection pool](https://tomcat.apache.org/tomcat-9.0-doc/jdbc-pool.html#Common_Attributes),则可以自定义许多其他设置,如下例所示: + +属性 + +``` +spring.datasource.tomcat.max-wait=10000 +spring.datasource.tomcat.max-active=50 +spring.datasource.tomcat.test-on-borrow=true +``` + +Yaml + +``` +spring: + datasource: + tomcat: + max-wait: 10000 + max-active: 50 + test-on-borrow: true +``` + +这将设置池等待 10000ms,然后在没有可用连接的情况下抛出异常,将最大连接数量限制为 50,并在从池中借用连接之前验证连接。 + +#### 1.1.4.支持的连接池 + +Spring 启动使用以下算法来选择具体的实现方式: + +1. 我们更喜欢[Hikaricp](https://github.com/brettwooldridge/HikariCP)的性能和并发性。如果有 Hikaricp,我们总是选择它。 + +2. 否则,如果 Tomcat 池`DataSource`可用,我们就使用它。 + +3. 否则,如果[Commons DBCP2](https://commons.apache.org/proper/commons-dbcp/)是可用的,我们就使用它。 + +4. 如果 HIKARICP、 Tomcat 和 DBCP2 都不可用,并且如果 Oracle UCP 可用,我们就使用它。 + +| |如果使用`spring-boot-starter-jdbc`或`spring-boot-starter-data-jpa`“starters”,则会自动获得对`HikariCP`的依赖关系。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------| + +你可以完全绕过该算法,并通过设置`spring.datasource.type`属性来指定要使用的连接池。如果你在 Tomcat 容器中运行应用程序,这一点尤其重要,因为`tomcat-jdbc`是默认提供的。 + +总是可以使用`DataSourceBuilder`手动配置其他连接池。如果你定义了自己的`DataSource` Bean,则不会发生自动配置。`DataSourceBuilder`支持以下连接池: + +* HikariCP + +* Tomcat 池`Datasource` + +* Commons DBCP2 + +* Oracle UCP&`OracleDataSource` + +* Spring 框架的`SimpleDriverDataSource` + +* h2`JdbcDataSource` + +* PostgreSQL`PGSimpleDataSource` + +#### 1.1.5.连接到 JNDI 数据源 + +如果你将 Spring 引导应用程序部署到应用程序服务器,你可能希望通过使用应用程序服务器的内置功能来配置和管理你的数据源,并通过使用 JNDI 访问它。 + +`spring.datasource.jndi-name`属性可以用作`spring.datasource.url`、`spring.datasource.username`和`spring.datasource.password`属性的替代选项,以从特定的 JNDI 位置访问`DataSource`。例如,`application.properties`中的以下部分显示了如何按照`DataSource`的定义访问 JBoss: + +属性 + +``` +spring.datasource.jndi-name=java:jboss/datasources/customers +``` + +Yaml + +``` +spring: + datasource: + jndi-name: "java:jboss/datasources/customers" +``` + +### 1.2.使用 JDBCTemplate + +Spring 的`JdbcTemplate`和`NamedParameterJdbcTemplate`类是自动配置的,并且你可以将它们`@Autowire`直接放入你自己的 bean 中,如以下示例所示: + +``` +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final JdbcTemplate jdbcTemplate; + + public MyBean(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void doSomething() { + this.jdbcTemplate ... + } + +} + +``` + +你可以使用`spring.jdbc.template.*`属性自定义模板的一些属性,如下例所示: + +属性 + +``` +spring.jdbc.template.max-rows=500 +``` + +Yaml + +``` +spring: + jdbc: + template: + max-rows: 500 +``` + +| |`NamedParameterJdbcTemplate`在幕后重用相同的`JdbcTemplate`实例。
如果定义了多个`JdbcTemplate`且不存在主要候选项,则`NamedParameterJdbcTemplate`不会自动配置。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.3. JPA 和 Spring 数据 JPA + +Java Persistence API 是一种标准技术,它允许你将对象“映射”到关系数据库。`spring-boot-starter-data-jpa` POM 提供了一种快速启动的方法。它提供了以下关键依赖项: + +* Hibernate:最流行的实现方式之一 JPA。 + +* Spring 数据 JPA:帮助你实现基于 JPA 的存储库。 + +* Spring ORM:来自 Spring 框架的核心 ORM 支持。 + +| |在这里,我们不会详细讨论 JPA 或[Spring Data](https://spring.io/projects/spring-data)的细节。
你可以按照[“Accessing Data with JPA”](https://spring.io/guides/gs/accessing-data-jpa/)中的[“Accessing Data with JPA”](https://spring.io/guides/gs/accessing-data-jpa/)指南,并阅读[Spring Data JPA](https://spring.io/projects/spring-data-jpa)和[Hibernate](https://hibernate.org/orm/documentation/)参考文档。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.1.实体类别 + +传统上, JPA“实体”类是在`persistence.xml`文件中指定的。在 Spring 启动时,这个文件不是必需的,而是使用“实体扫描”。默认情况下,将搜索主配置类下面的所有包(注释为`@EnableAutoConfiguration`或`@SpringBootApplication`的包)。 + +任何带有`@Entity`、`@Embeddable`或`@MappedSuperclass`注释的类都将被考虑。典型的实体类类似于以下示例: + +``` +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +@Entity +public class City implements Serializable { + + @Id + @GeneratedValue + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String state; + + // ... additional members, often include @OneToMany mappings + + protected City() { + // no-args constructor required by JPA spec + // this one is protected since it should not be used directly + } + + public City(String name, String state) { + this.name = name; + this.state = state; + } + + public String getName() { + return this.name; + } + + public String getState() { + return this.state; + } + + // ... etc + +} + +``` + +| |你可以使用`@EntityScan`注释自定义实体扫描位置。
参见“[howto.html](howto.html#howto.data-access.separate-entity-definitions-from-spring-configuration)”操作方法。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.2. Spring 数据 JPA 存储库 + +[Spring Data JPA](https://spring.io/projects/spring-data-jpa)存储库是你可以定义以访问数据的接口。 JPA 查询是根据你的方法名称自动创建的。例如,一个`CityRepository`接口可以声明一个`findAllByState(String state)`方法来查找给定状态下的所有城市。 + +对于更复杂的查询,你可以使用 Spring data 的[`Query`](https://docs.spring.io/spring-data/jpa/docs/2.6.2/api/org/springframework/data/jpa/repository/Query.html)注释来注释你的方法。 + +Spring 数据存储库通常从[`Repository`](https://docs.spring.io/spring-data/commons/docs/2.6.2/api/org/springframework/data/repository/Repository.html)或[“粗栓剂”](https://docs.spring.io/spring-data/commons/docs/2.6.2/api/org/springframework/data/repository/CrudRepository.html)接口扩展。如果使用自动配置,那么存储库将从包含主配置类(用`@EnableAutoConfiguration`或`@SpringBootApplication`注释的类)的包中进行搜索。 + +下面的示例显示了典型的 Spring 数据存储库接口定义: + +``` +import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.City; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.Repository; + +public interface CityRepository extends Repository { + + Page findAll(Pageable pageable); + + City findByNameAndStateAllIgnoringCase(String name, String state); + +} + +``` + +Spring 数据 JPA 存储库支持三种不同的引导模式:默认模式、延迟模式和惰性模式。要启用延迟引导或延迟引导,将`spring.data.jpa.repositories.bootstrap-mode`属性分别设置为`deferred`或`lazy`。当使用延迟或延迟引导时,自动配置的`EntityManagerFactoryBuilder`将使用上下文的`AsyncTaskExecutor`(如果有的话)作为引导执行器。如果存在多个选项,将使用名为`applicationTaskExecutor`的选项。 + +| |当使用延迟引导或延迟引导时,请确保在应用程序上下文引导阶段之后推迟对 JPA 基础架构的任何访问。
你可以使用`SmartInitializingSingleton`来调用任何需要 JPA 基础架构的初始化。
对于创建为 Spring bean 的 JPA 组件(例如转换器),使用`ObjectProvider`来延迟依赖关系的解决(如果有)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |我们几乎没有触及 Spring 数据 JPA 的表面。
有关完整的详细信息,请参见[Spring Data JPA reference documentation](https://docs.spring.io/spring-data/jpa/docs/2.6.2/reference/html)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.3. Spring 数据影响存储库 + +如果[Spring Data Envers](https://spring.io/projects/spring-data-envers)可用, JPA 存储库将自动配置为支持典型的 Envers 查询。 + +要使用 Spring data envers,请确保你的存储库从`RevisionRepository`扩展,如下例所示: + +``` +import org.springframework.boot.docs.data.sql.jpaandspringdata.entityclasses.Country; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.history.RevisionRepository; + +public interface CountryRepository extends RevisionRepository, Repository { + + Page findAll(Pageable pageable); + +} + +``` + +| |有关更多详细信息,请查看[Spring Data Envers reference documentation](https://docs.spring.io/spring-data/envers/docs/2.6.2/reference/html/)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.4.创建和删除 JPA 数据库 + +默认情况下,如果使用嵌入式数据库(H2、HSQL 或 Derby), JPA 数据库将自动创建**只是**。你可以使用`spring.jpa.*`属性显式地配置 JPA 设置。例如,要创建和删除表格,你可以在`application.properties`中添加以下行: + +属性 + +``` +spring.jpa.hibernate.ddl-auto=create-drop +``` + +Yaml + +``` +spring: + jpa: + hibernate.ddl-auto: "create-drop" +``` + +| |Hibernate 自己的内部属性名称(如果你能更好地记住它的话)是`hibernate.hbm2ddl.auto`,
,你可以将其与其他 Hibernate 本机属性一起设置,通过使用`spring.jpa.properties.*`(前缀在将其添加到实体管理器之前被剥离)。
下面的一行显示了为 Hibernate 设置 JPA 属性的示例:| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +属性 + +``` +spring.jpa.properties.hibernate[globally_quoted_identifiers]=true +``` + +Yaml + +``` +spring: + jpa: + properties: + hibernate: + "globally_quoted_identifiers": "true" +``` + +前面示例中的行将`hibernate.globally_quoted_identifiers`属性的值`true`传递给 Hibernate 实体管理器。 + +默认情况下,DDL 的执行(或验证)被推迟到`ApplicationContext`已经开始时。还有一个`spring.jpa.generate-ddl`标志,但如果 Hibernate 自动配置是活动的,则不会使用它,因为`ddl-auto`设置的粒度更细。 + +#### 1.3.5.在视图中打开 EntityManager + +如果你正在运行一个 Web 应用程序, Spring 在默认情况下引导寄存器[“OpenEntityManagerinViewInterceptor”](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/orm/jpa/support/OpenEntityManagerInViewInterceptor.html)以应用“Open EntityManager in View”模式,从而允许在 Web 视图中进行延迟加载。如果不希望发生这种行为,则应在`application.properties`中将`spring.jpa.open-in-view`设置为`false`。 + +### 1.4. Spring 数据 JDBC + +Spring 数据包括对 JDBC 的存储库支持,并且将为`CrudRepository`上的方法自动生成 SQL。对于更高级的查询,提供了`@Query`注释。 + +Spring 当必要的依赖关系在 Classpath 上时,启动将自动配置 Spring 数据的 JDBC 存储库。它们可以通过对`spring-boot-starter-data-jdbc`的单一依赖添加到你的项目中。如果有必要,你可以通过向应用程序添加`@EnableJdbcRepositories`注释或`JdbcConfiguration`子类来控制 Spring 数据 JDBC 的配置。 + +| |有关 Spring 数据 JDBC 的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/jdbc/docs/2.3.2/reference/html/)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.5.使用 H2 的网络控制台 + +[H2 database](https://www.h2database.com)提供了一个[基于浏览器的控制台](https://www.h2database.com/html/quickstart.html#h2_console), Spring 引导可以为你自动配置。当满足以下条件时,控制台将自动配置: + +* 你正在开发一个基于 Servlet 的 Web 应用程序。 + +* `com.h2database:h2`在 Classpath 上。 + +* 你使用的是[Spring Boot’s developer tools](using.html#using.devtools)。 + +| |如果你不使用 Spring Boot 的开发工具,但仍想使用 H2 的控制台,则可以配置值为`spring.h2.console.enabled`的`true`属性。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |H2 控制台仅用于开发过程中,因此你应该注意确保在生产过程中不将`spring.h2.console.enabled`设置为`true`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.5.1.改变 H2 控制台的路径 + +默认情况下,控制台位于`/h2-console`。你可以使用`spring.h2.console.path`属性来定制控制台的路径。 + +### 1.6.使用 Jooq + +Jooq 面向对象查询([jOOQ](https://www.jooq.org/))是[Data Geekery](https://www.datageekery.com/)中的一种流行产品,它从你的数据库生成 Java 代码,并允许你通过其 Fluent API 构建类型安全的 SQL 查询。商业版和开放源代码版都可以与 Spring boot 一起使用。 + +#### 1.6.1.代码生成 + +为了使用 Jooq 类型安全查询,你需要从数据库模式生成 Java 类。你可以按照[Jooq 用户手册](https://www.jooq.org/doc/3.14.15/manual-single-page/#jooq-in-7-steps-step3)中的说明进行操作。如果你使用`jooq-codegen-maven`插件,并且还使用`spring-boot-starter-parent`“parent POM”,则可以安全地省略该插件的``标记。你还可以使用 Spring 引导定义的版本变量(例如`h2.version`)来声明插件的数据库依赖关系。下面的清单展示了一个示例: + +``` + + org.jooq + jooq-codegen-maven + + ... + + + + com.h2database + h2 + ${h2.version} + + + + + org.h2.Driver + jdbc:h2:~/yourdatabase + + + ... + + + +``` + +#### 1.6.2.使用 DSLContext + +由 Jooq 提供的 Fluent API 是通过`org.jooq.DSLContext`接口启动的。 Spring 引导自动将`DSLContext`配置为 Spring Bean,并将其连接到你的应用程序`DataSource`。要使用`DSLContext`,你可以注入它,如以下示例所示: + +``` +import java.util.GregorianCalendar; +import java.util.List; + +import org.jooq.DSLContext; + +import org.springframework.stereotype.Component; + +import static org.springframework.boot.docs.data.sql.jooq.dslcontext.Tables.AUTHOR; + +@Component +public class MyBean { + + private final DSLContext create; + + public MyBean(DSLContext dslContext) { + this.create = dslContext; + } + +} + +``` + +| |Jooq 手册倾向于使用一个名为`create`的变量来保持`DSLContext`。| +|---|--------------------------------------------------------------------------------| + +然后,你可以使用`DSLContext`来构造查询,如下例所示: + +``` +public List authorsBornAfter1980() { + return this.create.selectFrom(AUTHOR) + .where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1))) + .fetch(AUTHOR.DATE_OF_BIRTH); + +``` + +#### 1.6.3.Jooq SQL 方言 + +除非已经配置了`spring.jooq.sql-dialect`属性,否则 Spring boot 将确定要用于数据源的 SQL 方言。如果 Spring boot 无法检测到该方言,则使用`DEFAULT`。 + +| |Spring 启动只能自动配置由开源版本的 Jooq 支持的方言。| +|---|------------------------------------------------------------------------------------------| + +#### 1.6.4.定制 Jooq + +可以通过定义你自己的`DefaultConfigurationCustomizer` Bean 来实现更高级的自定义,该自定义将在创建`org.jooq.Configuration``@Bean`之前被调用。这优先于自动配置所应用的任何内容。 + +如果你想完全控制 Jooq 配置,还可以创建自己的`org.jooq.Configuration``@Bean`。 + +### 1.7.使用 R2DBC + +反应式关系数据库连接([R2DBC](https://r2dbc.io))项目将反应式编程 API 引入到关系数据库中。R2DBC 的`io.r2dbc.spi.Connection`提供了一种处理非阻塞数据库连接的标准方法。使用`ConnectionFactory`提供连接,类似于使用 JDBC 提供`DataSource`。 + +`ConnectionFactory`配置由`spring.r2dbc.*`中的外部配置属性控制。例如,你可以在`application.properties`中声明以下部分: + +属性 + +``` +spring.r2dbc.url=r2dbc:postgresql://localhost/test +spring.r2dbc.username=dbuser +spring.r2dbc.password=dbpass +``` + +Yaml + +``` +spring: + r2dbc: + url: "r2dbc:postgresql://localhost/test" + username: "dbuser" + password: "dbpass" +``` + +| |你不需要指定驱动程序类名,因为 Spring Boot 从 R2DBC 的连接工厂发现获得驱动程序。| +|---|-------------------------------------------------------------------------------------------------------------------------------| + +| |至少应该提供 URL。
URL 中指定的信息优先于单个属性,即`name`、`username`、`password`和池选项。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |“how-to”部分包括[关于如何初始化数据库的一节](howto.html#howto.data-initialization.using-basic-sql-scripts)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------| + +要自定义由`ConnectionFactory`创建的连接,即设置在中央数据库配置中不希望(或不能)配置的特定参数,可以使用`ConnectionFactoryOptionsBuilderCustomizer``@Bean`。下面的示例展示了如何手动重写数据库端口,而其余的选项将从应用程序配置中获取: + +``` +import io.r2dbc.spi.ConnectionFactoryOptions; + +import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyR2dbcConfiguration { + + @Bean + public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryPortCustomizer() { + return (builder) -> builder.option(ConnectionFactoryOptions.PORT, 5432); + } + +} + +``` + +以下示例展示了如何设置一些 PostgreSQL 连接选项: + +``` +import java.util.HashMap; +import java.util.Map; + +import io.r2dbc.postgresql.PostgresqlConnectionFactoryProvider; + +import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyPostgresR2dbcConfiguration { + + @Bean + public ConnectionFactoryOptionsBuilderCustomizer postgresCustomizer() { + Map options = new HashMap<>(); + options.put("lock_timeout", "30s"); + options.put("statement_timeout", "60s"); + return (builder) -> builder.option(PostgresqlConnectionFactoryProvider.OPTIONS, options); + } + +} + +``` + +当`ConnectionFactory` Bean 可用时,常规的 JDBC`DataSource`自动配置将退出。如果你希望保留 JDBC`DataSource`自动配置,并且对在反应性应用程序中使用阻塞的 JDBC API 的风险感到满意,那么在应用程序中的`@Import(DataSourceAutoConfiguration.class)`类上添加`@Import(DataSourceAutoConfiguration.class)`以重新启用它。 + +#### 1.7.1.嵌入式数据库支持 + +与[JDBC 支持](features.html#data.sql.datasource.embedded)类似, Spring 引导可以自动配置嵌入式数据库,以进行反应性使用。你不需要提供任何连接 URL。你只需要对要使用的嵌入式数据库包含一个构建依赖项,如以下示例所示: + +``` + + io.r2dbc + r2dbc-h2 + runtime + +``` + +| |如果你在测试中使用此功能,你可能会注意到,无论你使用的应用程序上下文的数量如何,整个测试套件都可以重用相同的数据库,如果你想确保每个上下文都有一个单独的嵌入式数据库,
,你应该将`spring.r2dbc.generate-unique-name`设置为`true`。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.7.2.使用 DatabaseClient + +a`DatabaseClient` Bean 是自动配置的,你可以`@Autowire`它直接进入你自己的 bean 中,如以下示例所示: + +``` +import java.util.Map; + +import reactor.core.publisher.Flux; + +import org.springframework.r2dbc.core.DatabaseClient; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final DatabaseClient databaseClient; + + public MyBean(DatabaseClient databaseClient) { + this.databaseClient = databaseClient; + } + + // ... + + public Flux> someMethod() { + return this.databaseClient.sql("select * from user").fetch().all(); + } + +} + +``` + +#### 1.7.3. Spring 数据 R2DBC 存储库 + +[Spring Data R2DBC](https://spring.io/projects/spring-data-r2dbc)存储库是你可以定义以访问数据的接口。查询是根据你的方法名称自动创建的。例如,一个`CityRepository`接口可以声明一个`findAllByState(String state)`方法来查找给定状态下的所有城市。 + +对于更复杂的查询,你可以使用 Spring data 的[`Query`](https://docs.spring.io/spring-data/r2dbc/docs/1.4.2/api/org/springframework/data/r2dbc/repository/Query.html)注释来注释你的方法。 + +Spring 数据存储库通常从[`Repository`](https://docs.spring.io/spring-data/commons/docs/2.6.2/api/org/springframework/data/repository/Repository.html)或[“粗栓剂”](https://docs.spring.io/spring-data/commons/docs/2.6.2/api/org/springframework/data/repository/CrudRepository.html)接口扩展。如果使用自动配置,那么存储库将从包含主配置类(用`@EnableAutoConfiguration`或`@SpringBootApplication`注释的类)的包中向下搜索。 + +下面的示例显示了典型的 Spring 数据存储库接口定义: + +``` +import reactor.core.publisher.Mono; + +import org.springframework.data.repository.Repository; + +public interface CityRepository extends Repository { + + Mono findByNameAndStateAllIgnoringCase(String name, String state); + +} + +``` + +| |我们几乎没有触及 Spring 数据 R2DBC 的表面。有关完整的详细信息,请参见[Spring Data R2DBC reference documentation](https://docs.spring.io/spring-data/r2dbc/docs/1.4.2/reference/html/)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 2. 使用 NoSQL 技术 +---------- + +Spring Data 提供了额外的项目,这些项目帮助你访问各种 NoSQL 技术,包括: + +* [MongoDB](https://spring.io/projects/spring-data-mongodb) + +* [Neo4J](https://spring.io/projects/spring-data-neo4j) + +* [Elasticsearch](https://spring.io/projects/spring-data-elasticsearch) + +* [Redis](https://spring.io/projects/spring-data-redis) + +* [GemFire](https://spring.io/projects/spring-data-gemfire)或[Geode](https://spring.io/projects/spring-data-geode) + +* [Cassandra](https://spring.io/projects/spring-data-cassandra) + +* [Couchbase](https://spring.io/projects/spring-data-couchbase) + +* [LDAP](https://spring.io/projects/spring-data-ldap) + +Spring Boot 为 Redis、MongoDB、NEO4J、SOLR、ElasticSearch、Cassandra、CouchBase、LDAP 和 InfluxDB 提供了自动配置。你可以使用其他项目,但必须自己配置它们。请参阅[spring.io/projects/spring-data](https://spring.io/projects/spring-data)上的相应参考文档。 + +### 2.1.雷迪斯 + +[Redis](https://redis.io/)是一个缓存、消息代理和功能丰富的键值存储。 Spring Boot 为[Lettuce](https://github.com/lettuce-io/lettuce-core/)和[Jedis](https://github.com/xetorthio/jedis/)客户端库以及[Spring Data Redis](https://github.com/spring-projects/spring-data-redis)在它们之上提供的抽象提供了基本的自动配置。 + +有一个`spring-boot-starter-data-redis`“starter”可以方便地收集依赖项。默认情况下,它使用[Lettuce](https://github.com/lettuce-io/lettuce-core/)。该启动器同时处理传统应用程序和被动应用程序。 + +| |我们还提供了`spring-boot-starter-data-redis-reactive`“starter”,以便与具有反应性支持的其他存储保持一致。| +|---|----------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.1.连接到 Redis + +可以像其他任何实例 Spring Bean 一样,注入一个自动配置的`RedisConnectionFactory`、`StringRedisTemplate`或 vanilla`RedisTemplate`实例。默认情况下,实例尝试以`localhost:6379`连接到 Redis 服务器。下面的清单显示了这样的示例 Bean: + +``` +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final StringRedisTemplate template; + + public MyBean(StringRedisTemplate template) { + this.template = template; + } + + // ... + + public Boolean someMethod() { + return this.template.hasKey("spring"); + } + +} + +``` + +| |你还可以注册任意数量的实现`LettuceClientConfigurationBuilderCustomizer`的 bean,以进行更高级的自定义。“clientresources”也可以使用`ClientResourcesBuilderCustomizer`进行自定义。
如果你使用 JEDIS,`JedisClientConfigurationBuilderCustomizer`也是可用的。
或者,你可以注册类型为`RedisStandaloneConfiguration`、`RedisSentinelConfiguration`的 Bean,或`RedisClusterConfiguration`来完全控制配置。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你添加了你自己的`@Bean`的任何自动配置的类型,它将替换默认的类型(除了在`RedisTemplate`的情况下,当排除是基于 Bean 名称时,`redisTemplate`,而不是它的类型)。 + +默认情况下,如果`commons-pool2`在 Classpath 上,则自动配置池连接工厂。 + +### 2.2.MongoDB + +[MongoDB](https://www.mongodb.com/)是一个开源的 NoSQL 文档数据库,它使用类似 JSON 的模式,而不是传统的基于表的关系数据。 Spring Boot 为使用 MongoDB 提供了几种便利,包括`spring-boot-starter-data-mongodb`和`spring-boot-starter-data-mongodb-reactive`“starters”。 + +#### 2.2.1.连接到 MongoDB 数据库 + +要访问 MongoDB 数据库,你可以插入一个自动配置的`org.springframework.data.mongodb.MongoDatabaseFactory`。默认情况下,实例尝试以`mongodb://localhost/test`连接到 MongoDB 服务器。下面的示例展示了如何连接到 MongoDB 数据库: + +``` +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +import org.springframework.data.mongodb.MongoDatabaseFactory; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final MongoDatabaseFactory mongo; + + public MyBean(MongoDatabaseFactory mongo) { + this.mongo = mongo; + } + + // ... + + public MongoCollection someMethod() { + MongoDatabase db = this.mongo.getMongoDatabase(); + return db.getCollection("users"); + } + +} + +``` + +如果你已经定义了自己的`MongoClient`,它将被用来自动配置一个合适的`MongoDatabaseFactory`。 + +自动配置的`MongoClient`是使用`MongoClientSettings` Bean 创建的。如果你已经定义了自己的`MongoClientSettings`,则将不加修改地使用它,并且`spring.data.mongodb`属性将被忽略。否则,将自动配置`MongoClientSettings`,并将`spring.data.mongodb`属性应用到它。在这两种情况下,都可以声明一个或多个`MongoClientSettingsBuilderCustomizer`bean 来微调`MongoClientSettings`配置。每个都将按顺序调用`MongoClientSettings.Builder`,这是用来构建`MongoClientSettings`的。 + +你可以设置`spring.data.mongodb.uri`属性来更改 URL 并配置其他设置,例如*复制集*,如下例所示: + +属性 + +``` +spring.data.mongodb.uri=mongodb://user:[email protected]:12345,mongo2.example.com:23456/test +``` + +Yaml + +``` +spring: + data: + mongodb: + uri: "mongodb://user:[email protected]:12345,mongo2.example.com:23456/test" +``` + +或者,你可以使用离散属性指定连接细节。例如,你可以在`application.properties`中声明以下设置: + +属性 + +``` +spring.data.mongodb.host=mongoserver.example.com +spring.data.mongodb.port=27017 +spring.data.mongodb.database=test +spring.data.mongodb.username=user +spring.data.mongodb.password=secret +``` + +Yaml + +``` +spring: + data: + mongodb: + host: "mongoserver.example.com" + port: 27017 + database: "test" + username: "user" + password: "secret" +``` + +| |如果没有指定`spring.data.mongodb.port`,则使用`27017`的默认值。
你可以从前面显示的示例中删除这一行。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果不使用 Spring data MongoDB,则可以注入`MongoClient` Bean 而不是使用`MongoDatabaseFactory`。
如果你想完全控制建立 MongoDB 连接,还可以声明你自己的`MongoDatabaseFactory`或`MongoClient` Bean。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你使用的是反应式驱动程序,则 SSL 需要 Netty。
如果 Netty 可用,并且使用的工厂尚未自定义,则自动配置将自动配置该工厂。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.2.2.MongoTemplate + +[Spring Data MongoDB](https://spring.io/projects/spring-data-mongodb)提供了一个[`MongoTemplate`](https://docs.spring.io/spring-data/mongodb/docs/3.3.2/api/org/springframework/data/mongodb/core/MongoTemplate.html)类,其设计与 Spring 的`JdbcTemplate`非常相似。与`JdbcTemplate`一样, Spring 引导自动配置一个 Bean,以便你插入模板,如下所示: + +``` +import com.mongodb.client.MongoCollection; +import org.bson.Document; + +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final MongoTemplate mongoTemplate; + + public MyBean(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + } + + // ... + + public MongoCollection someMethod() { + return this.mongoTemplate.getCollection("users"); + } + +} + +``` + +有关完整的详细信息,请参见[“蒙古行动”Javadoc](https://docs.spring.io/spring-data/mongodb/docs/3.3.2/api/org/springframework/data/mongodb/core/MongoOperations.html)。 + +#### 2.2.3. Spring 数据 MongoDB 存储库 + +Spring 数据包括对 MongoDB 的存储库支持。与前面讨论的 JPA 存储库一样,基本原理是基于方法名自动构造查询。 + +实际上, Spring data JPA 和 Spring data MongoDB 共享相同的公共基础设施。你可以使用前面的 JPA 示例,假设`City`现在是一个 MongoDB 数据类,而不是 JPA `@Entity`,它以相同的方式工作,如以下示例所示: + +``` +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.repository.Repository; + +public interface CityRepository extends Repository { + + Page findAll(Pageable pageable); + + City findByNameAndStateAllIgnoringCase(String name, String state); + +} + +``` + +| |你可以使用`@EntityScan`注释自定义文档扫描位置。| +|---|------------------------------------------------------------------------------------| + +| |有关 Spring Data MongoDB 的完整详细信息,包括其丰富的对象映射技术,请参见其[参考文献](https://spring.io/projects/spring-data-mongodb)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.2.4.嵌入式 Mongo + +Spring Boot 为[Embedded Mongo](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo)提供自动配置。要在 Spring 引导应用程序中使用它,请在`de.flapdoodle.embed:de.flapdoodle.embed.mongo`上添加一个依赖项,并将`spring.mongodb.embedded.version`属性设置为与应用程序将在生产中使用的 MongoDB 版本匹配。 + +| |默认的下载配置允许访问[Embedded Mongo’s `Version` class](https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo/blob/de.flapdoodle.embed.mongo-3.0.0/src/main/java/de/flapdoodle/embed/mongo/distribution/Version.java)中列出的大多数版本以及其他一些版本。
在试图下载服务器时,配置一个不可访问的版本将导致一个错误。
这样的错误可以通过定义一个适当配置的`DownloadConfigBuilderCustomizer` Bean 来纠正。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +可以通过设置`spring.data.mongodb.port`属性来配置 Mongo 监听的端口。要使用随机分配的空闲端口,请使用 0。由`MongoAutoConfiguration`创建的`MongoClient`被自动配置为使用随机分配的端口。 + +| |如果不配置自定义端口,则默认情况下,嵌入式支持使用随机端口(而不是 27017)。| +|---|--------------------------------------------------------------------------------------------------------------| + +如果在 Classpath 上有 SLF4J,则 Mongo 产生的输出将自动路由到名为`org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo`的记录器。 + +你可以声明自己的`IMongodConfig`和`IRuntimeConfig`bean,以控制 Mongo 实例的配置和日志路由。可以通过声明`DownloadConfigBuilderCustomizer` Bean 来定制下载配置。 + +### 2.3.NEO4J + +[Neo4j](https://neo4j.com/)是一个开源的 NoSQL 图数据库,它使用由第一类关系连接的节点的丰富数据模型,与传统的 RDBMS 方法相比,该模型更适合于连接的大数据。 Spring Boot 为使用 NEO4j 提供了几种便利,包括`spring-boot-starter-data-neo4j`“starter”。 + +#### 2.3.1.连接到 NEO4J 数据库 + +要访问 NEO4J 服务器,可以插入自动配置的`org.neo4j.driver.Driver`。默认情况下,该实例尝试使用 Bolt 协议在`localhost:7687`处连接到 NEO4J 服务器。下面的示例展示了如何注入一个 NEO4j`Driver`,它使你能够访问`Session`,其中包括: + +``` +import org.neo4j.driver.Driver; +import org.neo4j.driver.Session; +import org.neo4j.driver.Values; + +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final Driver driver; + + public MyBean(Driver driver) { + this.driver = driver; + } + + // ... + + public String someMethod(String message) { + try (Session session = this.driver.session()) { + return session.writeTransaction((transaction) -> transaction + .run("CREATE (a:Greeting) SET a.message = $message RETURN a.message + ', from node ' + id(a)", + Values.parameters("message", message)) + .single().get(0).asString()); + } + } + +} + +``` + +你可以使用`spring.neo4j.*`属性配置驱动程序的各个方面。下面的示例展示了如何配置要使用的 URI 和凭据: + +属性 + +``` +spring.neo4j.uri=bolt://my-server:7687 +spring.neo4j.authentication.username=neo4j +spring.neo4j.authentication.password=secret +``` + +Yaml + +``` +spring: + neo4j: + uri: "bolt://my-server:7687" + authentication: + username: "neo4j" + password: "secret" +``` + +自动配置的`Driver`是使用`ConfigBuilder`创建的。要微调其配置,请声明一个或多个`ConfigBuilderCustomizer`bean。每个都将按顺序调用`ConfigBuilder`,这是用来构建`Driver`的。 + +#### 2.3.2. Spring 数据 NEO4j 存储库 + +Spring 数据存储库包括对 NEO4j 的支持。有关 Spring 数据 NEO4j 的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/neo4j/docs/6.2.2/reference/html/)。 + +Spring 数据 NEO4J 与 Spring 数据共享公共基础设施 JPA,就像许多其他 Spring 数据模块所做的那样。你可以从前面的 JPA 示例中将`City`定义为 Spring 数据 NEO4j`@Node`,而不是 JPA `@Entity`,并且存储库抽象以相同的方式工作,如以下示例所示: + +``` +import java.util.Optional; + +import org.springframework.data.neo4j.repository.Neo4jRepository; + +public interface CityRepository extends Neo4jRepository { + + Optional findOneByNameAndState(String name, String state); + +} + +``` + +`spring-boot-starter-data-neo4j`“starter”支持存储库和事务管理。 Spring 使用`Neo4jTemplate`或`ReactiveNeo4jTemplate`bean,启动同时支持经典的和反应性的 NEO4j 存储库。 Classpath 上的项目反应器可用时,反应方式也是自动配置的。 + +你可以通过在`@Configuration`- Bean 上分别使用`@EnableNeo4jRepositories`和`@EntityScan`来定制查找存储库和实体的位置。 + +| |在使用反应样式的应用程序中,`ReactiveTransactionManager`不会自动配置。
以启用事务管理,以下 Bean 必须在你的配置中定义:

``
导入 org.NEO4j.driver.driver;

Import org.springframework.context.annotation. Bean;
import org.springframework.context.annation.contextation.conventation;import org.datforum.data.data4j.core.reactiveasr;



``| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.4.SOLR + +[Apache Solr](https://lucene.apache.org/solr/)是一个搜索引擎。 Spring 启动提供了用于 SOLR5 客户端库的基本自动配置。 + +#### 2.4.1.连接到 SOLR + +你可以像注入任何其他 Spring Bean 一样注入一个自动配置的`SolrClient`实例。默认情况下,实例尝试连接到`[localhost:8983/solr](http://localhost:8983/solr)`的服务器。以下示例显示了如何注入 SOLR Bean: + +``` +import java.io.IOException; + +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.SolrPingResponse; + +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final SolrClient solr; + + public MyBean(SolrClient solr) { + this.solr = solr; + } + + // ... + + public SolrPingResponse someMethod() throws SolrServerException, IOException { + return this.solr.ping("users"); + } + +} + +``` + +如果你添加了你自己的`@Bean`类型的`SolrClient`,它将替换缺省值。 + +### 2.5.Elasticsearch + +[Elasticsearch](https://www.elastic.co/products/elasticsearch)是一个开源、分布式、RESTful 搜索和分析引擎。 Spring Boot 为 ElasticSearch 客户端提供了基本的自动配置。 + +Spring 引导支持几个客户端: + +* 官方的 Java“low level”和“high level”REST 客户端 + +* Spring Data ElasticSearch 提供的`ReactiveElasticsearchClient` + +Spring Boot 提供了一个专用的“starter”,`spring-boot-starter-data-elasticsearch`。 + +#### 2.5.1.使用 REST 客户端连接到 ElasticSearch + +ElasticSearch 附带[两个不同的 REST 客户机](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html),你可以使用它来查询集群:“低级别”客户端和“高级别”客户端。 Spring Boot 提供对“高级别”客户端的支持,该客户端附带`org.elasticsearch.client:elasticsearch-rest-high-level-client`。此外, Spring Boot 基于 Spring Framework 的`WebClient`提供了对反应式客户端的支持,该客户端附带`org.springframework.data:spring-data-elasticsearch`。默认情况下,客户机将以`[localhost:9200](http://localhost:9200)`为目标。你可以使用`spring.elasticsearch.*`属性来进一步优化客户机的配置方式,如下例所示: + +属性 + +``` +spring.elasticsearch.uris=https://search.example.com:9200 +spring.elasticsearch.socket-timeout=10s +spring.elasticsearch.username=user +spring.elasticsearch.password=secret +``` + +Yaml + +``` +spring: + elasticsearch: + uris: "https://search.example.com:9200" + socket-timeout: "10s" + username: "user" + password: "secret" +``` + +##### 使用 resthigLevelClient 连接到 ElasticSearch#### + +如果在 Classpath 上有`elasticsearch-rest-high-level-client`,则 Spring 引导将自动配置并注册一个`RestHighLevelClient` Bean。除了前面描述的属性之外,要微调`RestHighLevelClient`,你可以注册任意数量的 bean,这些 bean 实现`RestClientBuilderCustomizer`以进行更高级的自定义。要完全控制其注册,请定义`RestClientBuilder` Bean。 + +| |如果你的应用程序需要访问“low level”`RestClient`,则可以通过在自动配置的`client.getLowLevelClient()`上调用`client.getLowLevelClient()`来获得它。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此外,如果`elasticsearch-rest-client-sniffer`在 Classpath 上,则`Sniffer`被自动配置为自动从正在运行的 ElasticSearch 集群中发现节点,并将它们设置在`RestHighLevelClient` Bean 上。你可以进一步调整`Sniffer`的配置方式,如以下示例所示: + +属性 + +``` +spring.elasticsearch.restclient.sniffer.interval=10m +spring.elasticsearch.restclient.sniffer.delay-after-failure=30s +``` + +Yaml + +``` +spring: + elasticsearch: + restclient: + sniffer: + interval: "10m" + delay-after-failure: "30s" +``` + +##### 使用 ReactiveElasticSearchClient 连接到 ElasticSearch#### + +[Spring Data Elasticsearch](https://spring.io/projects/spring-data-elasticsearch)提供`ReactiveElasticsearchClient`,用于以反应方式查询 ElasticSearch 实例。它是建立在 WebFlux 的`WebClient`之上的,因此`spring-boot-starter-elasticsearch`和`spring-boot-starter-webflux`依赖关系都有助于启用此支持。 + +默认情况下, Spring 启动将自动配置并注册`ReactiveElasticsearchClient`。除了前面描述的属性外,`spring.elasticsearch.webclient.*`属性还可用于配置特定于反应性的设置,如以下示例所示: + +属性 + +``` +spring.elasticsearch.webclient.max-in-memory-size=1MB +``` + +Yaml + +``` +spring: + elasticsearch: + webclient: + max-in-memory-size: "1MB" +``` + +如果`spring.elasticsearch.`**and `spring.elasticsearch.webclient.`**配置属性不够,并且希望完全控制客户机配置,则可以注册一个自定义`ClientConfiguration` Bean。 + +#### 2.5.2.通过使用 Spring 数据 #### 连接到 ElasticSearch + +要连接到 ElasticSearch,必须定义一个`RestHighLevelClient` Bean,通过 Spring 引导自动配置,或者由应用程序手动提供(请参见前面的部分)。有了这种配置,就可以像其他任何 Spring Bean 一样注入“ElasticSearchRestTemplate”,如以下示例所示: + +``` +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final ElasticsearchRestTemplate template; + + public MyBean(ElasticsearchRestTemplate template) { + this.template = template; + } + + // ... + + public boolean someMethod(String id) { + return this.template.exists(id, User.class); + } + +} + +``` + +在存在`spring-data-elasticsearch`和使用`WebClient`所需的依赖关系(通常为`spring-boot-starter-webflux`)的情况下, Spring 引导还可以自动配置一个[ReactiveElasticSearchClient](features.html#data.nosql.elasticsearch.connecting-using-rest.webclient)和一个`ReactiveElasticsearchTemplate`作为 bean。他们相当于其他 REST 客户的被动反应。 + +#### 2.5.3. Spring 数据弹性搜索库 + +Spring 数据包括对 ElasticSearch 的存储库支持。与前面讨论的 JPA 存储库一样,基本原则是根据方法名自动为你构造查询。 + +实际上, Spring 数据 JPA 和 Spring 数据弹性搜索共享相同的公共基础设施。你可以使用前面的 JPA 示例,并且假设`City`现在是 ElasticSearch`@Document`类,而不是 JPA `@Entity`类,它以同样的方式工作。 + +| |有关 Spring 数据弹性搜索的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 使用`ElasticsearchRestTemplate`或`ReactiveElasticsearchTemplate`bean,启动同时支持经典的和反应的 Elasticsearch 存储库。考虑到存在所需的依赖关系,这些 bean 很可能是通过 Spring 引导自动配置的。 + +如果你希望使用自己的模板来支持 ElasticSearch 存储库,则可以添加自己的`ElasticsearchRestTemplate`或`ElasticsearchOperations``@Bean`,只要它的名称为`"elasticsearchTemplate"`。这同样适用于`ReactiveElasticsearchTemplate`和`ReactiveElasticsearchOperations`,其 Bean 名称为`"reactiveElasticsearchTemplate"`。 + +你可以选择使用以下属性禁用存储库支持: + +属性 + +``` +spring.data.elasticsearch.repositories.enabled=false +``` + +Yaml + +``` +spring: + data: + elasticsearch: + repositories: + enabled: false +``` + +### 2.6.卡桑德拉 + +[Cassandra](https://cassandra.apache.org/)是一个开源的分布式数据库管理系统,旨在处理跨许多商品服务器的大量数据。 Spring Boot 提供了用于 Cassandra 的自动配置以及由[Spring Data Cassandra](https://github.com/spring-projects/spring-data-cassandra)提供的在其之上的抽象。有一个`spring-boot-starter-data-cassandra`“starter”,可以方便地收集依赖项。 + +#### 2.6.1.连接到 Cassandra + +可以像使用任何其他 Spring Bean 一样,注入一个自动配置的`CassandraTemplate`或一个 Cassandra`CqlSession`实例。`spring.data.cassandra.*`属性可用于自定义连接。通常,你提供`keyspace-name`和`contact-points`以及本地数据中心名称,如以下示例所示: + +属性 + +``` +spring.data.cassandra.keyspace-name=mykeyspace +spring.data.cassandra.contact-points=cassandrahost1:9042,cassandrahost2:9042 +spring.data.cassandra.local-datacenter=datacenter1 +``` + +Yaml + +``` +spring: + data: + cassandra: + keyspace-name: "mykeyspace" + contact-points: "cassandrahost1:9042,cassandrahost2:9042" + local-datacenter: "datacenter1" +``` + +如果你的所有联系点的端口都是相同的,那么你可以使用快捷方式,并且只指定主机名,如下例所示: + +属性 + +``` +spring.data.cassandra.keyspace-name=mykeyspace +spring.data.cassandra.contact-points=cassandrahost1,cassandrahost2 +spring.data.cassandra.local-datacenter=datacenter1 +``` + +Yaml + +``` +spring: + data: + cassandra: + keyspace-name: "mykeyspace" + contact-points: "cassandrahost1,cassandrahost2" + local-datacenter: "datacenter1" +``` + +| |这两个示例与端口默认设置`9042`相同。
如果需要配置端口,请使用`spring.data.cassandra.port`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------| + +| |Cassandra 驱动程序有自己的配置基础结构在 Classpath 的根位置加载`application.conf`。

Spring boot 默认情况下不查找这样的文件,但可以使用`spring.data.cassandra.config`加载一个文件。
如果一个属性在`spring.data.cassandra.*`和配置文件中都存在,`spring.data.cassandra.*`中的值优先。

对于更高级的驱动程序自定义,你可以注册任意数量的实现`DriverConfigLoaderBuilderCustomizer`的 bean。
`CqlSession`的`CqlSession`可以使用类型为`CqlSessionBuilderCustomizer`的 Bean 进行自定义。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果使用`CqlSessionBuilder`创建多个`CqlSession`bean,请记住构建器是可变的,因此请确保为每个会话注入一个新的副本。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的代码清单显示了如何注入 Cassandra Bean: + +``` +import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final CassandraTemplate template; + + public MyBean(CassandraTemplate template) { + this.template = template; + } + + // ... + + public long someMethod() { + return this.template.count(User.class); + } + +} + +``` + +如果你添加了你自己的`@Bean`类型的`CassandraTemplate`,它将替换缺省值。 + +#### 2.6.2. Spring 数据 Cassandra 存储库 + +Spring 数据包括对 Cassandra 的基本存储库支持。目前,这比前面讨论的 JPA 存储库更加有限,并且需要用`@Query`注释查找方法。 + +| |有关 Spring 数据 Cassandra 的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/cassandra/docs/)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------| + +### 2.7.Couchbase + +[Couchbase](https://www.couchbase.com/)是一个开源的、分布式的、多模型的面向 NoSQL 文档的数据库,它为交互式应用程序进行了优化。 Spring Boot 提供了对 Couchbase 的自动配置,以及由[Spring Data Couchbase](https://github.com/spring-projects/spring-data-couchbase)提供的在其之上的抽象。有`spring-boot-starter-data-couchbase`和`spring-boot-starter-data-couchbase-reactive`“starter”用于以方便的方式收集依赖项。 + +#### 2.7.1.连接到 Couchbase + +你可以通过添加 CouchBase SDK 和一些配置来获得`Cluster`。`spring.couchbase.*`属性可用于自定义连接。通常,你提供[连接字符串](https://github.com/couchbaselabs/sdk-rfcs/blob/master/rfc/0011-connection-string.md)、用户名和密码,如以下示例所示: + +属性 + +``` +spring.couchbase.connection-string=couchbase://192.168.1.123 +spring.couchbase.username=user +spring.couchbase.password=secret +``` + +Yaml + +``` +spring: + couchbase: + connection-string: "couchbase://192.168.1.123" + username: "user" + password: "secret" +``` + +也可以自定义一些`ClusterEnvironment`设置。例如,以下配置更改了用于打开新的`Bucket`并启用 SSL 支持的超时时间: + +属性 + +``` +spring.couchbase.env.timeouts.connect=3s +spring.couchbase.env.ssl.key-store=/location/of/keystore.jks +spring.couchbase.env.ssl.key-store-password=secret +``` + +Yaml + +``` +spring: + couchbase: + env: + timeouts: + connect: "3s" + ssl: + key-store: "/location/of/keystore.jks" + key-store-password: "secret" +``` + +| |查看`spring.couchbase.env.*`属性以获得更多详细信息。
要获得更多控制权,可以使用一个或多个`ClusterEnvironmentBuilderCustomizer`bean。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.7.2. Spring 数据 CouchBase 存储库 + +Spring 数据包括对 CouchBase 的存储库支持。有关 Spring Data Couchbase 的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/couchbase/docs/4.3.2/reference/html/)。 + +如果`CouchbaseClientFactory` Bean 可用,则可以像使用任何其他 Spring Bean 一样注入自动配置的`CouchbaseTemplate`实例。如上所述,当`Cluster`可用时,并且指定了一个 bucket 名称时,就会发生这种情况: + +属性 + +``` +spring.data.couchbase.bucket-name=my-bucket +``` + +Yaml + +``` +spring: + data: + couchbase: + bucket-name: "my-bucket" +``` + +以下示例显示了如何注入`CouchbaseTemplate` Bean: + +``` +import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final CouchbaseTemplate template; + + public MyBean(CouchbaseTemplate template) { + this.template = template; + } + + // ... + + public String someMethod() { + return this.template.getBucketName(); + } + +} + +``` + +你可以在自己的配置中定义一些 bean 来覆盖自动配置提供的那些 bean: + +* a`CouchbaseMappingContext``@Bean`,其名称为`couchbaseMappingContext`。 + +* a`CustomConversions``@Bean`,其名称为`couchbaseCustomConversions`。 + +* a`CouchbaseTemplate``@Bean`,名称为`couchbaseTemplate`。 + +为了避免在自己的配置中对这些名称进行硬编码,你可以重用 Spring Data Couchbase 提供的`BeanNames`。例如,你可以自定义要使用的转换器,如下所示: + +``` +import org.assertj.core.util.Arrays; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.config.BeanNames; +import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions; + +@Configuration(proxyBeanMethods = false) +public class MyCouchbaseConfiguration { + + @Bean(BeanNames.COUCHBASE_CUSTOM_CONVERSIONS) + public CouchbaseCustomConversions myCustomConversions() { + return new CouchbaseCustomConversions(Arrays.asList(new MyConverter())); + } + +} + +``` + +### 2.8.LDAP + +[LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)(轻量级目录访问协议)是一种开放的、与供应商无关的行业标准应用程序协议,用于在 IP 网络上访问和维护分布式目录信息服务。 Spring Boot 提供了对任何兼容的 LDAP 服务器的自动配置,以及对来自[UnboundID](https://ldap.com/unboundid-ldap-sdk-for-java/)的嵌入式内存中 LDAP 服务器的支持。 + +LDAP 抽象由[Spring Data LDAP](https://github.com/spring-projects/spring-data-ldap)提供。有一个`spring-boot-starter-data-ldap`“starter”可以方便地收集依赖项。 + +#### 2.8.1.连接到 LDAP 服务器 + +要连接到 LDAP 服务器,请确保声明对`spring-boot-starter-data-ldap`“starter”或`spring-ldap-core`的依赖关系,然后在应用程序中声明服务器的 URL。属性,如以下示例所示: + +属性 + +``` +spring.ldap.urls=ldap://myserver:1235 +spring.ldap.username=admin +spring.ldap.password=secret +``` + +Yaml + +``` +spring: + ldap: + urls: "ldap://myserver:1235" + username: "admin" + password: "secret" +``` + +如果需要自定义连接设置,可以使用`spring.ldap.base`和`spring.ldap.base-environment`属性。 + +基于这些设置,可以自动配置`LdapContextSource`。如果`DirContextAuthenticationStrategy` Bean 是可用的,则它与自动配置的`LdapContextSource`相关联。如果你需要自定义它,例如使用`PooledContextSource`,你仍然可以注入自动配置的`LdapContextSource`。请确保将你定制的`ContextSource`标记为`@Primary`,以便自动配置的`LdapTemplate`使用它。 + +#### 2.8.2. Spring 数据 LDAP 存储库 + +Spring 数据包括对 LDAP 的存储库支持。有关 Spring 数据 LDAP 的完整详细信息,请参见[参考文献](https://docs.spring.io/spring-data/ldap/docs/1.0.x/reference/html/)。 + +还可以像使用任何其他 Spring Bean 一样,注入一个自动配置的`LdapTemplate`实例,如以下示例所示: + +``` +import java.util.List; + +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final LdapTemplate template; + + public MyBean(LdapTemplate template) { + this.template = template; + } + + // ... + + public List someMethod() { + return this.template.findAll(User.class); + } + +} + +``` + +#### 2.8.3.嵌入式内存 LDAP 服务器 + +出于测试目的, Spring 引导支持从[UnboundID](https://ldap.com/unboundid-ldap-sdk-for-java/)自动配置内存中的 LDAP 服务器。要配置服务器,请向`com.unboundid:unboundid-ldapsdk`添加一个依赖项,并声明一个`spring.ldap.embedded.base-dn`属性,如下所示: + +Properties + +``` +spring.ldap.embedded.base-dn=dc=spring,dc=io +``` + +Yaml + +``` +spring: + ldap: + embedded: + base-dn: "dc=spring,dc=io" +``` + +| |可以定义多个 base-dn 值,但是,由于专有名称通常包含逗号,因此必须使用正确的符号来定义它们。

在 YAML 文件中,可以使用 YAML 列表符号。在属性文件中,必须将索引作为属性名称的一部分包括在内:

属性

``
ldap.embedded.base-dn[0]=dc=“542”/> Spring.ldap.ldap.embedded.base-dn Spring =dc=“dc=”/>gt=“dc=”/>pivottr==`544“/><>“lgt====5446”/>“lgt>”."peddlgt===| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,服务器从一个随机端口启动,并触发常规的 LDAP 支持。不需要指定`spring.ldap.urls`属性。 + +如果在你的 Classpath 上有一个`schema.ldif`文件,则该文件将用于初始化服务器。如果希望从其他资源加载初始化脚本,还可以使用`spring.ldap.embedded.ldif`属性。 + +默认情况下,标准模式用于验证`LDIF`文件。你可以通过设置`spring.ldap.embedded.validation.enabled`属性来完全关闭验证。如果你有自定义属性,你可以使用`spring.ldap.embedded.validation.schema`来定义你的自定义属性类型或对象类。 + +### 2.9.影响 b + +[InfluxDB](https://www.influxdata.com/)是一个开放源代码的时间序列数据库,该数据库经过优化,可快速、高可用性地存储和检索操作监控、应用度量、物联网传感器数据和实时分析等领域的时间序列数据。 + +#### 2.9.1.连接到 InfluxDB + +Spring 引导自动配置`InfluxDB`实例,前提是`influxdb-java`客户端在 Classpath 上并且设置了数据库的 URL,如以下示例所示: + +Properties + +``` +spring.influx.url=https://172.0.0.1:8086 +``` + +Yaml + +``` +spring: + influx: + url: "https://172.0.0.1:8086" +``` + +如果到 InfluxDB 的连接需要用户和密码,则可以相应地设置`spring.influx.user`和`spring.influx.password`属性。 + +InfluxDB 依赖于 OkHTTP。如果你需要调优幕后使用的 HTTP 客户端`InfluxDB`,则可以注册一个`InfluxDbOkHttpClientBuilderProvider` Bean。 + +如果需要对配置进行更多控制,可以考虑注册`InfluxDbCustomizer` Bean。 + +## 3. 接下来读什么? +---------- + +你现在应该对如何在各种数据技术中使用 Spring 引导有了一种感觉。从这里,你可以了解 Spring Boot 对各种[消息传递技术](messaging.html#messaging)的支持,以及如何在应用程序中启用它们。 + diff --git a/docs/spring-boot/deployment.md b/docs/spring-boot/deployment.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8bbb85c11eaba4d2e574acfe9ae26abb38159c75 100644 --- a/docs/spring-boot/deployment.md +++ b/docs/spring-boot/deployment.md @@ -0,0 +1,573 @@ +# 部署 Spring 引导应用程序 + +Spring 在部署应用程序时,Boot 的灵活打包选项提供了很多选择。你可以将 Spring 引导应用程序部署到各种云平台,部署到虚拟机/真机,或者使它们在 UNIX 系统中完全可执行。 + +本节将介绍一些更常见的部署场景。 + +## 1. 部署到云上 + +Spring Boot 的可执行 JAR 已经为大多数流行的云 PaaS(Platform-as-a-Service,即平台即服务)提供商准备好了。这些供应商往往要求你“带上自己的容器”。它们管理应用程序进程(而不是专门的 Java 应用程序),因此它们需要一个中间层,将*你的*应用程序适应于运行进程的*Cloud’s*概念。 + +Heroku 和 Cloud Foundry 这两家颇受欢迎的云供应商采用了一种“BuildPack”方法。BuildPack 将部署的代码封装在应用程序*开始*所需的任何内容中。它可能是一个 JDK 和对`java`的调用、一个嵌入式 Web 服务器或一个成熟的应用程序服务器。BuildPack 是可插入的,但在理想情况下,你应该能够通过尽可能少的自定义来实现它。这减少了不在你控制范围内的功能的占用。它最大限度地减少了开发环境和生产环境之间的差异。 + +理想情况下,你的应用程序,比如 Spring 可执行的引导程序 jar,包含了在其中运行所需的所有内容。 + +在这一部分中,我们将研究如何在“Getting Started”部分中获得[我们开发的应用程序](getting-started.html#getting-started.first-application)并在云中运行。 + +### 1.1.Cloud Foundry + +Cloud Foundry 提供了默认的构建包,如果没有指定其他构建包,这些构建包就会发挥作用。Cloud Foundry[Java buildpack](https://github.com/cloudfoundry/java-buildpack)对 Spring 应用程序具有出色的支持,包括 Spring 启动。你可以部署独立的可执行程序 jar 以及传统的`.war`打包应用程序。 + +一旦构建了应用程序(例如,通过使用`mvn clean package`)并拥有[installed the `cf` command line tool](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html),就可以使用`cf push`命令部署应用程序,并替换已编译`.jar`的路径。在推送应用程序之前,一定要有[logged in with your `cf` command line client](https://docs.cloudfoundry.org/cf-cli/getting-started.html#login)。下面的一行显示了使用`cf push`命令部署应用程序的情况: + +``` +$ cf push acloudyspringtime -p target/demo-0.0.1-SNAPSHOT.jar +``` + +| |在前面的示例中,我们用`acloudyspringtime`代替你给出的任何值`cf`作为应用程序的名称。| +|---|-----------------------------------------------------------------------------------------------------------------------------| + +有关更多选项,请参见[“CF 推送”文档](https://docs.cloudfoundry.org/cf-cli/getting-started.html#push)。如果在同一目录中存在一个 Cloud Foundry[`manifest.yml`](https://docs.cloudfoundry.org/devguide/deploy-apps/manifest.html)文件,则将考虑该文件。 + +此时,`cf`开始上载应用程序,生成类似于以下示例的输出: + +``` +Uploading acloudyspringtime... OK +Preparing to start acloudyspringtime... OK +-----> Downloaded app package (8.9M) +-----> Java Buildpack Version: v3.12 (offline) | https://github.com/cloudfoundry/java-buildpack.git#6f25b7e +-----> Downloading Open Jdk JRE 1.8.0_121 from https://java-buildpack.cloudfoundry.org/openjdk/trusty/x86_64/openjdk-1.8.0_121.tar.gz (found in cache) + Expanding Open Jdk JRE to .java-buildpack/open_jdk_jre (1.6s) +-----> Downloading Open JDK Like Memory Calculator 2.0.2_RELEASE from https://java-buildpack.cloudfoundry.org/memory-calculator/trusty/x86_64/memory-calculator-2.0.2_RELEASE.tar.gz (found in cache) + Memory Settings: -Xss349K -Xmx681574K -XX:MaxMetaspaceSize=104857K -Xms681574K -XX:MetaspaceSize=104857K +-----> Downloading Container Certificate Trust Store 1.0.0_RELEASE from https://java-buildpack.cloudfoundry.org/container-certificate-trust-store/container-certificate-trust-store-1.0.0_RELEASE.jar (found in cache) + Adding certificates to .java-buildpack/container_certificate_trust_store/truststore.jks (0.6s) +-----> Downloading Spring Auto Reconfiguration 1.10.0_RELEASE from https://java-buildpack.cloudfoundry.org/auto-reconfiguration/auto-reconfiguration-1.10.0_RELEASE.jar (found in cache) +Checking status of app 'acloudyspringtime'... + 0 of 1 instances running (1 starting) + ... + 0 of 1 instances running (1 starting) + ... + 0 of 1 instances running (1 starting) + ... + 1 of 1 instances running (1 running) + +App started +``` + +恭喜你!应用程序现已上线! + +应用程序启用后,你可以使用`cf apps`命令来验证部署的应用程序的状态,如以下示例所示: + +``` +$ cf apps +Getting applications in ... +OK + +name requested state instances memory disk urls +... +acloudyspringtime started 1/1 512M 1G acloudyspringtime.cfapps.io +... +``` + +一旦 Cloud Foundry 确认你的应用程序已被部署,你就应该能够在给定的 URI 中找到该应用程序。在前面的示例中,你可以在`https://acloudyspringtime.cfapps.io/`处找到它。 + +#### 1.1.1.对服务的约束 + +默认情况下,有关正在运行的应用程序的元数据以及服务连接信息作为环境变量公开给应用程序(例如:`$VCAP_SERVICES`)。这一架构决定是由于 Cloud Foundry 的多语种(任何语言和平台都可以作为 buildpack 支持)的特性。过程范围的环境变量与语言无关。 + +环境变量并不总是最简单的 API,因此 Spring 引导会自动提取它们,并将数据平坦化为可通过 Spring 的`Environment`抽象访问的属性,如以下示例所示: + +``` +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +@Component +public class MyBean implements EnvironmentAware { + + private String instanceId; + + @Override + public void setEnvironment(Environment environment) { + this.instanceId = environment.getProperty("vcap.application.instance_id"); + } + + // ... + +} + +``` + +所有 Cloud Foundry 属性的前缀都是`vcap`。你可以使用`vcap`属性来访问应用程序信息(例如应用程序的公共 URL)和服务信息(例如数据库凭据)。有关完整的详细信息,请参见[“CloudFoundryVCapEnvironmentPostProcessor”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.html)Javadoc。 + +| |[Java CFEnv](https://github.com/pivotal-cf/java-cfenv/)项目更适合于配置数据源等任务。| +|---|-------------------------------------------------------------------------------------------------------------------------------| + +### 1.2.库贝内特斯 + +Spring 通过检查`"*_SERVICE_HOST"`和`"*_SERVICE_PORT"`变量的环境,引导自动检测 Kubernetes 部署环境。你可以使用`spring.main.cloud-platform`配置属性重写此检测。 + +Spring boot 帮助你[管理应用程序的状态](features.html#features.spring-application.application-availability)并用[使用致动器的 HTTP Kubernetes 探测](actuator.html#actuator.endpoints.kubernetes-probes)导出它。 + +#### 1.2.1.Kubernetes 容器生命周期 + +当 Kubernetes 删除一个应用程序实例时,关闭过程涉及多个并发的子系统:关闭钩子、取消注册服务、从负载平衡器中删除实例…,因为这个关闭过程是并行进行的(而且是由于分布式系统的性质),有一个窗口,在此期间,流量可以路由到一个也已开始关机处理的 pod。 + +你可以在 Prestop 处理程序中配置睡眠执行,以避免请求被路由到已经开始关闭的 POD。这种睡眠时间应该足够长,新的请求可以停止路由到 POD,其持续时间也会因部署而异。可以使用 POD 配置文件中的 PodSpec 对 Prestop 处理程序进行配置,具体如下: + +``` +spec: + containers: + - name: "example-container" + image: "example-image" + lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 10"] +``` + +一旦预停钩子完成,SIGTERM 将被发送到容器,[优雅的关机](web.html#web.graceful-shutdown)将开始,允许任何剩余的飞行中请求完成。 + +| |当 Kubernetes 向 POD 发送 SIGTERM 信号时,它会等待一个指定的时间,称为终止宽限期(默认为 30 秒)。
如果容器在宽限期之后仍在运行,它们被发送 SIGKILL 信号并被强制删除。
如果 POD 关闭所需的时间超过 30 秒,这可能是因为你增加了`spring.lifecycle.timeout-per-shutdown-phase`,请确保通过在 POD YAML 中设置`terminationGracePeriodSeconds`选项来增加终止宽限期。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.3.赫罗库 + +Heroku 是另一个流行的 PaaS 平台。为了定制 Heroku 构建,你提供了`Procfile`,它提供了部署应用程序所需的咒语。Heroku 为 Java 应用程序分配一个`port`,然后确保对外部 URI 的路由工作。 + +你必须配置你的应用程序以侦听正确的端口。下面的示例显示了我们的 Starter REST 应用程序的`Procfile`: + +``` +web: java -Dserver.port=$PORT -jar target/demo-0.0.1-SNAPSHOT.jar +``` + +Spring 引导使`-D`参数作为可从 Spring `Environment`实例访问的属性可用。`server.port`配置属性被馈送到嵌入式 Tomcat、 Jetty 或 Undertow 实例,然后在启动时使用端口。`$PORT`环境变量由 Heroku PaaS 分配给我们。 + +这应该是你所需要的一切。Heroku 部署最常见的部署工作流是`git push`到生产的代码,如以下示例所示: + +``` +$ git push heroku main +``` + +这将导致以下情况: + +``` +Initializing repository, done. +Counting objects: 95, done. +Delta compression using up to 8 threads. +Compressing objects: 100% (78/78), done. +Writing objects: 100% (95/95), 8.66 MiB | 606.00 KiB/s, done. +Total 95 (delta 31), reused 0 (delta 0) + +-----> Java app detected +-----> Installing OpenJDK 1.8... done +-----> Installing Maven 3.3.1... done +-----> Installing settings.xml... done +-----> Executing: mvn -B -DskipTests=true clean install + + [INFO] Scanning for projects... + Downloading: https://repo.spring.io/... + Downloaded: https://repo.spring.io/... (818 B at 1.8 KB/sec) + .... + Downloaded: https://s3pository.heroku.com/jvm/... (152 KB at 595.3 KB/sec) + [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/target/... + [INFO] Installing /tmp/build_0c35a5d2-a067-4abc-a232-14b1fb7a8229/pom.xml ... + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 59.358s + [INFO] Finished at: Fri Mar 07 07:28:25 UTC 2014 + [INFO] Final Memory: 20M/493M + [INFO] ------------------------------------------------------------------------ + +-----> Discovering process types + Procfile declares types -> web + +-----> Compressing... done, 70.4MB +-----> Launching... done, v6 + https://agile-sierra-1405.herokuapp.com/ deployed to Heroku + +To [email protected]:agile-sierra-1405.git + * [new branch] main -> main +``` + +你的应用程序现在应该已经在 Heroku 上启动并运行了。有关更多详细信息,请参见[Deploying Spring Boot Applications to Heroku](https://devcenter.heroku.com/articles/deploying-spring-boot-apps-to-heroku)。 + +### 1.4.OpenShift + +[OpenShift](https://www.openshift.com/)有许多资源描述如何部署 Spring 引导应用程序,包括: + +* [使用 S2i Builder](https://blog.openshift.com/using-openshift-enterprise-grade-spring-boot-deployments/) + +* [建筑指南](https://access.redhat.com/documentation/en-us/reference_architectures/2017/html-single/spring_boot_microservices_on_red_hat_openshift_container_platform_3/) + +* [作为传统的 Web 应用程序在 Wildfly 上运行](https://blog.openshift.com/using-spring-boot-on-openshift/) + +* [OpenShift Commons 简报](https://blog.openshift.com/openshift-commons-briefing-96-cloud-native-applications-spring-rhoar/) + +### 1.5.亚马逊网络服务 + +Amazon Web Services 提供了多种方式来安装 Spring 基于启动的应用程序,既可以是传统的 Web 应用程序,也可以是带有嵌入式 Web 服务器的可执行文件 jar。这些选择包括: + +* AWS 弹性豆茎 + +* AWS 代码部署 + +* AWS Ops Works + +* AWS 云形成 + +* AWS 容器注册表 + +每一种都有不同的特点和定价模式。在本文中,我们描述了使用 AWS 弹性豆茎的方法。 + +#### 1.5.1.AWS 弹性豆茎 + +正如在官方[Elastic Beanstalk Java 指南](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create_deploy_Java.html)中所描述的,部署 Java 应用程序有两个主要选项。你可以使用“ Tomcat 平台”或“Java SE 平台”。 + +##### 使用 Tomcat 平台 + +此选项适用于生成 WAR 文件的 Spring 引导项目。不需要特殊配置。你只需要按照官方的指南去做就行了。 + +##### 使用 Java SE 平台 + +此选项适用于生成 jar 文件并运行嵌入式 Web 容器的 Spring 引导项目。Elastic Beanstalk 环境在端口 80 上运行一个 Nginx 实例,以代理在端口 5000 上运行的实际应用程序。要配置它,请在`application.properties`文件中添加以下行: + +``` +server.port=5000 +``` + +| |上传二进制文件而不是源文件

默认情况下,Elastic Beanstalk 上传源文件并在 AWS 中进行编译。
但是,最好是上传二进制文件。
要这样做,在你的`.elasticbeanstalk/config.yml`文件中添加类似于以下的行:
`gt r=”87“/>r=”:<<87"/>r=”部署 88“/>:target/demo-0.0. jar-0.1-knapshot=”。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |通过设置环境类型

来降低成本,默认情况下,弹性 Beanstalk 环境是负载平衡的,
负载平衡器有很大的成本,
为了避免这种成本,请将环境类型设置为“单实例”,如[亚马逊文档](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-create-wizard.html#environments-create-wizard-capacity).
中所述,还可以通过使用 CLI 和以下命令创建单个实例环境:

``
eb create-s
```| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.5.2.摘要 + +这是访问 AWS 的最简单的方法之一,但还有更多的事情要做,例如如何将 Elastic Beanstalk 集成到任何 CI/CD 工具中,使用 Elastic Beanstalk Maven 插件而不是 CLI,等等。有一个[blog post](https://exampledriven.wordpress.com/2017/01/09/spring-boot-aws-elastic-beanstalk-example/)更详细地介绍了这些主题。 + +### 1.6.CloudCaptain 和亚马逊网络服务 + +[CloudCaptain](https://cloudcaptain.sh/)的工作原理是将你的 Spring 引导可执行文件 jar 或 WAR 转换为一个最小的 VM 映像,该映像可以在 VirtualBox 或 AWS 上不变地部署。CloudCaptain 具有用于 Spring 引导的深度集成,并使用来自 Spring 引导配置文件的信息来自动配置端口和健康检查 URL。CloudCaptain 为它生成的映像以及它提供的所有资源(实例、安全组、弹性负载均衡器等)利用了这些信息。 + +一旦你创建了[CloudCaptain 帐户](https://console.cloudcaptain.sh),将其连接到你的 AWS 帐户,安装最新版本的 CloudCaptain 客户端,并确保该应用程序已由 Maven 或 Gradle 构建(例如,通过使用`mvn clean package`),你可以使用类似于以下的命令将 Spring 引导应用程序部署到 AWS: + +``` +$ boxfuse run myapp-1.0.jar -env=prod +``` + +有关更多选项,请参见[“BoxFuse 运行”文档](https://cloudcaptain.sh/docs/commandline/run.html)。如果当前目录中存在[`boxfuse.conf`](https://cloudcaptain.sh/docs/commandline/#configuration)文件,则考虑该文件。 + +| |默认情况下,CloudCaptain 会在启动时激活一个名为`boxfuse`的 Spring 配置文件。
如果你的可执行文件 jar 或 WAR 包含一个[应用程序-boxfuse.properties](https://cloudcaptain.sh/docs/payloads/springboot.html#configuration)文件,CloudCaptain 的配置基于其包含的属性。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此时,CloudCaptain 将为你的应用程序创建一个映像,将其上载,并在 AWS 上配置和启动必要的资源,其输出结果类似于以下示例: + +``` +Fusing Image for myapp-1.0.jar ... +Image fused in 00:06.838s (53937 K) -> axelfontaine/myapp:1.0 +Creating axelfontaine/myapp ... +Pushing axelfontaine/myapp:1.0 ... +Verifying axelfontaine/myapp:1.0 ... +Creating Elastic IP ... +Mapping myapp-axelfontaine.boxfuse.io to 52.28.233.167 ... +Waiting for AWS to create an AMI for axelfontaine/myapp:1.0 in eu-central-1 (this may take up to 50 seconds) ... +AMI created in 00:23.557s -> ami-d23f38cf +Creating security group boxfuse-sg_axelfontaine/myapp:1.0 ... +Launching t2.micro instance of axelfontaine/myapp:1.0 (ami-d23f38cf) in eu-central-1 ... +Instance launched in 00:30.306s -> i-92ef9f53 +Waiting for AWS to boot Instance i-92ef9f53 and Payload to start at https://52.28.235.61/ ... +Payload started in 00:29.266s -> https://52.28.235.61/ +Remapping Elastic IP 52.28.233.167 to i-92ef9f53 ... +Waiting 15s for AWS to complete Elastic IP Zero Downtime transition ... +Deployment completed successfully. axelfontaine/myapp:1.0 is up and running at https://myapp-axelfontaine.boxfuse.io/ +``` + +你的应用程序现在应该已经启动并在 AWS 上运行。 + +请参阅[deploying Spring Boot apps on EC2](https://cloudcaptain.sh/blog/spring-boot-ec2.html)上的博客文章以及[documentation for the CloudCaptain Spring Boot integration](https://cloudcaptain.sh/docs/payloads/springboot.html),以开始运行该应用程序的 Maven 构建。 + +### 1.7.天蓝色 + +这[入门指南](https://spring.io/guides/gs/spring-boot-for-azure/)将引导你将 Spring 引导应用程序部署到[Azure Spring Cloud](https://azure.microsoft.com/en-us/services/spring-cloud/)或[Azure 应用程序服务](https://docs.microsoft.com/en-us/azure/app-service/overview)。 + +### 1.8.谷歌云 + +Google Cloud 有几种可用于启动 Spring 启动应用程序的选项。最容易开始使用的可能是 App Engine,但你也可以找到在具有容器引擎的容器中或在具有计算引擎的虚拟机上运行 Spring 启动的方法。 + +要在 App Engine 中运行,你可以首先在 UI 中创建一个项目,该项目为你设置唯一的标识符,还可以设置 HTTP 路由。将一个 Java 应用程序添加到项目中,并将其保持为空,然后使用[Google Cloud SDK](https://cloud.google.com/sdk/install)将你的 Spring 引导应用程序从命令行或 CI 构建推入该插槽。 + +App Engine 标准要求你使用战争包装。按照[these steps](https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java8/springboot-helloworld/README.md)将 App Engine 标准应用程序部署到 Google Cloud。 + +或者,App Engine Flex 要求你创建`app.yaml`文件来描述应用程序所需的资源。通常,将此文件放入`src/main/appengine`,它应该类似于以下文件: + +``` +service: "default" + +runtime: "java" +env: "flex" + +runtime_config: + jdk: "openjdk8" + +handlers: +- url: "/.*" + script: "this field is required, but ignored" + +manual_scaling: + instances: 1 + +health_check: + enable_health_check: false + +env_variables: + ENCRYPT_KEY: "your_encryption_key_here" +``` + +你可以通过将项目 ID 添加到构建配置中来部署应用程序(例如,使用 Maven 插件),如下例所示: + +``` + + com.google.cloud.tools + appengine-maven-plugin + 1.3.0 + + myproject + + +``` + +然后使用`mvn appengine:deploy`进行部署(如果需要首先进行身份验证,则生成失败)。 + +## 2. 安装 Spring 启动应用程序 + +Spring 除了通过使用`java -jar`运行启动应用程序外,还可以为 UNIX 系统制作完全可执行的应用程序。完全可执行的 jar 可以像任何其他可执行的二进制文件一样执行,也可以是[registered with `init.d` or `systemd`](#deployment.installing.nix-services)。这有助于在公共生产环境中安装和管理 Spring 引导应用程序。 + +| |完全可执行的 JAR 通过在文件前面嵌入一个额外的脚本来工作。
目前,一些工具不接受这种格式,因此你可能不能总是使用这种技术。
例如,`jar -xf`可能无法自动提取已完全可执行的 jar 或 WAR,
建议仅在打算直接执行时才使 jar 或 WAR 完全可执行,而不是使用`java -jar`运行它,或者将其部署到 Servlet 容器中。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |不能使 ZIP64 格式的 jar 文件完全可执行。
尝试这样做将导致在直接执行或使用`java -jar`执行时报告为已损坏的 jar 文件。
包含一个或多个 ZIP64 格式嵌套 JAR 的标准格式 jar 文件可以完全可执行。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要使用 Maven 创建一个“完全可执行的” jar,请使用以下插件配置: + +``` + + org.springframework.boot + spring-boot-maven-plugin + + true + + +``` + +下面的示例展示了等效的 Gradle 配置: + +``` +bootJar { + launchScript() +} +``` + +然后,你可以通过输入`./my-application.jar`(其中`my-application`是工件的名称)来运行应用程序。包含 jar 的目录用作应用程序的工作目录。 + +### 2.1.支持的操作系统 + +默认脚本支持大多数 Linux 发行版,并在 Centos 和 Ubuntu 上进行测试。其他平台,如 OS X 和 FreeBSD,需要使用自定义`embeddedLaunchScript`。 + +### 2.2.Unix/Linux 服务 + +Spring 通过使用`init.d`或`systemd`,启动应用程序可以很容易地作为 UNIX/Linux 服务启动。 + +#### 2.2.1.作为 init.d 服务(系统 V)安装 + +如果你将 Spring boot 的 Maven 或 Gradle 插件配置为生成[fully executable jar](#deployment.installing),并且不使用自定义的`embeddedLaunchScript`,那么你的应用程序可以用作`init.d`服务。为此,将 jar 符号链接到`init.d`以支持标准`start`、`stop`、`restart`和`status`命令。 + +该脚本支持以下功能: + +* 作为 OWNS jar 文件的用户启动服务 + +* 使用`/var/run//.pid`跟踪应用程序的 PID + +* 将控制台日志写入`/var/log/.log` + +假设在`/var/myapp`中安装了 Spring 引导应用程序,要将 Spring 引导应用程序安装为`init.d`服务,请创建一个符号链接,如下所示: + +``` +$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp +``` + +一旦安装,你就可以用通常的方式启动和停止服务。例如,在基于 Debian 的系统上,你可以用以下命令启动它: + +``` +$ service myapp start +``` + +| |如果应用程序无法启动,请检查写入`/var/log/.log`的日志文件中的错误。| +|---|------------------------------------------------------------------------------------------------------| + +你还可以使用标准的操作系统工具标记应用程序自动启动。例如,在 Debian 上,你可以使用以下命令: + +``` +$ update-rc.d myapp defaults +``` + +##### 获得 init.d 服务 + +| |以下是关于如何保护作为 init.d 服务运行的 Spring 引导应用程序的一组指导原则。
它并不是要详尽列出所有应该做的事情,以加强应用程序及其运行的环境。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +当作为 root 执行时,就像使用 root 启动 init.d 服务时一样,默认的可执行脚本以`RUN_AS_USER`环境变量中指定的用户的身份运行应用程序。当环境变量未被设置时,OWNS 该 jar 文件的用户被用来代替。永远不应该将 Spring 引导应用程序运行为`root`,因此`RUN_AS_USER`永远不应该是 root,并且应用程序的 jar 文件永远不应该由 root 拥有。相反,创建一个特定的用户来运行你的应用程序,并设置`RUN_AS_USER`环境变量,或者使用`chown`使其成为 jar 文件的所有者,如以下示例所示: + +``` +$ chown bootapp:bootapp your-app.jar +``` + +在这种情况下,默认的可执行脚本以`bootapp`用户的身份运行应用程序。 + +| |为了减少应用程序的用户帐户遭到破坏的可能性,你应该考虑阻止它使用登录 shell。
例如,你可以将帐户的 shell 设置为`/usr/sbin/nologin`。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你还应该采取措施,防止修改应用程序的 jar 文件。首先,配置其权限,使其不能被写入,只能由其所有者读取或执行,如以下示例所示: + +``` +$ chmod 500 your-app.jar +``` + +其次,如果你的应用程序或正在运行它的帐户遭到破坏,你还应该采取措施限制损害。如果攻击者确实获得了访问权限,他们可以使 jar 文件可写并更改其内容。防止这种情况发生的一种方法是使用`chattr`使其不可更改,如下例所示: + +``` +$ sudo chattr +i your-app.jar +``` + +这将阻止任何用户(包括根用户)修改 jar。 + +如果使用 root 来控制应用程序的服务,并且你[use a `.conf` file](#deployment.installing.nix-services.script-customization.when-running.conf-file)自定义其启动,则根用户将读取`.conf`文件并对其进行评估。它应该得到相应的保护。使用`chmod`使文件只能由所有者读取,并使用`chown`使根用户成为所有者,如以下示例所示: + +``` +$ chmod 400 your-app.conf +$ sudo chown root:root your-app.conf +``` + +#### 2.2.2.安装为 Systemd 服务 + +`systemd`是 System v init 系统的继承者,现在被许多现代 Linux 发行版使用。虽然可以继续使用`init.d`带有`systemd`的脚本,但也可以通过使用`systemd`’service’脚本启动 Spring 启动应用程序。 + +假设在`/var/myapp`中安装了 Spring 引导应用程序,要将 Spring 引导应用程序安装为`systemd`服务,请创建一个名为`myapp.service`的脚本,并将其放置在`/etc/systemd/system`目录中。下面的脚本提供了一个示例: + +``` +[Unit] +Description=myapp +After=syslog.target + +[Service] +User=myapp +ExecStart=/var/myapp/myapp.jar +SuccessExitStatus=143 + +[Install] +WantedBy=multi-user.target +``` + +| |请记住更改应用程序的`Description`、`User`和`ExecStart`字段。| +|---|------------------------------------------------------------------------------------------| + +| |`ExecStart`字段不声明脚本操作命令,这意味着默认情况下使用`run`命令。| +|---|------------------------------------------------------------------------------------------------------------------------| + +请注意,与作为`init.d`服务运行不同,运行应用程序的用户、PID 文件和控制台日志文件由`systemd`本身管理,因此必须通过在“服务”脚本中使用适当的字段进行配置。有关更多详细信息,请咨询[服务单元配置手册页](https://www.freedesktop.org/software/systemd/man/systemd.service.html)。 + +要标记应用程序在系统启动时自动启动,请使用以下命令: + +``` +$ systemctl enable myapp.service +``` + +运行`man systemctl`获取更多详细信息。 + +#### 2.2.3.自定义启动脚本 + +由 Maven 或 Gradle 插件编写的默认嵌入式启动脚本可以通过多种方式进行定制。对于大多数人来说,使用默认脚本并进行一些自定义通常就足够了。如果你发现无法自定义你需要的内容,请使用`embeddedLaunchScript`选项完全编写自己的文件。 + +##### 在编写开始脚本时自定义 ##### + +当开始脚本被写入 jar 文件时,定制它的元素通常是有意义的。例如,init.d 脚本可以提供一个“描述”。因为你预先知道描述(并且不需要更改),所以在生成 jar 时也可以提供它。 + +要定制写入的元素,请使用 Spring boot Maven 插件的`embeddedLaunchScriptProperties`选项或[`properties` property of the Spring Boot Gradle plugin’s `launchScript`](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#packaging-executable-configuring-launch-script)。 + +默认脚本支持以下属性替换: + +| Name |说明| Gradle default | Maven default | +|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|------------------------------------------------------------| +| `mode` |脚本模式。| `auto` | `auto` | +| `initInfoProvides` |“init info”的`Provides`部分| `${task.baseName}` | `${project.artifactId}` | +| `initInfoRequiredStart` |`Required-Start`“init info”部分。| `$remote_fs $syslog $network` | `$remote_fs $syslog $network` | +| `initInfoRequiredStop` |`Required-Stop`“init info”部分。| `$remote_fs $syslog $network` | `$remote_fs $syslog $network` | +| `initInfoDefaultStart` |`Default-Start`“init info”部分。| `2 3 4 5` | `2 3 4 5` | +| `initInfoDefaultStop` |`Default-Stop`“init info”部分。| `0 1 6` | `0 1 6` | +|`initInfoShortDescription`|`Short-Description`“init info”部分。|Single-line version of `${project.description}` (falling back to `${task.baseName}`)| `${project.name}` | +| `initInfoDescription` |`Description`“init info”部分。| `${project.description}` (falling back to `${task.baseName}`) |`${project.description}` (falling back to `${project.name}`)| +| `initInfoChkconfig` |`chkconfig`“init info”部分| `2345 99 01` | `2345 99 01` | +| `confFolder` |`CONF_FOLDER`的默认值| Folder containing the jar | Folder containing the jar | +| `inlinedConfScript` |引用在默认启动脚本中应该内联的文件脚本。
在加载任何外部配置文件之前,这可以用来设置环境变量,例如`JAVA_OPTS`| | | +| `logFolder` |`LOG_FOLDER`的默认值。
仅对`init.d`服务有效| | | +| `logFilename` |`LOG_FILENAME`的默认值。
仅对`init.d`服务有效| | | +| `pidFolder` |`PID_FOLDER`的默认值。
仅对`init.d`服务有效| | | +| `pidFilename` |`PID_FOLDER`中 PID 文件名称的默认值。
仅对`init.d`服务有效| | | +| `useStartStopDaemon` |当`start-stop-daemon`命令可用时,是否应该使用它来控制进程| `true` | `true` | +| `stopWaitTime` |`STOP_WAIT_TIME`的默认值(以秒为单位)。
仅对`init.d`服务有效| 60 | 60 | + +##### 在脚本运行时自定义脚本 ##### + +对于需要自定义*之后* jar 的脚本项,可以使用环境变量或[config file](#deployment.installing.nix-services.script-customization.when-running.conf-file)。 + +默认脚本支持以下环境属性: + +| Variable |说明| +|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `MODE` |操作的“模式”。
默认值取决于构建 jar 的方式,但通常是`auto`(这意味着它试图通过检查来猜测它是否是一个 init 脚本)如果它是一个名为`init.d`的目录中的符号链接)。
你可以显式地将它设置为`service`,这样 `stop|start|status|restart` commands work or to `run` if you want to run the script in the foreground.| +| `RUN_AS_USER` |将用于运行该应用程序的用户。
当未设置时,将使用 OWNS jar 文件的用户。| +|`USE_START_STOP_DAEMON`|是否应该使用`start-stop-daemon`命令来控制进程。
默认为`true`。| +| `PID_FOLDER` |PID 文件夹的根名(默认为 `/var/run’)。| +| `LOG_FOLDER` |放置日志文件的文件夹的名称(默认情况下为“/var/log”)。| +| `CONF_FOLDER` |读取.conf 文件的文件夹的名称(默认情况下与 jar-file 文件相同)。| +| `LOG_FILENAME` |在`LOG_FOLDER`(.log` 默认情况下)中日志文件的名称。| +| `APP_NAME` |应用程序的名称。
如果 jar 是从符号链接运行的,则脚本猜测应用程序的名称。
如果不是符号链接,或者你希望显式设置应用程序名称,这可能是有用的。| +| `RUN_ARGS` |要传递给程序( Spring 引导应用程序)的参数。| +| `JAVA_HOME` |默认情况下,`java`可执行文件的位置是通过使用`PATH`发现的,但是如果在`$JAVA_HOME/bin/java`处有一个可执行文件,则可以显式地设置它。| +| `JAVA_OPTS` |在 JVM 启动时传递给它的选项。| +| `JARFILE` |jar 文件的明确位置,以防该脚本被用来启动 jar 它实际上并未嵌入。| +| `DEBUG` |如果不是空的,则在 shell 进程上设置`-x`标志,允许你查看脚本中的逻辑。| +| `STOP_WAIT_TIME` |在强制关闭应用程序之前,停止应用程序所需的等待时间(以秒为单位)(默认为 `60’)。| + +| |`PID_FOLDER`,`LOG_FOLDER`,和`LOG_FILENAME`变量仅对`init.d`服务有效。
对于`systemd`,通过使用’service’脚本进行等效的自定义。
有关更多详细信息,请参见[服务单元配置手册页](https://www.freedesktop.org/software/systemd/man/systemd.service.html)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +除了`JARFILE`和`APP_NAME`之外,可以通过使用`.conf`文件配置上一节中列出的设置。该文件预计将位于 jar 文件的旁边,并且具有相同的名称,但后缀为`.conf`,而不是`.jar`。例如,名为`/var/myapp/myapp.jar`的 jar 使用名为`/var/myapp/myapp.conf`的配置文件,如以下示例所示: + +myapp.conf + +``` +JAVA_OPTS=-Xmx1024M +LOG_FOLDER=/custom/log/folder +``` + +| |如果不喜欢将配置文件放在 jar 文件旁边,则可以设置`CONF_FOLDER`环境变量来定制配置文件的位置。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要了解如何适当地保护此文件,请参见[获得 init.d 服务的指导方针](#deployment.installing.nix-services.init-d.securing)。 + +### 2.3.Microsoft Windows 服务 + +Spring 启动应用程序可以通过使用[`winsw`](https://github.com/kohsuke/winsw)作为 Windows 服务启动。 + +a([单独维护的样本](https://github.com/snicoll/spring-boot-daemon))逐步描述了如何为 Spring 引导应用程序创建 Windows 服务。 + +## 3. 接下来读什么? + +有关 PaaS 可以提供的特性的更多信息,请参见[Cloud Foundry](https://www.cloudfoundry.org/)、[Heroku](https://www.heroku.com/)、[OpenShift](https://www.openshift.com)和[Boxfuse](https://boxfuse.com)网站。这些只是最受欢迎的 Java PaaS 提供商中的四个。 Spring 由于引导非常适合基于云的部署,因此你也可以自由地考虑其他提供商。 + +下一节继续介绍*[Spring Boot CLI](cli.html#cli)*,或者你可以提前阅读有关*[构建工具插件](build-tool-plugins.html#build-tool-plugins)*的内容。 + diff --git a/docs/spring-boot/documentation.md b/docs/spring-boot/documentation.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..54215da158ad1f470bed463979412e0d9886c1ef 100644 --- a/docs/spring-boot/documentation.md +++ b/docs/spring-boot/documentation.md @@ -0,0 +1,130 @@ +# 文件概述 + +本节简要概述了 Spring 引导参考文档。它是这份文件其余部分的一张地图。 + +该文档的最新副本可在[docs.spring.io/spring-boot/docs/current/reference/](https://docs.spring.io/spring-boot/docs/current/reference/)上获得。 + +## 1. 第一步 + +如果你开始使用 Spring boot 或一般的“ Spring”,请从[以下主题](getting-started.html#getting-started)开始: + +* **从零开始:** [Overview](getting-started.html#getting-started.introducing-spring-boot) | [Requirements](getting-started.html#getting-started.system-requirements) | [Installation](getting-started.html#getting-started.installing) + +* **教程:** [Part 1](getting-started.html#getting-started.first-application) | [Part 2](getting-started.html#getting-started.first-application.code) + +* **运行你的示例:** [Part 1](getting-started.html#getting-started.first-application.run) | [Part 2](getting-started.html#getting-started.first-application.executable-jar) + +## 2. 从早期版本升级 + +你应该始终确保运行的是 Spring boot 的[支持的版本](https://github.com/spring-projects/spring-boot/wiki/Supported-Versions)。 + +根据你要升级到的版本,你可以在这里找到一些额外的技巧: + +* **从 1.x 开始:** [从 1.x 升级](actuator.html#upgrading.from-1x) + +* **到一个新的功能版本:** [升级到新功能版本](upgrading.html#upgrading.to-feature) + +* **Spring Boot CLI:** [Upgrading the Spring Boot CLI](upgrading.html#upgrading.cli) + +## 3. 用 Spring boot 开发 + +准备好真正开始使用 Spring 引导了吗?[我们有你的保险。](using.html#using): + +* **构建系统:** [Maven](using.html#using.build-systems.maven) | [Gradle](using.html#using.build-systems.gradle) | [Ant](using.html#using.build-systems.ant) | [Starters](using.html#using.build-systems.starters) + +* **最佳实践:** [Code Structure](using.html#using.structuring-your-code) | [@Configuration](using.html#using.configuration-classes) | [@enableAutoConfiguration](using.html#using.auto-configuration) | [bean 和依赖注入](using.html#using.spring-beans-and-dependency-injection) + +* **运行你的代码:** [IDE](using.html#using.running-your-application.from-an-ide) | [Packaged](using.html#using.running-your-application.as-a-packaged-application) | [Maven](using.html#using.running-your-application.with-the-maven-plugin) | [Gradle](using.html#using.running-your-application.with-the-gradle-plugin) + +* **打包应用程序:** [Production jars](using.html#using.packaging-for-production) + +* **Spring Boot CLI:** [Using the CLI](cli.html#cli) + +## 4. 学习 Spring 引导功能 + +需要更多有关 Spring Boot 核心功能的详细信息吗?[以下内容是为你准备的](features.html#features): + +* **Spring Application:** [SpringApplication](features.html#features.spring-application) + +* **外部配置:** [外部配置](features.html#features.external-config) + +* **配置文件:** [Profiles](features.html#features.profiles) + +* **日志记录:** [Logging](features.html#features.logging) + +## 5. 万维网 + +如果开发 Spring 引导 Web 应用程序,请查看以下内容: + +* **Servlet Web Applications:** [Spring MVC, Jersey, Embedded Servlet Containers](web.html#web.servlet) + +* **反应式 Web 应用程序:** [Spring Webflux, Embedded Servlet Containers](web.html#web.reactive) + +* **优雅的关机:** [优雅的关机](web.html#web.graceful-shutdown) + +* **Spring Security:** [默认安全配置,自动配置 OAuth2,SAML](web.html#web.security) + +* **Spring Session:** [Auto-configuration for Spring Session](web.html#web.spring-session) + +* **Spring HATEOAS:** [Auto-configuration for Spring HATEOAS](web.html#web.spring-hateoas) + +## 6. 数据 + +如果你的应用程序处理一个数据存储,你可以在这里看到如何配置它: + +* **SQL:** [配置 SQL 数据存储、嵌入式数据库支持、连接池等。](data.html#data.sql) + +* **NoSQL:** [自动配置 NoSQL 存储,如 Redis、MongoDB、NEO4J 等。](data.html#data.nosql) + +## 7. 消息传递 + +如果你的应用程序使用任何消息传递协议,请参阅以下一个或多个部分: + +* **JMS:** [自动配置 ActiveMQ 和 Artemis,通过 JMS 发送和接收消息](messaging.html#messaging.jms) + +* **AMQP:** [RabbitMQ 的自动配置](messaging.html#messaging.amqp) + +* **卡夫卡:** [Auto-configuration for Spring Kafka](messaging.html#messaging.kafka) + +* **RSocket:** [Auto-configuration for Spring Framework’s RSocket Support](messaging.html#messaging.rsocket) + +* **Spring Integration:** [Auto-configuration for Spring Integration](messaging.html#messaging.spring-integration) + +## 8. IO + +如果你的应用程序需要 IO 功能,请参阅以下一个或多个部分: + +* **缓存:** [缓存支持 Ehcache、Hazelcast、Infinispan 等](io.html#io.caching) + +* **石英:** [石英调度](io.html#io.quartz) + +* **邮件:** [Sending Email](io.html#io.email) + +* **验证:** [JSR-303 验证](io.html#io.validation) + +* **休息客户:** [使用 RESTTemplate 和 WebClient 调用 REST 服务](io.html#io.rest-client) + +* **网络服务:** [Auto-configuration for Spring Web Services](io.html#io.webservices) + +* **JTA:** [使用 JTA 的分布式事务](io.html#io.jta) + +## 9. 容器图像 + +Spring Boot 为构建高效的容器映像提供了一流的支持。你可以在这里了解更多有关它的信息: + +* **有效的容器映像:** [优化容器图像(如 Docker 图像)的技巧](container-images.html#container-images.efficient-images) + +* **DockerFiles:** [使用 DockerFiles 构建容器映像](container-images.html#container-images.dockerfiles) + +* **云原生构建包:** [Support for Cloud Native Buildpacks with Maven and Gradle](container-images.html#container-images.buildpacks) + +## 10. 高级主题 + +最后,我们为更高级的用户提供了几个主题: + +* **Spring Boot Applications Deployment:** [云部署](deployment.html#deployment.cloud) | [OS Service](deployment.html#deployment.installing.nix-services) + +* **构建工具插件:** [Maven](build-tool-plugins.html#build-tool-plugins.maven) | [Gradle](build-tool-plugins.html#build-tool-plugins.gradle) + +* **附录:** [应用程序属性](application-properties.html#appendix.application-properties) | [配置元数据](configuration-metadata.html#appendix.configuration-metadata) | [自动配置类](auto-configuration-classes.html#appendix.auto-configuration-classes) | [测试自动配置注释](test-auto-configuration.html#appendix.test-auto-configuration) | [Executable Jars](executable-jar.html#appendix.executable-jar) | [依赖版本](dependency-versions.html#appendix.dependency-versions) + diff --git a/docs/spring-boot/features.md b/docs/spring-boot/features.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cfccc70a6f8493fbe299c978d5440536a221e634 100644 --- a/docs/spring-boot/features.md +++ b/docs/spring-boot/features.md @@ -0,0 +1,4667 @@ +# 核心功能 + +这一节深入讨论了 Spring 引导的细节。在这里,你可以了解你可能想要使用和自定义的关键功能。如果你还没有这样做,你可能希望阅读“[getting-started.html](getting-started.html#getting-started)”和“[using.html](using.html#using)”部分,以便你有一个良好的基础知识。 + +## 1. SpringApplication + + +`SpringApplication`类提供了一种方便的方式来引导从`main()`方法启动的 Spring 应用程序。在许多情况下,可以委托给静态`SpringApplication.run`方法,如以下示例所示: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +当应用程序启动时,你应该会看到类似于以下输出的内容: + +``` + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: v2.6.4 + +2021-02-03 10:33:25.224 INFO 17321 --- [ main] o.s.b.d.s.s.SpringApplicationExample : Starting SpringApplicationExample using Java 1.8.0_232 on mycomputer with PID 17321 (/apps/myjar.jar started by pwebb) +2021-02-03 10:33:25.226 INFO 17900 --- [ main] o.s.b.d.s.s.SpringApplicationExample : No active profile set, falling back to default profiles: default +2021-02-03 10:33:26.046 INFO 17321 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) +2021-02-03 10:33:26.054 INFO 17900 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] +2021-02-03 10:33:26.055 INFO 17900 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41] +2021-02-03 10:33:26.097 INFO 17900 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2021-02-03 10:33:26.097 INFO 17900 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 821 ms +2021-02-03 10:33:26.144 INFO 17900 --- [ main] s.tomcat.SampleTomcatApplication : ServletContext initialized +2021-02-03 10:33:26.376 INFO 17900 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' +2021-02-03 10:33:26.384 INFO 17900 --- [ main] o.s.b.d.s.s.SpringApplicationExample : Started SampleTomcatApplication in 1.514 seconds (JVM running for 1.823) +``` + +默认情况下,将显示`INFO`日志消息,包括一些相关的启动细节,例如启动应用程序的用户。如果需要`INFO`以外的日志级别,则可以设置它,如[Log Levels](#features.logging.log-levels)中所述。应用程序版本是使用主应用程序类的包中的实现版本来确定的。可以通过将`spring.main.log-startup-info`设置为`false`来关闭启动信息日志。这也将关闭应用程序活动配置文件的日志记录。 + +| |要在启动期间添加额外的日志记录,可以在`SpringApplication`的子类中覆盖`logStartupInfo(boolean)`。| +|---|--------------------------------------------------------------------------------------------------------------------------| + +### 1.1.启动失败 + +如果你的应用程序无法启动,注册`FailureAnalyzers`将有机会提供一个专用的错误消息和具体的操作来解决问题。例如,如果你在端口`8080`上启动了一个 Web 应用程序,并且该端口已经在使用中,那么你应该会看到类似于以下消息的内容: + +``` +*************************** +APPLICATION FAILED TO START +*************************** + +Description: + +Embedded servlet container failed to start. Port 8080 was already in use. + +Action: + +Identify and stop the process that is listening on port 8080 or configure this application to listen on another port. +``` + +| |Spring Boot 提供了许多`FailureAnalyzer`实现,并且可以[add your own](howto.html#howto.application.failure-analyzer)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------| + +如果没有故障分析器能够处理异常,则仍然可以显示完整的条件报告,以更好地了解出了什么问题。要这样做,你需要[enable the `debug` property](#features.external-config)或[enable `DEBUG` logging](#features.logging.log-levels)for`org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener`。 + +例如,如果你使用`java -jar`运行你的应用程序,你可以启用`debug`属性,如下所示: + +``` +$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug +``` + +### 1.2.惰性初始化 + +`SpringApplication`允许应用程序进行惰性初始化。当启用惰性初始化时,将根据需要而不是在应用程序启动期间创建 bean。因此,启用延迟初始化可以减少启动应用程序所需的时间。在 Web 应用程序中,启用延迟初始化将导致许多与 Web 相关的 bean 在收到 HTTP 请求之前不会被初始化。 + +延迟初始化的一个缺点是,它可能会延迟应用程序问题的发现。如果延迟初始化配置错误的 Bean,则在启动过程中将不再发生故障,并且只有在初始化 Bean 时,问题才会变得明显。还必须注意确保 JVM 有足够的内存来容纳所有应用程序的 bean,而不仅仅是那些在启动期间初始化的 bean。由于这些原因,默认情况下不启用惰性初始化,建议在启用惰性初始化之前对 JVM 的堆大小进行微调。 + +可以使用`SpringApplicationBuilder`上的`lazyInitialization`方法或`setLazyInitialization`上的`setLazyInitialization`方法以编程方式启用惰性初始化。或者,可以使用`spring.main.lazy-initialization`属性启用它,如以下示例所示: + +属性 + +``` +spring.main.lazy-initialization=true +``` + +Yaml + +``` +spring: + main: + lazy-initialization: true +``` + +| |如果你希望在对应用程序的其余部分使用惰性初始化的同时禁用某些 bean 的惰性初始化,那么可以使用`@Lazy(false)`注释显式地将它们的惰性属性设置为 false。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.3.自定义横幅 + +可以通过在 Classpath 中添加`banner.txt`文件或将`spring.banner.location`属性设置为此类文件的位置来更改在启动时打印的横幅。如果文件的编码不是 UTF-8,则可以设置`spring.banner.charset`。除了文本文件之外,还可以在 Classpath 中添加`banner.gif`、`banner.jpg`或`banner.png`图像文件,或者设置`spring.banner.image.location`属性。图像被转换为 ASCII 艺术表现,并打印在任何文本横幅之上。 + +在`banner.txt`文件中,你可以使用`Environment`中可用的任何键以及以下任何占位符: + +| Variable |说明| +|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `${application.version}` |应用程序的版本号,如`MANIFEST.MF`中声明的。
例如,`Implementation-Version: 1.0`打印为`1.0`。| +| `${application.formatted-version}` |应用程序的版本号,如`MANIFEST.MF`中声明的那样,并进行了格式化以供显示(周围用括号包围,前缀为`v`)。
例如`(v1.0)`。| +| `${spring-boot.version}` |你正在使用的 Spring 引导版本。
例如`2.6.4`。| +| `${spring-boot.formatted-version}` |你正在使用的 Spring 引导版本,格式为显示(周围用括号和前缀`v`)。
例如`(v2.6.4)`。| +|`${Ansi.NAME}` (or `${AnsiColor.NAME}`, `${AnsiBackground.NAME}`, `${AnsiStyle.NAME}`)|其中`NAME`是 ANSI 转义代码的名称。
有关详细信息,请参见[“AnsipropertySource”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/AnsiPropertySource.java)。| +| `${application.title}` |在`MANIFEST.MF`中声明的应用程序的标题。
例如`Implementation-Title: MyApp`打印为`MyApp`。| + +| |如果你想以编程方式生成横幅,可以使用`SpringApplication.setBanner(…​)`方法。
使用`org.springframework.boot.Banner`接口并实现你自己的`printBanner()`方法。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你还可以使用`spring.main.banner-mode`属性来确定横幅是否必须打印在`System.out`(`console’)上,发送到配置的日志程序(`log’),或者根本不生成(`off’)。 + +打印的横幅被注册为单件 Bean,其名称如下:`springBootBanner`。 + +| |只有当你使用 Spring 引导启动器时,`${application.version}`和`${application.formatted-version}`属性才可用。
这些值将不会被解析如果你正在运行一个 Unpacked jar 并以`java -cp `启动它。

这就是为什么我们建议你总是使用`java org.springframework.boot.loader.JarLauncher`启动 Unpacked JAR。
这将在构建 Classpath 并启动应用程序之前初始化`application.*`横幅变量。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.4.自定义 SpringApplication + +如果`SpringApplication`默认值不符合你的喜好,那么你可以创建一个本地实例并对其进行自定义。例如,要关闭横幅,你可以写: + +``` +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(MyApplication.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } + +} + +``` + +| |传递给`SpringApplication`的构造函数参数是 Spring bean 的配置源。
在大多数情况下,这些参数是对`@Configuration`类的引用,但它们也可以是直接引用`@Component`类。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +也可以通过使用`application.properties`文件配置`SpringApplication`。详见*[外部化配置](#features.external-config)*。 + +有关配置选项的完整列表,请参见[“SpringApplication”Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/SpringApplication.html)。 + +### 1.5.Fluent Builder API + +如果你需要构建`ApplicationContext`层次结构(具有父/子关系的多个上下文),或者如果你更喜欢使用“Fluent”Builder API,则可以使用`SpringApplicationBuilder`。 + +`SpringApplicationBuilder`允许你将多个方法调用链接在一起,并且包括`parent`和`child`方法,这些方法允许你创建一个层次结构,如以下示例所示: + +``` +new SpringApplicationBuilder() + .sources(Parent.class) + .child(Application.class) + .bannerMode(Banner.Mode.OFF) + .run(args); + +``` + +| |创建`ApplicationContext`层次结构时有一些限制。
例如,Web 组件**必须**包含在子上下文中,并且相同的`Environment`用于父上下文和子上下文。
有关详细信息,请参见[SpringApplicationBuilder Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/builder/SpringApplicationBuilder.html)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.6.应用程序可用性 + +当部署在平台上时,应用程序可以使用[Kubernetes 探测器](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)之类的基础设施向平台提供有关其可用性的信息。 Spring 启动包括对常用的“活性”和“就绪”可用性状态的开箱即用支持。如果你使用 Spring Boot 的“actuator”支持,那么这些状态将作为健康端点组公开。 + +此外,你还可以通过将`ApplicationAvailability`接口注入你自己的 bean 来获得可用性状态。 + +#### 1.6.1.活性状态 + +应用程序的“活性”状态表示其内部状态是否允许其正确工作,或者在当前出现故障时是否允许其自行恢复。中断的“活性”状态意味着应用程序处于无法恢复的状态,基础结构应该重新启动应用程序。 + +| |通常,“活性”状态不应该基于外部检查,例如[Health checks](actuator.html#actuator.endpoints.health)。
如果是这样,失败的外部系统(数据库、Web API、外部缓存)将触发大规模重启和跨平台的级联故障。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 引导应用程序的内部状态主要由 Spring `ApplicationContext`表示。 Spring 如果应用程序上下文已成功启动,则启动假定应用程序处于有效状态。一旦上下文被刷新,应用程序就被认为是实时的,请参见[Spring Boot application lifecycle and related Application Events](#features.spring-application.application-events-and-listeners)。 + +#### 1.6.2.准备状态 + +应用程序的“就绪”状态表明该应用程序是否已准备好处理流量。一个失败的“就绪”状态告诉平台,它现在不应该将流量路由到应用程序。这通常发生在启动过程中,而`CommandLineRunner`和`ApplicationRunner`组件正在被处理,或者在任何时候,如果应用程序决定它太忙而不能进行额外的流量。 + +一旦调用了应用程序和命令行运行器,就认为应用程序已准备就绪,请参见[Spring Boot application lifecycle and related Application Events](#features.spring-application.application-events-and-listeners)。 + +| |期望在启动期间运行的任务应该由`CommandLineRunner`和`ApplicationRunner`组件执行,而不是使用 Spring 组件生命周期回调,例如`@PostConstruct`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.6.3.管理应用程序可用性状态 + +通过注入`ApplicationAvailability`接口并在其上调用方法,应用程序组件可以随时检索当前的可用性状态。更常见的情况是,应用程序希望侦听状态更新或更新应用程序的状态。 + +例如,我们可以将应用程序的“就绪”状态导出到一个文件中,以便 Kubernetes 的“Exec Probe”可以查看该文件: + +``` +import org.springframework.boot.availability.AvailabilityChangeEvent; +import org.springframework.boot.availability.ReadinessState; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +@Component +public class MyReadinessStateExporter { + + @EventListener + public void onStateChange(AvailabilityChangeEvent event) { + switch (event.getState()) { + case ACCEPTING_TRAFFIC: + // create file /tmp/healthy + break; + case REFUSING_TRAFFIC: + // remove file /tmp/healthy + break; + } + } + +} + +``` + +当应用程序中断且无法恢复时,我们还可以更新应用程序的状态: + +``` +import org.springframework.boot.availability.AvailabilityChangeEvent; +import org.springframework.boot.availability.LivenessState; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +@Component +public class MyLocalCacheVerifier { + + private final ApplicationEventPublisher eventPublisher; + + public MyLocalCacheVerifier(ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + public void checkLocalCache() { + try { + // ... + } + catch (CacheCompletelyBrokenException ex) { + AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN); + } + } + +} + +``` + +Spring Boot 提供[Kubernetes HTTP 探测与执行器健康端点的“活性”和“准备状态”](actuator.html#actuator.endpoints.kubernetes-probes)。你可以获得更多关于[deploying Spring Boot applications on Kubernetes in the dedicated section](deployment.html#deployment.cloud.kubernetes)的指导。 + +### 1.7.应用程序事件和监听器 + +Spring 除了通常的框架事件,例如[“Contextrefreshedevent”](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/context/event/ContextRefreshedEvent.html),一个`SpringApplication`发送一些额外的应用程序事件。 + +| |有些事件实际上是在`ApplicationContext`被创建之前被触发的,所以你不能在这些事件上注册一个侦听器作为`@Bean`。
你可以用`SpringApplication.addListeners(…​)`方法或`SpringApplicationBuilder.listeners(…​)`方法来注册它们,
如果你想要自动注册这些侦听器,无论创建应用程序的方式如何,你都可以通过使用`org.springframework.context.ApplicationListener`键将`META-INF/spring.factories`文件添加到你的项目中,并引用你的侦听器,如以下示例所示:

``
org.applingframework.context.context.aptationListener=com.example.project.myListener
``````| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +在应用程序运行时,应用程序事件按以下顺序发送: + +1. 一个`ApplicationStartingEvent`在运行开始时但在任何处理之前发送,除了注册侦听器和初始化器。 + +2. 当要在上下文中使用的`Environment`是已知的但在创建上下文之前发送`ApplicationEnvironmentPreparedEvent`。 + +3. 当`ApplicationContext`准备好并且在加载任何 Bean 定义之前调用了应用上下文初始化器时,将发送`ApplicationContextInitializedEvent`。 + +4. 一个`ApplicationPreparedEvent`在刷新开始之前但是在 Bean 定义加载之后被发送。 + +5. 在刷新上下文之后,但在调用任何应用程序和命令行运行器之前,将发送`ApplicationStartedEvent`。 + +6. 在使用`LivenessState.CORRECT`之后立即发送`AvailabilityChangeEvent`,以表明该应用程序被认为是动态的。 + +7. 在调用任何[应用程序和命令行运行器](#features.spring-application.command-line-runner)后发送`ApplicationReadyEvent`。 + +8. 在使用`ReadinessState.ACCEPTING_TRAFFIC`之后立即发送`AvailabilityChangeEvent`,以表明应用程序已准备好为请求提供服务。 + +9. 如果启动时出现异常,则发送`ApplicationFailedEvent`。 + +上面的列表只包括与`SpringApplicationEvent`绑定的`SpringApplication`。除了这些,以下事件还发布在`ApplicationPreparedEvent`之后和`ApplicationStartedEvent`之前: + +* 在`WebServer`准备好之后发送`WebServerInitializedEvent`。“ServletWebServerInitializeDevent”和`ReactiveWebServerInitializedEvent`分别是 Servlet 和反应变量。 + +* 当刷新`ApplicationContext`时,将发送`ContextRefreshedEvent`。 + +| |你通常不需要使用应用程序事件,但是知道它们的存在是很方便的。
在内部, Spring boot 使用事件来处理各种任务。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |默认情况下,事件侦听器在同一个线程中执行时,不应该运行可能很长的任务。
考虑使用[应用程序和命令行运行器](#features.spring-application.command-line-runner)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +应用程序事件通过使用 Spring Framework 的事件发布机制发送。该机制的一部分确保在子上下文中发布给侦听器的事件也在任何祖先上下文中发布给侦听器。因此,如果应用程序使用`SpringApplication`实例的层次结构,则侦听器可能会接收同一类型的应用程序事件的多个实例。 + +为了使侦听器能够区分其上下文的事件和子代上下文的事件,它应该请求注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现`ApplicationContextAware`来注入,或者,如果侦听器是 Bean,则可以通过使用`@Autowired`来注入。 + +### 1.8.网络环境 + +a`SpringApplication`试图代表你创建`ApplicationContext`的正确类型。用于确定`WebApplicationType`的算法如下: + +* 如果存在 Spring MVC,则使用`AnnotationConfigServletWebServerApplicationContext` + +* 如果 Spring MVC 不存在而 Spring WebFlux 存在,则使用`AnnotationConfigReactiveWebServerApplicationContext` + +* 否则,使用`AnnotationConfigApplicationContext` + +这意味着,如果你在同一应用程序中使用 Spring MVC 和 Spring WebFlux 中的新`WebClient`,则默认情况下将使用 Spring MVC。你可以通过调用`setWebApplicationType(WebApplicationType)`轻松地重写它。 + +也可以通过调用`setApplicationContextClass(…​)`来完全控制`ApplicationContext`类型。 + +| |在 JUnit 测试中使用`SpringApplication`时,通常需要调用`setWebApplicationType(WebApplicationType.NONE)`。| +|---|----------------------------------------------------------------------------------------------------------------------------------| + +### 1.9.访问应用程序参数 + +如果需要访问传递给`SpringApplication.run(…​)`的应用程序参数,则可以插入`org.springframework.boot.ApplicationArguments` Bean。`ApplicationArguments`接口提供对 RAW`String[]`参数以及解析的`option`和`non-option`参数的访问,如以下示例所示: + +``` +import java.util.List; + +import org.springframework.boot.ApplicationArguments; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + public MyBean(ApplicationArguments args) { + boolean debug = args.containsOption("debug"); + List files = args.getNonOptionArgs(); + if (debug) { + System.out.println(files); + } + // if run with "--debug logfile.txt" prints ["logfile.txt"] + } + +} + +``` + +| |Spring Boot 还使用 Spring `Environment`注册了`CommandLinePropertySource`。
这也允许你通过使用`@Value`注释来注入单个应用程序参数。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.10.使用 ApplicationRunner 或 CommandLineRunner + +如果在`SpringApplication`启动后需要运行某些特定的代码,则可以实现`ApplicationRunner`或`CommandLineRunner`接口。这两个接口以相同的方式工作,并提供一个`run`方法,该方法在`SpringApplication.run(…​)`完成之前被调用。 + +| |此契约非常适合在应用程序启动后但在它开始接受流量之前运行的任务。| +|---|------------------------------------------------------------------------------------------------------------------------| + +`CommandLineRunner`接口以字符串数组的形式提供对应用程序参数的访问,而`ApplicationRunner`接口使用前面讨论的`ApplicationArguments`接口。下面的示例显示了带有`run`方法的`CommandLineRunner`: + +``` +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +@Component +public class MyCommandLineRunner implements CommandLineRunner { + + @Override + public void run(String... args) { + // Do something... + } + +} + +``` + +如果定义了几个`CommandLineRunner`或`ApplicationRunner`bean,并且必须以特定的顺序进行调用,则可以另外实现`org.springframework.core.Ordered`接口或使用`org.springframework.core.annotation.Order`注释。 + +### 1.11.应用程序退出 + +每个`SpringApplication`都向 JVM 注册一个关闭钩子,以确保`ApplicationContext`在退出时优雅地关闭。可以使用所有标准的 Spring 生命周期回调(例如`DisposableBean`接口或`@PreDestroy`注释)。 + +此外,如果希望在调用`SpringApplication.exit()`时返回特定的退出代码,则 bean 可以实现`org.springframework.boot.ExitCodeGenerator`接口。然后可以将此退出代码传递给`System.exit()`,以将其作为状态代码返回,如下例所示: + +``` +import org.springframework.boot.ExitCodeGenerator; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class MyApplication { + + @Bean + public ExitCodeGenerator exitCodeGenerator() { + return () -> 42; + } + + public static void main(String[] args) { + System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args))); + } + +} + +``` + +此外,`ExitCodeGenerator`接口可以通过异常来实现。当遇到这样的异常时, Spring 引导返回由实现的`getExitCode()`方法提供的退出代码。 + +### 1.12.管理功能 + +通过指定`spring.application.admin.enabled`属性,可以为应用程序启用与管理相关的特性。这将在平台`MBeanServer`上公开[SpringApplication AdminMXBean’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/admin/SpringApplicationAdminMXBean.java)。你可以使用此功能远程管理你的 Spring 启动应用程序。这个特性对于任何服务包装器实现都是有用的。 + +| |如果你想知道应用程序在哪个 HTTP 端口上运行,请使用`local.server.port`的键获取该属性。| +|---|----------------------------------------------------------------------------------------------------------------------| + +### 1.13.应用程序启动跟踪 + +在应用程序启动期间,`SpringApplication`和`ApplicationContext`执行许多与应用程序生命周期、bean 生命周期甚至处理应用程序事件有关的任务。使用[ApplicationStartup](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/core/metrics/ApplicationStartup.html), Spring 框架[allows you to track the application startup sequence with `StartupStep` objects](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/core.html#context-functionality-startup)。收集这些数据可以用于分析目的,或者仅仅是为了更好地了解应用程序启动过程。 + +在设置`SpringApplication`实例时,可以选择`ApplicationStartup`实现。例如,要使用`BufferingApplicationStartup`,你可以写: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(MyApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + } + +} + +``` + +第一个可用的实现,`FlightRecorderApplicationStartup`是由 Spring 框架提供的。它将 Spring 特定的启动事件添加到 Java 飞行记录器会话中,用于分析应用程序,并将其 Spring 上下文生命周期与 JVM 事件(例如分配、GCS、类加载……)关联起来。一旦配置完成,你就可以通过启用飞行记录器来运行应用程序来记录数据: + +``` +$ java -XX:StartFlightRecording:filename=recording.jfr,duration=10s -jar demo.jar +``` + +Spring boot 附带`BufferingApplicationStartup`变体;该实现用于缓冲启动步骤并将其导入外部度量系统。应用程序可以在任何组件中请求类型`BufferingApplicationStartup`的 Bean。 + +Spring 还可以将引导配置为公开一个[“启动”端点](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/htmlsingle/#startup),该[“启动”端点](https://docs.spring.io/spring-boot/docs/2.6.4/actuator-api/htmlsingle/#startup)作为 JSON 文档提供该信息。 + +## 2. 外部化配置 + + +Spring 引导允许你外部化你的配置,以便你可以在不同的环境中使用相同的应用程序代码。你可以使用各种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。 + +可以使用`@Value`注释直接将属性值注入到 bean 中,该注释可通过 Spring 的`Environment`抽象访问,也可以通过[绑定到结构化对象](#features.external-config.typesafe-configuration-properties)通过`@Configuration属性`访问。 + +Spring 引导使用非常特殊的`PropertySource`顺序,该顺序被设计为允许合理地覆盖值。按以下顺序考虑属性(来自较低项的值覆盖了较早的项): + +1. 默认属性(通过设置`SpringApplication.setDefault属性`指定)。 + +2. [@PropertySource](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/context/annotation/PropertySource.html)类上的注释。请注意,在刷新应用程序上下文之前,不会将此类属性源添加到`Environment`中。现在配置某些属性(如`logging.*`和`spring.main.*`)已经太晚了,这些属性是在开始刷新之前读取的。 + +3. 配置数据(如`application.properties`文件)。 + +4. a`RandomValuePropertySource`仅在`random.*`中具有属性。 + +5. OS 环境变量。 + +6. java system properties(`system.get属性()’)。 + +7. 来自`java:comp/env`的 JNDI 属性。 + +8. `ServletContext`init 参数。 + +9. `ServletConfig`init 参数。 + +10. 来自`SPRING_APPLICATION_JSON`的属性(嵌入在环境变量或系统属性中的内联 JSON)。 + +11. 命令行参数。 + +12. `properties`测试中的属性。 + 在[@springboottest](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/test/context/SpringBootTest.html)和[测试用于测试应用程序的特定部分的注释](#features.testing.spring-boot-applications.autoconfigured-tests)上可用。 + +13. [@TestPropertySource](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/test/context/TestPropertySource.html)对测试的注释。 + +14. 当 devtools 处于活动状态时,[DevTools 全局设置属性](using.html#using.devtools.globalsettings)在`$HOME/.config/spring-boot`目录中。 + +配置数据文件按以下顺序考虑: + +1. [应用程序属性](#features.external-config.files)打包在你的 jar(`application.properties’和 YAML 变体)中。 + +2. [特定于配置文件的应用程序属性](#features.external-config.files.profile-specific)打包在你的 jar(`application-{profile}.properties’和 YAML 变体)中。 + +3. [应用程序属性](#features.external-config.files)在你打包的 jar 之外(“application.properties”和 YAML 变体)。 + +4. [特定于配置文件的应用程序属性](#features.external-config.files.profile-specific)在你打包的 jar 之外(`application-{profile}.properties’和 YAML 变体)。 + +| |对于整个应用程序,建议使用一种格式。如果你的配置文件中同时有`.properties`和`.yml`两种格式,则`.properties`优先。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +为了提供一个具体的示例,假设你开发了一个`@Component`,它使用了`name`属性,如以下示例所示: + +``` +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @Value("${name}") + private String name; + + // ... + +} + +``` + +在你的应用程序 Classpath(例如,在你的 jar 内部)上,你可以拥有一个`application.properties`文件,该文件为`name`提供了一个合理的默认属性值。在新环境中运行时,可以在 jar 之外提供一个`application.properties`文件,该文件覆盖`name`。对于一次性测试,可以使用特定的命令行开关启动(例如,`java -jar app.jar --name="Spring"`)。 + +| |`env`和`configprops`端点可以用于确定属性为什么具有特定值。
你可以使用这两个端点来诊断意外的属性值。
有关详细信息,请参见“[生产就绪功能](actuator.html#actuator.endpoints)一节。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.1.访问命令行属性 + +默认情况下,`SpringApplication`将任何命令行选项参数(即以`--`开头的参数,例如`--server.port=9000`)转换为`property`,并将其添加到 Spring `Environment`。如前所述,命令行属性总是优先于基于文件的属性源。 + +如果不希望将命令行属性添加到`Environment`,则可以通过使用`SpringApplication.setAddCommandLine属性(false)`禁用它们。 + +### 2.2.JSON 应用程序属性 + +环境变量和系统属性通常有一些限制,这意味着一些属性名称不能使用。 Spring Boot 允许你将一组属性编码到一个 JSON 结构中,以帮助实现这一点。 + +当应用程序启动时,任何`spring.application.json`或`SPRING_APPLICATION_JSON`属性都将被解析并添加到`Environment`中。 + +例如,`SPRING_APPLICATION_JSON`属性可以在 un\*x shell 的命令行中作为环境变量提供: + +``` +$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar +``` + +在前面的示例中,在 Spring `Environment`中得到`my.name=test`。 + +同样的 JSON 也可以作为系统属性提供: + +``` +$ java -Dspring.application.json='{"my":{"name":"test"}}' -jar myapp.jar +``` + +或者,你可以通过使用一个命令行参数来提供 JSON: + +``` +$ java -jar myapp.jar --spring.application.json='{"my":{"name":"test"}}' +``` + +如果要部署到经典的应用程序服务器,还可以使用名为`java:comp/env/spring.application.json`的 JNDI 变量。 + +| |虽然来自 JSON 的`null`值将被添加到结果属性源中,但`PropertySourcesPropertyResolver`将`null`属性视为缺失值。
这意味着 JSON 不能使用`null`值覆盖来自较低顺序属性源的属性。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.3.外部应用程序属性 + +Spring 当应用程序启动时,启动将自动从以下位置查找并加载`application.properties`和`application.yaml`文件: + +1. 来自 Classpath + + 1. Classpath 根 + + 2. Classpath `/config`包 + +2. 从当前目录 + + 1. 当前目录 + + 2. 当前目录中的`/config`子目录 + + 3. `/config`子目录的直接子目录 + +该列表是按优先级排序的(来自较低项的值覆盖了较早的项)。来自加载的文件的文档被添加为`PropertySources`到 Spring `Environment`。 + +如果不喜欢`application`作为配置文件名,则可以通过指定`spring.config.name`环境属性切换到另一个文件名。例如,要查找`myproject.properties`和`myproject.yaml`文件,你可以按以下方式运行应用程序: + +``` +$ java -jar myproject.jar --spring.config.name=myproject +``` + +你还可以使用`spring.config.location`Environment 属性引用显式位置。此属性接受要检查的一个或多个位置的逗号分隔列表。 + +下面的示例展示了如何指定两个不同的文件: + +``` +$ java -jar myproject.jar --spring.config.location=\ + optional:classpath:/default.properties,\ + optional:classpath:/override.properties +``` + +| |如果[位置是可选的](#features.external-config.files.optional-prefix),则使用前缀`optional:`,并且不介意它们是否不存在。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------| + +| |`spring.config.name`、`spring.config.location`和`spring.config.additional-location`很早就被用于确定必须加载哪些文件。
它们必须被定义为一个环境属性(通常是一个 OS 环境变量、一个系统属性或一个命令行参数)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果`spring.config.location`包含目录(与文件相反),则它们应该以`/`结尾。在运行时,在加载之前,将使用从`spring.config.name`生成的名称对它们进行追加。在`spring.config.location`中指定的文件是直接导入的。 + +| |还扩展了目录和文件位置值以检查[特定于配置文件的文件](#features.external-config.files.profile-specific)。
例如,如果你有`spring.config.location`的`classpath:myconfig.properties`,你还会发现适当的`classpath:myconfig-.properties`文件已加载。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +在大多数情况下,你添加的每个`spring.config.location`项都将引用一个文件或目录。位置是按照定义的顺序进行处理的,后面的位置可以覆盖前面的位置的值。 + +如果你有一个复杂的位置设置,并且你使用特定于配置文件的配置文件,那么你可能需要提供进一步的提示,以便 Spring 引导知道它们应该如何分组。位置组是所有位置都在同一级别上被考虑的位置的集合。例如,你可能希望对所有 Classpath 位置进行分组,然后对所有外部位置进行分组。位置组中的项应该用`;`分隔。有关更多详细信息,请参见“[配置文件特定的文件](#features.external-config.files.profile-specific)”小节中的示例。 + +使用`spring.config.location`配置的位置替换默认位置。例如,如果`spring.config.location`被配置为值`optional:classpath:/custom-config/,optional:file:./custom-config/`,则考虑的完整位置集为: + +1. `optional:classpath:custom-config/` + +2. `optional:file:./custom-config/` + +如果你更喜欢添加其他位置,而不是替换它们,那么可以使用`spring.config.additional-location`。从其他位置加载的属性可以覆盖默认位置中的属性。例如,如果`spring.config.additional-location`被配置为值`optional:classpath:/custom-config/,optional:file:./custom-config/`,则考虑的完整位置集为: + +1. `optional:classpath:/;optional:classpath:/config/` + +2. `optional:file:./;optional:file:./config/;optional:file:./config/*/` + +3. `optional:classpath:custom-config/` + +4. `optional:file:./custom-config/` + +这种搜索排序允许你在一个配置文件中指定默认值,然后有选择地重写另一个配置文件中的这些值。你可以在其中一个默认位置的`application.properties`(或你选择的任何其他带有`spring.config.name`的 basename)中为你的应用程序提供默认值。然后可以在运行时使用位于自定义位置之一中的不同文件重写这些默认值。 + +| |如果使用环境变量而不是系统属性,大多数操作系统都不允许使用周期分隔的键名,但是可以使用下划线代替(例如,`SPRING_CONFIG_NAME`而不是`spring.config.name`)。
有关详细信息,请参见[来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你的应用程序运行在 Servlet 容器或应用程序服务器中,那么可以使用 JNDI 属性(在`java:comp/env`中)或 Servlet 上下文初始化参数来代替环境变量或系统属性。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.3.1.可选位置 + +默认情况下,当指定的配置数据位置不存在时, Spring 启动将抛出`ConfigDataLocationNotFoundException`,并且你的应用程序将不会启动。 + +如果你想指定一个位置,但并不介意它并不总是存在,那么可以使用`optional:`前缀。你可以在`spring.config.location`和`spring.config.additional-location`属性以及[`spring.config.import`](#features.external-config.files.importing)声明中使用此前缀。 + +例如,`spring.config.import`值`optional:file:./myconfig.properties`允许你的应用程序启动,即使缺少`myconfig.properties`文件。 + +如果要忽略所有`ConfigDataLocationNotFoundExceptions`并始终继续启动应用程序,则可以使用`spring.config.on-not-found`属性。使用`SpringApplication.setDefault属性(…​)`或使用系统/环境变量将值设置为`ignore`。 + +#### 2.3.2.通配符位置 + +如果配置文件位置包括最后一个路径段的`*`字符,则将其视为通配符位置。在加载配置时,通配符将被展开,以便也检查直接的子目录。当存在多个配置属性源时,通配符位置在 Kubernetes 等环境中特别有用。 + +例如,如果你有一些 Redis 配置和一些 MySQL 配置,那么你可能希望将这两个配置分开,同时要求这两个配置都存在于`application.properties`文件中。这可能会导致两个单独的`application.properties`文件安装在不同的位置,例如`/config/redis/application.properties`和`/config/mysql/application.properties`。在这种情况下,具有通配符位置`config/*/`,将导致这两个文件都被处理。 + +默认情况下, Spring 引导在默认搜索位置中包括`config/*/`。这意味着你的 jar 之外的`/config`目录的所有子目录都将被搜索。 + +可以使用`spring.config.location`和`spring.config.additional-location`属性自己使用通配符位置。 + +| |通配符位置必须只包含一个`*`,并且对于目录搜索位置以`*/`结尾,对于文件搜索位置以`*/`结尾。
通配符位置根据文件名的绝对路径按字母顺序排序。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |通配符位置只与外部目录一起工作。
在`classpath:`位置中不能使用通配符。| +|---|-----------------------------------------------------------------------------------------------------------------| + +#### 2.3.3.配置文件特定的文件 + +除了`application`属性文件外, Spring 引导还将尝试使用命名约定`application-{profile}`加载配置文件特定的文件。例如,如果应用程序激活名为`prod`的配置文件并使用 YAML 文件,那么`application.yml`和`application-prod.yml`都将被考虑。 + +配置文件特定的属性是从与标准`application.properties`相同的位置加载的,配置文件特定的文件总是覆盖非特定的文件。如果指定了多个配置文件,则应用最后胜出策略。例如,如果配置文件`prod,live`由`spring.profiles.active`属性指定,则`application-prod.properties`中的值可以被`application-live.properties`中的值覆盖。 + +| |Last-wins 策略适用于[location group](#features.external-config.files.location-groups)级别。
a`spring.config.location`of`classpath:/cfg/,classpath:/ext/`将不具有与`classpath:/cfg/;classpath:/ext/`相同的覆盖规则。

例如,继续我们上面的`prod,live`示例,我们可能会有以下文件:

``
/cfg
应用程序-live.properties
ext
应用程序-live.properties
应用程序-prod.properties
<<<<<>>的文件时,我们有一个>>>>>>>>>`/cfg/application-live.properties`

2。`/ext/application-prod.properties`

3。`/ext/application-live.properties`

当我们有`classpath:/cfg/;classpath:/ext/`代替(用`;`分隔符)时,我们在同一水平上处理`/cfg`和`/ext`:

1。`/ext/application-prod.properties`

2。`/cfg/application-live.properties`

3。`/ext/application-live.properties`| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +`Environment`具有一组默认配置文件(默认情况下,`[default]`),如果未设置活动配置文件,则使用这些配置文件。换句话说,如果没有配置文件被显式激活,那么`application-default`中的属性将被考虑。 + +| |属性文件只加载一次。
如果你已经直接[imported](#features.external-config.files.importing)配置文件特定的属性文件,那么它将不会被导入第二次。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.3.4.导入附加数据 + +应用程序属性可以使用`spring.config.import`属性从其他位置导入更多配置数据。导入在被发现时进行处理,并被视为在声明导入的文档下面插入的附加文档。 + +例如,你的 Classpath `application.properties`文件中可能包含以下内容: + +属性 + +``` +spring.application.name=myapp +spring.config.import=optional:file:./dev.properties +``` + +Yaml + +``` +spring: + application: + name: "myapp" + config: + import: "optional:file:./dev.properties" +``` + +这将触发当前目录中`dev.properties`文件的导入(如果存在这样的文件)。导入的`dev.properties`中的值将优先于触发导入的文件。在上面的示例中,`dev.properties`可以将`spring.application.name`重新定义为不同的值。 + +一次进口,无论申报了多少次,都只能进口一次。在 属性/YAML 文件中的单个文档中定义的导入顺序并不重要。例如,下面的两个示例产生相同的结果: + +属性 + +``` +spring.config.import=my.properties +my.property=value +``` + +Yaml + +``` +spring: + config: + import: "my.properties" +my: + property: "value" +``` + +属性 + +``` +my.property=value +spring.config.import=my.properties +``` + +Yaml + +``` +my: + property: "value" +spring: + config: + import: "my.properties" +``` + +在上述两个示例中,来自`my.properties`文件的值将优先于触发其导入的文件。 + +可以在一个`spring.config.import`键下指定多个位置。位置将按照定义的顺序进行处理,以后的导入将优先处理。 + +| |在适当的情况下,[特定于配置文件的变体](#features.external-config.files.profile-specific)也被考虑用于导入。
上面的示例将导入`my.properties`以及任何`my-.properties`变体。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Spring 引导包括可插入的 API,允许支持各种不同的位置地址。
默认情况下可以导入 Java 属性、YAML 和“[配置树](#features.external-config.files.configtree)”。

第三方 JAR 可以提供对额外技术的支持(不要求文件是本地的)。
例如,你可以想象配置数据来自外部商店,例如 Consul、Apache ZooKeeper 或 Netflix Archaius。

如果你想支持自己的位置,请参阅`ConfigDataLocationResolver`和`ConfigDataLoader`包中的类。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.3.5.导入无扩展文件 + +一些云平台不能向卷安装的文件添加文件扩展名。要导入这些无扩展文件,你需要给 Spring 引导一个提示,以便它知道如何加载它们。你可以通过在方括号中添加扩展提示来实现此目的。 + +例如,假设你有一个`/etc/config/myconfig`文件,你希望将其导入为 YAML。你可以使用以下方法从`application.properties`导入它: + +属性 + +``` +spring.config.import=file:/etc/config/myconfig[.yaml] +``` + +Yaml + +``` +spring: + config: + import: "file:/etc/config/myconfig[.yaml]" +``` + +#### 2.3.6.使用配置树 + +在云平台(如 Kubernetes)上运行应用程序时,通常需要读取平台提供的配置值。为此目的使用环境变量并不少见,但这可能有缺点,特别是如果值应该保密的话。 + +作为环境变量的一种替代方法,许多云平台现在允许你将配置映射到已安装的数据卷中。例如,Kubernetes 可以同时装载[`ConfigMaps`](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap)和[`Secrets`](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-files-from-a-pod)。 + +可以使用两种常见的卷安装模式: + +1. 一个文件包含一组完整的属性(通常写为 YAML)。 + +2. 多个文件被写入目录树,文件名成为“键”,内容成为“值”。 + +对于第一种情况,可以使用`spring.config.import`直接导入 YAML 或属性文件,如[above](#features.external-config.files.importing)所述。对于第二种情况,你需要使用`configtree:`前缀,以便 Spring 引导知道需要将所有文件作为属性公开。 + +作为一个例子,让我们想象一下 Kubernetes 已经安装了以下卷: + +``` +etc/ + config/ + myapp/ + username + password +``` + +`username`文件的内容将是一个配置值,而`password`的内容将是一个秘密。 + +要导入这些属性,可以将以下内容添加到`application.properties`或`application.yaml`文件中: + +属性 + +``` +spring.config.import=optional:configtree:/etc/config/ +``` + +Yaml + +``` +spring: + config: + import: "optional:configtree:/etc/config/" +``` + +然后可以以通常的方式从`Environment`访问或注入`myapp.username`和`myapp.password`属性。 + +| |配置树下的文件夹形成了属性名。
在上面的示例中,要访问属性为`username`和`password`,你可以将`spring.config.import`设置为`optional:configtree:/etc/config/myapp`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |例如,在上面的示例中,在`/etc/config`中名为`myapp.username`的文件将在`/etc/config`中产生`myapp.username`属性。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |根据预期的内容,配置树值可以绑定到字符串`String`和`byte[]`两种类型。| +|---|---------------------------------------------------------------------------------------------------------------------| + +如果有多个配置树要从同一个父文件夹导入,则可以使用通配符快捷方式。任何以`/*/`结尾的`configtree:`位置都将把所有直接的子节点作为配置树导入。 + +例如,给出以下卷: + +``` +etc/ + config/ + dbconfig/ + db/ + username + password + mqconfig/ + mq/ + username + password +``` + +可以使用`configtree:/etc/config/*/`作为导入位置: + +属性 + +``` +spring.config.import=optional:configtree:/etc/config/*/ +``` + +Yaml + +``` +spring: + config: + import: "optional:configtree:/etc/config/*/" +``` + +这将添加`db.username`,`db.password`,`mq.username`和`mq.password`属性。 + +| |使用通配符加载的目录是按字母顺序排序的。
如果需要不同的顺序,那么应该将每个位置作为单独的导入列表| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------| + +配置树也可以用于 Docker 秘密。当 Docker Swarm 服务被授予对秘密的访问权限时,该秘密将被装载到容器中。例如,如果一个名为`db.password`的秘密被安装在位置`/run/secrets/`上,则可以使用以下方法使`db.password`对 Spring 环境可用: + +属性 + +``` +spring.config.import=optional:configtree:/run/secrets/ +``` + +Yaml + +``` +spring: + config: + import: "optional:configtree:/run/secrets/" +``` + +#### 2.3.7.财产占位符 + +`application.properties`和`application.yml`中的值在使用时会通过现有的`Environment`进行过滤,因此你可以引用以前定义的值(例如,从系统属性)。标准的`${name}`属性占位符语法可以在一个值的任何地方使用。 + +例如,下面的文件将`app.description`设置为“MyApp 是一个 Spring 启动应用程序”: + +属性 + +``` +app.name=MyApp +app.description=${app.name} is a Spring Boot application +``` + +Yaml + +``` +app: + name: "MyApp" + description: "${app.name} is a Spring Boot application" +``` + +| |你也可以使用此技术来创建现有 Spring 引导属性的“短”变体。
有关详细信息,请参见*[howto.html](howto.html#howto.properties-and-configuration.short-command-line-arguments)*。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.3.8.使用多文档文件 + +Spring 启动允许将单个物理文件拆分成多个逻辑文档,每个逻辑文档都是独立添加的。文件的处理顺序是从上到下的。后面的文档可以覆盖前面的文档中定义的属性。 + +对于`application.yml`文件,使用标准的 YAML 多文档语法。三个连续的连字符表示一个文档的结束,和下一个文档的开始。 + +例如,下面的文件有两个逻辑文档: + +``` +spring: + application: + name: "MyApp" +--- +spring: + application: + name: "MyCloudApp" + config: + activate: + on-cloud-platform: "kubernetes" +``` + +对于`application.properties`文件,使用特殊的`#---`注释来标记文档分割: + +``` +spring.application.name=MyApp +#--- +spring.application.name=MyCloudApp +spring.config.activate.on-cloud-platform=kubernetes +``` + +| |属性文件分隔符不能有任何前置空格,并且必须正好有三个连字符。
分隔符前后的行不得是注释。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |多文档属性文件通常与激活属性结合使用,例如`spring.config.activate.on-profile`。
有关详细信息,请参见[next section](#features.external-config.files.activation-properties)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |使用`@PropertySource`或`@TestPropertySource`注释无法加载多文档属性文件。| +|---|-------------------------------------------------------------------------------------------------------------------| + +#### 2.3.9.活化特性 + +有时,只有在满足某些条件时才激活给定的一组属性是有用的。例如,你可能拥有仅在特定配置文件处于活动状态时才相关的属性。 + +可以使用`spring.config.activate.*`有条件地激活属性文档。 + +以下激活属性可用: + +| Property |注| +|-------------------|------------------------------------------------------------------------| +| `on-profile` |必须匹配才能使文档处于活动状态的配置文件表达式。| +|`on-cloud-platform`|要使文档处于活动状态,必须检测到的`CloudPlatform`。| + +例如,下面指定了第二个文档只有在 Kubernetes 上运行时才处于活动状态,并且只有当“prod”或“staging”配置文件处于活动状态时才处于活动状态: + +Properties + +``` +myprop=always-set +#--- +spring.config.activate.on-cloud-platform=kubernetes +spring.config.activate.on-profile=prod | staging +myotherprop=sometimes-set +``` + +Yaml + +``` +myprop: + "always-set" +--- +spring: + config: + activate: + on-cloud-platform: "kubernetes" + on-profile: "prod | staging" +myotherprop: "sometimes-set" +``` + +### 2.4.加密属性 + +Spring 引导不提供任何内置的对加密属性值的支持,但是,它确实提供了修改包含在 Spring `Environment`中的值所必需的钩点。`EnvironmentPostProcessor`接口允许你在应用程序启动之前操作`Environment`。详见[howto.html](howto.html#howto.application.customize-the-environment-or-application-context)。 + +如果你需要一种安全的方式来存储凭据和密码,[Spring Cloud Vault](https://cloud.spring.io/spring-cloud-vault/)项目提供了在[HashiCorp Vault](https://www.vaultproject.io/)中存储外部化配置的支持。 + +### 2.5.与 YAML 合作 + +[YAML](https://yaml.org)是 JSON 的超集,因此,是用于指定分层配置数据的一种方便的格式。只要在 Classpath 上有[SnakeYAML](https://bitbucket.org/asomov/snakeyaml)库,`SpringApplication`类就会自动支持 YAML 作为属性的替代。 + +| |如果你使用“starter”,那么 SnakeYAML 将由`spring-boot-starter`自动提供。| +|---|------------------------------------------------------------------------------------| + +#### 2.5.1.将 YAML 映射到属性 + +YAML 文档需要从其层次结构格式转换为可与 Spring `Environment`一起使用的平面结构。例如,考虑以下 YAML 文档: + +``` +environments: + dev: + url: "https://dev.example.com" + name: "Developer Setup" + prod: + url: "https://another.example.com" + name: "My Cool App" +``` + +为了从`Environment`中访问这些属性,它们将按以下方式进行平坦化: + +``` +environments.dev.url=https://dev.example.com +environments.dev.name=Developer Setup +environments.prod.url=https://another.example.com +environments.prod.name=My Cool App +``` + +同样,YAML 的列表也需要扁平化。它们被表示为带有`[index]`dereferencers 的属性键。例如,考虑以下 YAML: + +``` +my: + servers: + - "dev.example.com" + - "another.example.com" +``` + +前面的示例将转换为以下属性: + +``` +my.servers[0]=dev.example.com +my.servers[1]=another.example.com +``` + +| |使用`[index]`表示法的属性可以使用 Spring boot 的`List`对象绑定到 Java`Set`对象`Binder`class.
有关更多详细信息,请参见下面的“[类型安全配置属性](#features.external-config.typesafe-configuration-properties)”小节。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |无法通过使用`@PropertySource`或`@TestPropertySource`注释来加载 YAML 文件。
因此,在需要以这种方式加载值的情况下,需要使用属性文件。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.5.2.直接加载 YAML + +Spring Framework 提供了两个方便的类,它们可以用来加载 YAML 文档。`YamlPropertiesFactoryBean`将 YAML 加载为`Properties`,`YamlMapFactoryBean`将 YAML 加载为`Map`。 + +如果希望将 YAML 加载为 Spring `PropertySource`,也可以使用`YamlPropertySourceLoader`类。 + +### 2.6.配置随机值 + +`RandomValuePropertySource`对于注入随机值(例如,注入到秘密或测试用例中)很有用。它可以产生整数、长、UUID 或字符串,如以下示例所示: + +Properties + +``` +my.secret=${random.value} +my.number=${random.int} +my.bignumber=${random.long} +my.uuid=${random.uuid} +my.number-less-than-ten=${random.int(10)} +my.number-in-range=${random.int[1024,65536]} +``` + +Yaml + +``` +my: + secret: "${random.value}" + number: "${random.int}" + bignumber: "${random.long}" + uuid: "${random.uuid}" + number-less-than-ten: "${random.int(10)}" + number-in-range: "${random.int[1024,65536]}" +``` + +`random.int*`语法是`OPEN value (,max) CLOSE`,其中`OPEN,CLOSE`是任意字符,`value,max`是整数。如果提供了`max`,则`value`是最小值,`max`是最大值(不包含)。 + +### 2.7.配置系统环境属性 + +Spring 引导支持为环境属性设置前缀。如果系统环境由具有不同配置需求的多个 Spring 引导应用程序共享,那么这是有用的。系统环境属性的前缀可以直接设置在`SpringApplication`上。 + +例如,如果你将前缀设置为`input`,那么在系统环境中,诸如`remote.timeout`之类的属性也将解析为`input.remote.timeout`。 + +### 2.8.类型安全配置属性 + +使用`@Value("${property}")`注释来注入配置属性有时会很麻烦,尤其是在使用多个属性或数据本质上是分层的情况下。 Spring 引导提供了一种处理属性的替代方法,该方法允许强类型 bean 控制和验证应用程序的配置。 + +| |另见[differences between `@Value` and type-safe configuration properties](#features.external-config.typesafe-configuration-properties.vs-value-annotation)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.1.JavaBean 属性绑定 + +可以绑定声明标准 JavaBean 属性的 Bean,如下例所示: + +``` +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("my.service") +public class MyProperties { + + private boolean enabled; + + private InetAddress remoteAddress; + + private final Security security = new Security(); + + // getters / setters... + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public InetAddress getRemoteAddress() { + return this.remoteAddress; + } + + public void setRemoteAddress(InetAddress remoteAddress) { + this.remoteAddress = remoteAddress; + } + + public Security getSecurity() { + return this.security; + } + + public static class Security { + + private String username; + + private String password; + + private List roles = new ArrayList<>(Collections.singleton("USER")); + + // getters / setters... + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public List getRoles() { + return this.roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + } + +} + +``` + +前面的 POJO 定义了以下属性: + +* `my.service.enabled`,默认值为`false`。 + +* `my.service.remote-address`,其类型可以从`String`强制执行。 + +* `my.service.security.username`,带有嵌套的“security”对象,其名称由属性的名称确定。特别是,该类型在那里根本不使用,并且可能是`SecurityProperties`。 + +* `my.service.security.password`. + +* `my.service.security.roles`,其集合`String`默认为`USER`。 + +| |映射到 Spring boot 中可用的`@ConfigurationProperties`类的属性(通过属性文件、YAML 文件、环境变量和其他机制进行配置)是公共 API,但是类本身的访问器(getters/setters)并不是直接使用的。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |这种安排依赖于缺省的空构造函数,而 getter 和 setter 通常是强制性的,因为绑定是通过标准的 Java bean 属性描述符进行的,就像 Spring MVC 中一样。
在以下情况下,可以省略 setter:

* 映射,只要它们被初始化,需要一个 getter,但不一定是 setter,因为它们可以通过绑定程序进行变异,

* 集合和数组可以通过索引(通常使用 YAML)或使用一个逗号分隔的值(属性)进行访问,
在后一种情况下,setter 是强制性的。
我们建议总是为这样的类型添加一个 setter。
如果你初始化一个集合,请确保它不是不可变的(如前面的示例),

* 如果初始化了嵌套的 POJO 属性(如前面示例中的`Security`字段),setter 不是必需的。
如果你希望活页夹使用其默认构造函数动态地创建实例,你需要一个 setter。

有些人使用 Project Lombok 来自动添加 getter 和 setter。
确保 Lombok 不会为这样的类型生成任何特定的构造函数,因为容器会自动使用它来实例化对象。

最后,只考虑标准的 Java Bean 属性,不支持对静态属性的绑定。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.2.构造函数绑定 + +上一节中的示例可以以一种不可更改的方式重写,如下例所示: + +``` +import java.net.InetAddress; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.boot.context.properties.bind.DefaultValue; + +@ConstructorBinding +@ConfigurationProperties("my.service") +public class MyProperties { + + // fields... + + private final boolean enabled; + + private final InetAddress remoteAddress; + + private final Security security; + + public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) { + this.enabled = enabled; + this.remoteAddress = remoteAddress; + this.security = security; + } + + // getters... + + public boolean isEnabled() { + return this.enabled; + } + + public InetAddress getRemoteAddress() { + return this.remoteAddress; + } + + public Security getSecurity() { + return this.security; + } + + public static class Security { + + // fields... + + private final String username; + + private final String password; + + private final List roles; + + public Security(String username, String password, @DefaultValue("USER") List roles) { + this.username = username; + this.password = password; + this.roles = roles; + } + + // getters... + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + + public List getRoles() { + return this.roles; + } + + } + +} + +``` + +在此设置中,`@ConstructorBinding`注释用于指示应该使用构造函数绑定。这意味着绑定器将期望找到一个具有你希望绑定的参数的构造函数。如果你使用的是 Java16 或更高版本,那么构造函数绑定可以用于记录。在这种情况下,除非你的记录有多个构造函数,否则不需要使用`@ConstructorBinding`。 + +`@ConstructorBinding`类的嵌套成员(例如上面示例中的`Security`)也将通过其构造函数绑定。 + +可以使用`@DefaultValue`指定默认值,并且将应用相同的转换服务来强制将`String`值强制到丢失属性的目标类型。默认情况下,如果没有属性绑定到`Security`,则`MyProperties`实例将包含`null`值`security`。如果你希望返回`Security`的非空实例,即使没有属性绑定到它,也可以使用空的`@DefaultValue`注释来这样做: + +``` +public MyProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) { + this.enabled = enabled; + this.remoteAddress = remoteAddress; + this.security = security; +} + +``` + +| |要使用构造函数绑定,必须使用`@EnableConfigurationProperties`或配置属性扫描来启用类。
你不能使用构造函数绑定由常规 Spring 机制创建的 bean(例如`@Component`bean,使用`@Bean`方法创建的 bean 或使用`@Import`加载的 bean)| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你的类有多个构造函数,那么你也可以在应该绑定的构造函数上直接使用`@ConstructorBinding`。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------| + +| |不推荐使用`java.util.Optional`和`@ConfigurationProperties`,因为它主要是作为返回类型使用的。
因此,它不适合配置属性注入。
用于与其他类型的属性保持一致,如果你确实声明了一个`Optional`属性并且它没有值,则将绑定`null`而不是空的`Optional`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.3.启用 @configrationProperties-注释类型 + +Spring Boot 提供了用于绑定`@ConfigurationProperties`类型并将它们注册为 bean 的基础设施。你可以逐个类地启用配置属性,也可以启用与组件扫描工作方式类似的配置属性扫描。 + +有时,用`@ConfigurationProperties`注释的类可能不适合扫描,例如,如果你正在开发自己的自动配置,或者希望有条件地启用它们。在这些情况下,使用`@EnableConfigurationProperties`注释指定要处理的类型列表。这可以在任何`@Configuration`类上完成,如以下示例所示: + +``` +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SomeProperties.class) +public class MyConfiguration { + +} + +``` + +要使用配置属性扫描,请将`@ConfigurationPropertiesScan`注释添加到应用程序中。通常,它被添加到用`@SpringBootApplication`注释的主应用程序类中,但它可以添加到任何`@Configuration`类中。默认情况下,将从声明注释的类的包中进行扫描。如果你想要定义要扫描的特定包,可以按照以下示例中所示的方式进行: + +``` +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; + +@SpringBootApplication +@ConfigurationPropertiesScan({ "com.example.app", "com.example.another" }) +public class MyApplication { + +} + +``` + +| |当使用配置属性扫描或通过`@EnableConfigurationProperties`注册`@ConfigurationProperties` Bean 时, Bean 有一个常规名称:`-`,其中``是`@ConfigurationProperties`注释中指定的环境密钥前缀,``是 Bean 的完全限定名称。
如果注释不提供任何前缀,只使用 Bean 的完全限定名称。

上面示例中的 Bean 名称是`com.example.app-com.example.app.SomeProperties`。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +我们建议`@ConfigurationProperties`只处理环境,尤其是不从上下文注入其他 bean。对于角的情况,可以使用 setter 注入或框架提供的任何`*Aware`接口(例如`EnvironmentAware`如果需要访问`Environment`)。如果你仍然希望使用构造函数注入其他 bean,那么配置属性 Bean 必须使用`@Component`进行注释,并使用基于 JavaBean 的属性绑定。 + +#### 2.8.4.使用 @configrationProperties 注释类型 + +这种配置风格在`SpringApplication`外部 YAML 配置中尤其适用,如以下示例所示: + +``` +my: + service: + remote-address: 192.168.1.1 + security: + username: "admin" + roles: + - "USER" + - "ADMIN" +``` + +要使用`@ConfigurationProperties`bean,你可以以与任何其他 Bean 相同的方式注入它们,如以下示例所示: + +``` +import org.springframework.stereotype.Service; + +@Service +public class MyService { + + private final SomeProperties properties; + + public MyService(SomeProperties properties) { + this.properties = properties; + } + + public void openConnection() { + Server server = new Server(this.properties.getRemoteAddress()); + server.start(); + // ... + } + + // ... + +} + +``` + +| |使用`@ConfigurationProperties`还可以生成元数据文件,IDE 可以使用这些文件为你自己的密钥提供自动补全功能。
有关详细信息,请参见[appendix](configuration-metadata.html#appendix.configuration-metadata)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.5.第三方配置 + +除了使用`@ConfigurationProperties`对类进行注释外,你还可以在 public`@Bean`方法上使用它。当你希望将属性绑定到你无法控制的第三方组件时,这样做会特别有用。 + +要从`Environment`属性配置 Bean,将`@ConfigurationProperties`添加到其 Bean 注册中,如以下示例所示: + +``` +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class ThirdPartyConfiguration { + + @Bean + @ConfigurationProperties(prefix = "another") + public AnotherComponent anotherComponent() { + return new AnotherComponent(); + } + +} + +``` + +任何用`another`前缀定义的 JavaBean 属性都映射到`AnotherComponent` Bean 上,其方式类似于前面的`SomeProperties`示例。 + +#### 2.8.6.松弛结合 + +Spring Boot 使用一些宽松的规则将`Environment`属性绑定到`@ConfigurationProperties`bean,因此不需要在`Environment`属性名称和 Bean 属性名称之间进行精确匹配。这是有用的常见示例,包括 dash 分隔的环境属性(例如,`context-path`绑定到`contextPath`)和大写的环境属性(例如,`PORT`绑定到`port`)。 + +例如,考虑以下`@ConfigurationProperties`类: + +``` +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "my.main-project.person") +public class MyPersonProperties { + + private String firstName; + + public String getFirstName() { + return this.firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + +} + +``` + +使用前面的代码,可以使用以下属性名称: + +| Property |注| +|-----------------------------------|----------------------------------------------------------------------------------------------| +|`my.main-project.person.first-name`|烤肉串的情况下,这是建议在`.properties`和`.yml`文件中使用的。| +|`my.main-project.person.firstName` |标准的驼峰大小写语法。| +|`my.main-project.person.first_name`|下划线符号,这是在`.properties`和`.yml`文件中使用的一种替代格式。| +| `MY_MAINPROJECT_PERSON_FIRSTNAME` |大写格式,这是建议时,使用系统环境变量.| + +| |注释`prefix`的值*必须*以烤串为例(小写并用`-`分隔,如`my.main-project.person`)。| +|---|---------------------------------------------------------------------------------------------------------------------------------| + +| Property Source |简单| List | +|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Properties Files |camel case、kebab case 或下划线符号| Standard list syntax using `[ ]` or comma-separated values | +| YAML Files |camel case、kebab case 或下划线符号| Standard YAML list syntax or comma-separated values | +|Environment Variables|大写格式,下划线作为分隔符(参见[来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables))。|Numeric values surrounded by underscores (see [来自环境变量的绑定](#features.external-config.typesafe-configuration-properties.relaxed-binding.environment-variables))| +| System properties |camel case、kebab case 或下划线符号| Standard list syntax using `[ ]` or comma-separated values | + +| |我们建议在可能的情况下,将属性存储在小写字母的烤肉串格式中,例如`my.person.first-name=Rod`。| +|---|-----------------------------------------------------------------------------------------------------------------------| + +##### 绑定地图 # + +当绑定到`Map`属性时,你可能需要使用一个特殊的括号表示法,以便保留原来的`key`值。如果键不被`[]`包围,则删除所有不是 alpha-numeric,`-`或`.`的字符。 + +例如,考虑将以下属性绑定到`Map`: + +Properties + +``` +my.map.[/key1]=value1 +my.map.[/key2]=value2 +my.map./key3=value3 +``` + +Yaml + +``` +my: + map: + "[/key1]": "value1" + "[/key2]": "value2" + "/key3": "value3" +``` + +| |对于 YAML 文件,括号中需要用引号包围,以便正确解析这些键。| +|---|------------------------------------------------------------------------------------------------| + +上面的属性将绑定到`Map`,其中`/key1`,`/key2`和`key3`作为映射中的键。斜杠已从`key3`中删除,因为它没有被方括号包围。 + +如果你的`key`包含`.`并且绑定到非标量值,那么你有时也可能需要使用括号表示法。例如,将`a.b=c`绑定到`Map`将返回一个带有条目`{"a"={"b"="c"}}`的映射,而`[a.b]=c`将返回一个带有条目`{"a.b"="c"}`的映射。 + +##### Binding from Environment Variables + +大多数操作系统都对可用于环境变量的名称施加了严格的规则。例如,Linux shell 变量只能包含字母(`a`to`z`或`A`to`Z`)、数字(`0`to`9`)或下划线字符(`_`)。按照惯例,UNIX shell 变量的名称也将是大写的。 + +Spring Boot 的宽松绑定规则被尽可能地设计为与这些命名限制兼容。 + +要将规范形式中的属性名转换为环境变量名,你可以遵循以下规则: + +* 将点(`.’)改为下划线(`_’)。 + +* 删除任何破折号。 + +* 转换为大写。 + +例如,配置属性`spring.main.log-startup-info`将是一个名为`SPRING_MAIN_LOGSTARTUPINFO`的环境变量。 + +当绑定到对象列表时,也可以使用环境变量。要绑定到`List`,元素号应该在变量名中用下划线包围。 + +例如,配置属性`my.service[0].other`将使用一个名为`MY_SERVICE_0_OTHER`的环境变量。 + +#### 2.8.7.合并复杂类型 + +当在多个位置配置列表时,可以通过替换整个列表来覆盖该列表。 + +例如,假设一个`MyPojo`对象,其`name`和`description`属性默认为`null`。下面的示例公开了来自`MyProperties`的`MyPojo`对象的列表: + +``` +import java.util.ArrayList; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("my") +public class MyProperties { + + private final List list = new ArrayList<>(); + + public List getList() { + return this.list; + } + +} + +``` + +考虑以下配置: + +Properties + +``` +my.list[0].name=my name +my.list[0].description=my description +#--- +spring.config.activate.on-profile=dev +my.list[0].name=my another name +``` + +Yaml + +``` +my: + list: + - name: "my name" + description: "my description" +--- +spring: + config: + activate: + on-profile: "dev" +my: + list: + - name: "my another name" +``` + +如果`dev`配置文件不是活动的,则`MyProperties.list`包含一个`MyPojo`条目,如前面定义的那样。但是,如果启用了`dev`配置文件,则`list`*Still*只包含一个条目(名称为`my another name`,描述为`null`)。此配置*不是*将第二个`MyPojo`实例添加到列表中,并且它不合并项。 + +当在多个配置文件中指定`List`时,将使用优先级最高的那个配置文件。考虑以下示例: + +Properties + +``` +my.list[0].name=my name +my.list[0].description=my description +my.list[1].name=another name +my.list[1].description=another description +#--- +spring.config.activate.on-profile=dev +my.list[0].name=my another name +``` + +Yaml + +``` +my: + list: + - name: "my name" + description: "my description" + - name: "another name" + description: "another description" +--- +spring: + config: + activate: + on-profile: "dev" +my: + list: + - name: "my another name" +``` + +在前面的示例中,如果`dev`配置文件是活动的,则`MyProperties.list`包含*One*`MyPojo`条目(名称为`my another name`,描述为`null`)。对于 YAML,逗号分隔的列表和 YAML 列表都可以用于完全覆盖列表的内容。 + +对于`Map`属性,你可以绑定来自多个源的属性值。但是,对于多个源中的相同属性,使用具有最高优先级的属性。下面的示例公开了来自`MyProperties`的`Map`: + +``` +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("my") +public class MyProperties { + + private final Map map = new LinkedHashMap<>(); + + public Map getMap() { + return this.map; + } + +} + +``` + +考虑以下配置: + +Properties + +``` +my.map.key1.name=my name 1 +my.map.key1.description=my description 1 +#--- +spring.config.activate.on-profile=dev +my.map.key1.name=dev name 1 +my.map.key2.name=dev name 2 +my.map.key2.description=dev description 2 +``` + +Yaml + +``` +my: + map: + key1: + name: "my name 1" + description: "my description 1" +--- +spring: + config: + activate: + on-profile: "dev" +my: + map: + key1: + name: "dev name 1" + key2: + name: "dev name 2" + description: "dev description 2" +``` + +如果`dev`配置文件不是活动的,`MyProperties.map`包含一个键值`key1`的条目(名称为`my name 1`,描述为`my description 1`)。但是,如果启用了`dev`配置文件,则`map`包含两个带有键`key1`的条目(名称为`dev name 1`,描述为`my description 1`)和`key2`(名称为`dev name 2`,描述为`dev description 2`)。 + +| |前面的合并规则适用于来自所有属性源的属性,而不仅仅是文件。| +|---|----------------------------------------------------------------------------------------------| + +#### 2.8.8.属性转换 + +Spring 当绑定到`@ConfigurationProperties`bean 时,引导尝试强制将外部应用程序属性转换为正确的类型。如果需要自定义类型转换,可以提供`ConversionService` Bean(带有 Bean 名为`conversionService`)或自定义属性编辑器(通过`CustomEditorConfigurer` Bean)或自定义`Converters`(带有 Bean 注释为`@ConfigurationPropertiesBinding`的定义)。 + +| |由于在应用程序生命周期中很早就请求了这个 Bean,因此请确保限制你的`ConversionService`正在使用的依赖关系。
通常,你所需要的任何依赖项在创建时可能不会完全初始化。
如果配置键强制不需要它,并且仅依赖用`@ConfigurationPropertiesBinding`限定的自定义转换器,那么你可能想要重命名自定义`ConversionService`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### 转换持续时间 + +Spring Boot 具有用于表示持续时间的专用支持。如果你公开了`java.time.Duration`属性,那么应用程序属性中的以下格式是可用的: + +* 一个常规的`long`表示(使用毫秒作为默认单位,除非指定了`@DurationUnit`) + +* 标准的 ISO-8601 格式[used by `java.time.Duration`](https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-) + +* 一种更易读的格式,其中的值和单位是耦合的(`10s’表示 10 秒) + +考虑以下示例: + +``` +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DurationUnit; + +@ConfigurationProperties("my") +public class MyProperties { + + @DurationUnit(ChronoUnit.SECONDS) + private Duration sessionTimeout = Duration.ofSeconds(30); + + private Duration readTimeout = Duration.ofMillis(1000); + + // getters / setters... + + public Duration getSessionTimeout() { + return this.sessionTimeout; + } + + public void setSessionTimeout(Duration sessionTimeout) { + this.sessionTimeout = sessionTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + + public void setReadTimeout(Duration readTimeout) { + this.readTimeout = readTimeout; + } + +} + +``` + +要指定 30 秒的会话超时,`30`、`PT30S`和`30s`都是等效的。可以以以下任何一种形式指定 500ms 的读取超时:`500`,`PT0.5S`和`500ms`。 + +你也可以使用任何支持的单位。这些是: + +* `ns`表示纳秒 + +* `us`为微秒 + +* `ms`毫秒 + +* `s`秒 + +* `m`分钟 + +* `h`小时 + +* `d`天数 + +默认的单位是毫秒,可以使用`@DurationUnit`重写,如上面的示例所示。 + +如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示: + +``` +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.boot.context.properties.bind.DefaultValue; +import org.springframework.boot.convert.DurationUnit; + +@ConfigurationProperties("my") +@ConstructorBinding +public class MyProperties { + + // fields... + + private final Duration sessionTimeout; + + private final Duration readTimeout; + + public MyProperties(@DurationUnit(ChronoUnit.SECONDS) @DefaultValue("30s") Duration sessionTimeout, + @DefaultValue("1000ms") Duration readTimeout) { + this.sessionTimeout = sessionTimeout; + this.readTimeout = readTimeout; + } + + // getters... + + public Duration getSessionTimeout() { + return this.sessionTimeout; + } + + public Duration getReadTimeout() { + return this.readTimeout; + } + +} + +``` + +| |如果你正在升级`Long`属性,请确保在不是毫秒的情况下定义该单元(使用`@DurationUnit`)。
这样做可以提供一个透明的升级路径,同时支持更丰富的格式。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### 转换周期 + +除了持续时间, Spring 引导还可以与`java.time.Period`类型一起工作。在应用程序属性中可以使用以下格式: + +* 一个常规的`int`表示(使用天数作为默认单位,除非指定了`@PeriodUnit`) + +* 标准的 ISO-8601 格式[used by `java.time.Period`](https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-) + +* 一种更简单的格式,其中的值和单位对是耦合的(`1y3d’表示 1 年零 3 天) + +以下单元以简单格式提供支持: + +* `y`多年 + +* `m`持续数月 + +* `w`数周 + +* `d`天 + +| |`java.time.Period`类型实际上不会存储周数,它是一种表示“7 天”的快捷方式。| +|---|------------------------------------------------------------------------------------------------------------| + +##### 转换数据大小 + +Spring 框架具有`DataSize`值类型,该类型表示以字节为单位的大小。如果你公开了`DataSize`属性,那么应用程序属性中的以下格式是可用的: + +* 一个常规的`long`表示(使用字节作为默认单位,除非指定了`@DataSizeUnit`) + +* 一种更易读的格式,其中的值和单位是耦合的(“10MB”表示 10MB) + +考虑以下示例: + +``` +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.convert.DataSizeUnit; +import org.springframework.util.unit.DataSize; +import org.springframework.util.unit.DataUnit; + +@ConfigurationProperties("my") +public class MyProperties { + + @DataSizeUnit(DataUnit.MEGABYTES) + private DataSize bufferSize = DataSize.ofMegabytes(2); + + private DataSize sizeThreshold = DataSize.ofBytes(512); + + // getters/setters... + + public DataSize getBufferSize() { + return this.bufferSize; + } + + public void setBufferSize(DataSize bufferSize) { + this.bufferSize = bufferSize; + } + + public DataSize getSizeThreshold() { + return this.sizeThreshold; + } + + public void setSizeThreshold(DataSize sizeThreshold) { + this.sizeThreshold = sizeThreshold; + } + +} + +``` + +要指定 10MB 的缓冲区大小,`10`和`10MB`是等效的。256 字节的大小阈值可以指定为`256`或`256B`。 + +你也可以使用任何支持的单位。这些是: + +* `B`表示字节 + +* `KB`表示千字节 + +* `MB`代表兆字节 + +* GB=“932”/> + +* `TB`用于 TB + +默认的单元是字节,可以使用`@DataSizeUnit`重写,如上面的示例所示。 + +如果你更喜欢使用构造函数绑定,则可以公开相同的属性,如下例所示: + +``` +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.boot.context.properties.bind.DefaultValue; +import org.springframework.boot.convert.DataSizeUnit; +import org.springframework.util.unit.DataSize; +import org.springframework.util.unit.DataUnit; + +@ConfigurationProperties("my") +@ConstructorBinding +public class MyProperties { + + // fields... + + private final DataSize bufferSize; + + private final DataSize sizeThreshold; + + public MyProperties(@DataSizeUnit(DataUnit.MEGABYTES) @DefaultValue("2MB") DataSize bufferSize, + @DefaultValue("512B") DataSize sizeThreshold) { + this.bufferSize = bufferSize; + this.sizeThreshold = sizeThreshold; + } + + // getters... + + public DataSize getBufferSize() { + return this.bufferSize; + } + + public DataSize getSizeThreshold() { + return this.sizeThreshold; + } + +} + +``` + +| |如果你正在升级`Long`属性,请确保定义不是字节的单元(使用`@DataSizeUnit`)。
这样做可以提供一个透明的升级路径,同时支持更丰富的格式。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.9.@configrationProperties 验证 + +Spring 每当使用 Spring 的`@Validated`注释对类进行注释时,引导都会尝试验证`@ConfigurationProperties`类。你可以直接在你的配置类上使用 JSR-303`javax.validation`约束注释。要做到这一点,请确保在你的 Classpath 上有一个兼容的 JSR-303 实现,然后将约束注释添加到你的字段中,如以下示例所示: + +``` +import java.net.InetAddress; + +import javax.validation.constraints.NotNull; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties("my.service") +@Validated +public class MyProperties { + + @NotNull + private InetAddress remoteAddress; + + // getters/setters... + + public InetAddress getRemoteAddress() { + return this.remoteAddress; + } + + public void setRemoteAddress(InetAddress remoteAddress) { + this.remoteAddress = remoteAddress; + } + +} + +``` + +| |你还可以通过注释`@Bean`方法来触发验证,该方法使用`@Validated`创建配置属性。| +|---|-----------------------------------------------------------------------------------------------------------------------------| + +要确保始终为嵌套属性触发验证,即使没有找到属性,也必须用`@Valid`注释关联字段。下面的示例以前面的`MyProperties`示例为基础: + +``` +import java.net.InetAddress; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties("my.service") +@Validated +public class MyProperties { + + @NotNull + private InetAddress remoteAddress; + + @Valid + private final Security security = new Security(); + + // getters/setters... + + public InetAddress getRemoteAddress() { + return this.remoteAddress; + } + + public void setRemoteAddress(InetAddress remoteAddress) { + this.remoteAddress = remoteAddress; + } + + public Security getSecurity() { + return this.security; + } + + public static class Security { + + @NotEmpty + private String username; + + // getters/setters... + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + } + +} + +``` + +还可以通过创建一个名为`configurationPropertiesValidator`的 Bean 定义来添加自定义 Spring `Validator`。应该声明`@Bean`方法`static`。配置属性验证器是在应用程序生命周期的很早阶段创建的,将`@Bean`方法声明为静态,可以在不需要实例化`@Configuration`类的情况下创建 Bean。这样做可以避免早期实例化可能带来的任何问题。 + +| |`spring-boot-actuator`模块包括一个公开所有`@ConfigurationProperties`bean 的端点。
将你的 Web 浏览器指向`/actuator/configprops`或使用等效的 JMX 端点。
有关详细信息,请参见“[生产就绪功能](actuator.html#actuator.endpoints)一节。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.8.10.@configrationProperties vs.@value + +`@Value`注释是一个核心容器特性,它不提供与类型安全配置属性相同的特性。下表总结了`@Value`和`@Value`所支持的功能: + +| Feature |`@ConfigurationProperties`|`@Value`| +|----------------------------------------------------------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------| +|[Relaxed binding](#features.external-config.typesafe-configuration-properties.relaxed-binding)| Yes |Limited(见[note below](#features.external-config.typesafe-configuration-properties.vs-value-annotation.note))| +| [Meta-data support](configuration-metadata.html#appendix.configuration-metadata) | Yes |无| +| `SpEL` evaluation | No |是的| + +| |如果你确实想使用`@Value`,我们建议你使用它们的规范形式(kebab-case 只使用小写字母)来引用属性名称。
这将允许 Spring 引导使用与放松绑定`@ConfigurationProperties`时相同的逻辑,例如,
,`@Value("{demo.item-price}")`将从`application.properties`文件中获取`demo.item-price`和`demo.itemPrice`窗体,以及从系统环境中获取`DEMO_ITEMPRICE`。
如果使用`@Value("{demo.itemPrice}")`代替,则不会考虑`demo.item-price`和`DEMO_ITEMPRICE`。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你为自己的组件定义了一组配置键,那么我们建议你将它们分组到一个用`@ConfigurationProperties`注释的 POJO 中。这样做将为你提供结构化的、类型安全的对象,你可以将其注入到自己的 bean 中。 + +`SpEL`来自[应用程序属性文件](#features.external-config.files)的表达式在解析这些文件和填充环境时不会被处理。但是,可以在`@Value`中写入`SpEL`表达式。如果应用程序属性文件中的属性的值是`SpEL`表达式,则在通过`@Value`使用时将对其进行求值。 + +## 3. 配置文件 + + +Spring 配置文件提供了一种方法来隔离应用程序配置的部分,并使其仅在某些环境中可用。任何`@Component`、`@Configuration`或`@ConfigurationProperties`都可以用`@Profile`标记,以在加载时进行限制,如以下示例所示: + +``` +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration(proxyBeanMethods = false) +@Profile("production") +public class ProductionConfiguration { + + // ... + +} + +``` + +| |如果`@Configuration属性`bean 是通过`@EnableConfiguration属性`而不是自动扫描来注册的,则需要在具有`@Configuration`注释的`@Configuration`类上指定
注释。
在`@Configuration属性`被扫描的情况下,`@Profile`可以在`@Configuration属性`类本身上指定。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你可以使用`spring.profiles.active``Environment`属性来指定哪些配置文件是活动的。你可以用本章前面描述的任何一种方式指定属性。例如,你可以将它包含在`application.properties`中,如以下示例所示: + +属性 + +``` +spring.profiles.active=dev,hsqldb +``` + +Yaml + +``` +spring: + profiles: + active: "dev,hsqldb" +``` + +你还可以通过使用以下开关在命令行中指定它:`--spring.profiles.active=dev,hsqldb`。 + +如果没有配置文件处于活动状态,则启用默认配置文件。默认配置文件的名称是`default`,可以使用`spring.profiles.default``Environment`属性对其进行调优,如下例所示: + +属性 + +``` +spring.profiles.default=none +``` + +Yaml + +``` +spring: + profiles: + default: "none" +``` + +### 3.1.添加活动配置文件 + +`spring.profiles.active`属性遵循与其他属性相同的排序规则:最高的`PropertySource`属性获胜。这意味着你可以使用命令行开关在`application.properties`中指定活动配置文件,然后在**替换**中指定它们。 + +有时,将**添加**的属性用于活动配置文件而不是替换它们是很有用的。`spring.profiles.active`入口点有一个用于设置附加配置文件的 Java API(即,在那些由`spring.profiles.active`属性激活的配置文件之上)。参见[SpringApplication](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/SpringApplication.html)中的`setAdditionalProfiles()`方法。在[next section](#features.profiles.groups)中描述的配置文件组也可以用于添加活动配置文件,如果给定的配置文件是活动的。 + +### 3.2.配置文件组 + +有时,你在应用程序中定义和使用的配置文件粒度太细,使用起来很麻烦。例如,你可能有`proddb`和`prodmq`配置文件,用于独立启用数据库和消息传递功能。 + +为了帮助实现这一点, Spring Boot 允许你定义配置文件组。配置文件组允许你为相关的配置文件组定义逻辑名称。 + +例如,我们可以创建一个`production`组,该组由我们的`proddb`和`prodmq`配置文件组成。 + +属性 + +``` +spring.profiles.group.production[0]=proddb +spring.profiles.group.production[1]=prodmq +``` + +Yaml + +``` +spring: + profiles: + group: + production: + - "proddb" + - "prodmq" +``` + +我们的应用程序现在可以使用`--spring.profiles.active=production`来启动`production`、`proddb`和`prodmq`配置文件。 + +### 3.3.以编程方式设置配置文件 + +你可以通过在应用程序运行之前调用`SpringApplication.setAdditionalProfiles(…​)`以编程方式设置活动配置文件。也可以通过使用 Spring 的`ConfigurableEnvironment`接口激活配置文件。 + +### 3.4.特定于配置文件的配置文件 + +`application.properties`(或`application.yml`)和通过`@Configuration属性`引用的文件的配置文件特定变体都被视为文件并加载。详见“[配置文件特定的文件](#features.external-config.files.profile-specific)”。 + +## 4. 伐木 + +Spring 启动对所有内部日志使用[Commons Logging](https://commons.apache.org/logging),但对底层日志实现保持开放。为[Java util 日志记录](https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html)、[Log4J2](https://logging.apache.org/log4j/2.x/)和[Logback](https://logback.qos.ch/)提供了默认配置。在每种情况下,记录器都被预先配置为使用控制台输出,可选的文件输出也可用。 + +默认情况下,如果你使用“starters”,则会使用回录进行日志记录。还包括适当的回登路由,以确保使用 Java util 日志、Commons 日志、log4j 或 SLF4j 的依赖库都能正确工作。 + +| |有很多日志框架可用于 Java。
如果上面的列表看起来令人困惑,请不要担心。
通常,你不需要更改日志依赖项,并且 Spring 引导默认值工作得很好。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |当你将应用程序部署到 Servlet 容器或应用程序服务器时,使用 Java util Logging API 执行的日志记录不会路由到应用程序的日志中。
这将防止由容器执行的日志或已部署到它的其他应用程序出现在应用程序的日志中。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.1.日志格式 + +Spring 引导的默认日志输出类似于以下示例: + +``` +2019-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.52 +2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext +2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms +2019-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] +2019-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] +``` + +以下项目是输出: + +* 日期和时间:毫秒精度和容易排序. + +* 日志级别:
,`WARN`,`INFO`,`DEBUG`,或`TRACE`。 + +* 进程 ID。 + +* 一个`---`分隔符,用于区分实际日志消息的开始。 + +* 线程名称:括在方括号中(对于控制台输出,可能会被截断)。 + +* 日志程序名称:这通常是源类名称(通常是缩写)。 + +* 日志消息。 + +| |注销没有`FATAL`级别。
它被映射到`production`。| +|---|-------------------------------------------------------------------| + +### 4.2.控制台输出 + +默认的日志配置会在消息写入时将消息回传到控制台。默认情况下,将记录`ERROR`-level、`WARN`-level 和`INFO`-level 消息。你还可以通过使用`--debug`标志启动应用程序来启用“调试”模式。 + +``` +$ java -jar myapp.jar --debug +``` + +| |你还可以在`application.properties`中指定`debug=true`。| +|---|-------------------------------------------------------------------| + +当调试模式被启用时,核心记录器(嵌入式容器、 Hibernate 和 Spring 引导)的选择被配置为输出更多信息。启用调试模式会*不是*配置你的应用程序以`DEBUG`级别记录所有消息。 + +或者,你可以通过使用`--trace`标志(或`trace=true`中的`application.properties`)启动应用程序来启用“跟踪”模式。这样做可以实现对核心记录器(嵌入式容器、 Hibernate 模式生成和整个 Spring 投资组合)的选择的跟踪日志记录。 + +#### 4.2.1.颜色编码输出 + +如果你的终端支持 ANSI,则使用颜色输出来提高可读性。可以将`spring.output.ansi.enabled`设置为[supported value](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/ansi/AnsiOutput.Enabled.html)以覆盖自动检测。 + +颜色编码是通过使用`%clr`转换字来配置的。在最简单的形式中,转换器根据日志级别对输出进行着色,如以下示例所示: + +``` +%clr(%5p) +``` + +下表描述了日志级别到颜色的映射: + +|电平|Color | +|-------|------| +|`FATAL`| Red | +|`ERROR`| Red | +|`WARN`|Yellow| +|`INFO`|Green | +|`DEBUG`|Green | +|`TRACE`|Green | + +或者,你可以通过将其作为转换的选项来指定应该使用的颜色或样式。例如,要使文本为黄色,请使用以下设置: + +``` +%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow} +``` + +支持以下颜色和样式: + +* `blue` + +* `cyan` + +* `faint` + +* `green` + +* `magenta` + +* `red` + +* `yellow` + +### 4.3.文件输出 + +默认情况下, Spring 只引导日志到控制台,不写日志文件。如果要在控制台输出之外写日志文件,则需要设置`logging.file.name`或`logging.file.path`属性(例如,在`application.properties`中)。 + +下表显示了如何一起使用`logging.*`属性: + +|`logging.file.name`|`logging.file.path`| Example |说明| +|-------------------|-------------------|----------|------------------------------------------------------------------------------------------------------------------------| +| *(none)* | *(none)* | |仅用于控制台日志记录。| +| Specific file | *(none)* | `my.log` |写入指定的日志文件。
名称可以是确切的位置或相对于当前目录的位置。| +| *(none)* |Specific directory |`/var/log`|将`spring.log`写入指定的目录。
名称可以是确切的位置或相对于当前目录的位置。| + +当日志文件达到 10MB 时会旋转,并且与控制台输出一样,`ERROR`-level,`WARN`-level 和`INFO`-level 消息在默认情况下会被记录。 + +| |日志记录属性独立于实际的日志基础设施。因此,特定的配置键(例如用于日志恢复的`logback.configurationFile`)不受 Spring 引导的管理。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.4.文件旋转 + +如果你使用的是回录,那么可以使用`application.properties`或`application.yaml`文件来微调日志旋转设置。对于所有其他日志系统,你将需要直接自己配置旋转设置(例如,如果你使用 log4j2,那么你可以添加`application.properties`或`log4j2-spring.xml`文件)。 + +支持以下旋转策略属性: + +| Name |说明| +|------------------------------------------------------|----------------------------------------------------------------------| +| `logging.logback.rollingpolicy.file-name-pattern` |用于创建日志归档的文件名模式。| +|`logging.logback.rollingpolicy.clean-history-on-start`|如果日志归档清理应该在应用程序启动时进行。| +| `logging.logback.rollingpolicy.max-file-size` |存档前日志文件的最大大小。| +| `logging.logback.rollingpolicy.total-size-cap` |在被删除之前,日志档案可以占用的最大大小。| +| `logging.logback.rollingpolicy.max-history` |保存的归档日志文件的最大数量(默认为 7)。| + +### 4.5.日志级别 + +通过使用`application.properties`,所有受支持的日志系统都可以在 Spring `Environment`(例如,在`application.properties`)中设置日志程序级别,其中`level`是跟踪、调试、信息、警告、错误、致命或关闭的级别之一。可以使用`logging.level.root`配置`root`记录器。 + +下面的示例在`application.properties`中显示了潜在的日志设置: + +属性 + +``` +logging.level.root=warn +logging.level.org.springframework.web=debug +logging.level.org.hibernate=error +``` + +Yaml + +``` +logging: + level: + root: "warn" + org.springframework.web: "debug" + org.hibernate: "error" +``` + +也可以使用环境变量设置日志记录级别。例如,`logging.level.root`将`org.springframework.web`设置为`DEBUG`。 + +| |上述方法仅适用于包级别的日志记录。
由于放松绑定总是将环境变量转换为小写,因此不可能以这种方式为单个类配置日志记录。
如果需要为一个类配置日志记录,可以使用[the `SPRING_APPLICATION_JSON`](#features.external-config.application-json)变量。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.6.日志组 + +能够将相关的记录器组合在一起,以便可以同时对它们进行配置,这通常是很有用的。例如,你通常可能会更改*全部* Tomcat 相关日志记录器的日志记录级别,但是你不能很容易地记住顶级包。 + +为了解决这个问题, Spring Boot 允许你在 Spring `Environment`中定义日志记录组。例如,下面是如何将“ Tomcat”组添加到`application.properties`中来定义该组的方法: + +属性 + +``` +logging.group.tomcat=org.apache.catalina,org.apache.coyote,org.apache.tomcat +``` + +Yaml + +``` +logging: + group: + tomcat: "org.apache.catalina,org.apache.coyote,org.apache.tomcat" +``` + +一旦定义好,你就可以用一行来改变组中所有记录器的级别: + +Properties + +``` +logging.level.tomcat=trace +``` + +Yaml + +``` +logging: + level: + tomcat: "trace" +``` + +Spring 启动包括以下预定义的记录组,这些记录组可以开箱即用: + +|Name|伐木工| +|----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|web |`org.springframework.core.codec`, `org.springframework.http`, `org.springframework.web`, `org.springframework.boot.actuate.endpoint.web`, `org.springframework.boot.web.servlet.ServletContextInitializerBeans`| +|sql |`org.springframework.jdbc.core`, `org.hibernate.SQL`, `org.jooq.tools.LoggerListener`| + +### 4.7.使用日志关闭钩子 + +为了在应用程序终止时释放日志资源,提供了一个关闭钩子,该钩子将在 JVM 退出时触发日志系统清理。除非你的应用程序被部署为 WAR 文件,否则此关闭钩子将自动注册。如果你的应用程序具有复杂的上下文层次结构,那么关闭钩子可能无法满足你的需求。如果没有,请禁用关闭钩子,并调查底层日志系统直接提供的选项。例如,Logback 提供[上下文选择器](http://logback.qos.ch/manual/loggingSeparation.html),它允许在自己的上下文中创建每个记录器。你可以使用`logging.register-shutdown-hook`属性禁用关闭钩子。将其设置为`false`将禁用注册。你可以在`application.properties`或`application.yaml`文件中设置该属性: + +Properties + +``` +logging.register-shutdown-hook=false +``` + +Yaml + +``` +logging: + register-shutdown-hook: false +``` + +### 4.8.自定义日志配置 + +各种日志记录系统可以通过在 Classpath 上包括适当的库来激活,并且可以通过在 Classpath 的根目录中或在由下面的 Spring 指定的位置中提供适当的配置文件来进一步定制属性:。 + +可以使用`org.springframework.boot.logging.LoggingSystem`系统属性强制 Spring 引导使用特定的日志系统。该值应该是`LoggingSystem`实现的完全限定类名称。你还可以使用`none`的值来完全禁用 Spring Boot 的日志配置。 + +| |由于日志是初始化的**在此之前**已创建`ApplicationContext`,因此不可能在 Spring `@PropertySources`文件中控制来自`@PropertySources`的日志记录。
改变日志系统或完全禁用它的唯一方法是通过系统属性。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +根据你的日志记录系统,将加载以下文件: + +| Logging System |定制| +|-----------------------|---------------------------------------------------------------------------------| +| Logback |`logback-spring.xml`,`logback-spring.groovy`,`logback-spring.groovy`,或`logback.groovy`| +| Log4j2 |`log4j2-spring.xml`或`log4j2.xml`| +|JDK (Java Util Logging)|`logging.properties`| + +| |如果可能,我们建议你在日志配置中使用`-spring`变量(例如,`logback-spring.xml`而不是`logback.xml`)。
如果使用标准配置位置, Spring 不能完全控制日志初始化。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在从“可执行文件 jar”运行时,Java util 日志记录中存在已知的类加载问题,这些问题会导致问题。
如果可能的话,我们建议你在从“可执行文件 jar”运行时避免这种情况。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +为了帮助进行定制,将一些其他属性从 Spring `Environment`转移到系统属性,如下表所述: + +| Spring Environment | System Property |评论| +|-----------------------------------|-------------------------------|-----------------------------------------------------------------------------------------------------------| +|`logging.exception-conversion-word`|`LOG_EXCEPTION_CONVERSION_WORD`|记录异常时使用的转换词.| +| `logging.file.name` | `LOG_FILE` |如果定义了,它将在默认的日志配置中使用。| +| `logging.file.path` | `LOG_PATH` |如果定义了,它将在默认的日志配置中使用。| +| `logging.pattern.console` | `CONSOLE_LOG_PATTERN` |在控制台上使用的日志模式。| +| `logging.pattern.dateformat` | `LOG_DATEFORMAT_PATTERN` |日志日期格式的附录模式。| +| `logging.charset.console` | `CONSOLE_LOG_CHARSET` |用于控制台日志记录的字符集。| +| `logging.pattern.file` | `FILE_LOG_PATTERN` |在文件中使用的日志模式(如果启用`LOG_FILE`)。| +| `logging.charset.file` | `FILE_LOG_CHARSET` |用于文件日志记录的字符集(如果启用`LOG_FILE`)。| +| `logging.pattern.level` | `LOG_LEVEL_PATTERN` |呈现日志级别时要使用的格式(默认`%5p`)。| +| `PID` | `PID` |当前的进程 ID(如果可能的话,在尚未定义为 OS 环境变量时发现)。| + +如果使用注销,还会传输以下属性: + +| Spring Environment | System Property |评论| +|------------------------------------------------------|----------------------------------------------|------------------------------------------------------------------------------------| +| `logging.logback.rollingpolicy.file-name-pattern` | `LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN` |滚转日志文件名的模式(默认`${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz`)。| +|`logging.logback.rollingpolicy.clean-history-on-start`|`LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START`|是否在启动时清除归档日志文件。| +| `logging.logback.rollingpolicy.max-file-size` | `LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE` |最大日志文件大小。| +| `logging.logback.rollingpolicy.total-size-cap` | `LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP` |要保留的日志备份的总大小。| +| `logging.logback.rollingpolicy.max-history` | `LOGBACK_ROLLINGPOLICY_MAX_HISTORY` |要保存的归档日志文件的最大数量。| + +所有受支持的日志记录系统在解析其配置文件时都可以参考系统属性。有关示例,请参见`spring-boot.jar`中的默认配置: + +* [Logback](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml) + +* [Log4j 2](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml) + +* [Java util 日志记录](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/resources/org/springframework/boot/logging/java/logging-file.properties) + +| |如果希望在日志属性中使用占位符,则应该使用[Spring Boot’s syntax](#features.external-config.files.property-placeholders),而不是底层框架的语法。
特别是,如果使用 logback,则应该使用`:`作为属性名与其默认值之间的分隔符,而不是使用`:-`。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |你可以通过只重写`LOG_LEVEL_PATTERN`(或`logging.pattern.level`带回日志)将 MDC 和其他临时内容添加到日志行中,
例如,如果你使用`logging.pattern.level=user:%X{user} %5p`,那么默认的日志格式包含一个“user”的 MDC 条目,如果它存在的话,如下例所示。

``
2019-08-3012:30:04.031 用户:someone info22174---[蔚来-8080-exec-0]demo.controller
处理经过验证的请求
```| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.9.回录扩展 + +Spring 启动包括许多对注销的扩展,这些扩展可以帮助进行高级配置。你可以在`logback-spring.xml`配置文件中使用这些扩展名。 + +| |因为标准的`logback.xml`配置文件加载得太早,所以不能在其中使用扩展名。
你需要使用`logback-spring.xml`或者定义`logging.config`属性。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |这些扩展名不能与 Logback 的[配置扫描](https://logback.qos.ch/manual/configuration.html#autoScan)一起使用。
如果你试图这样做,对配置文件进行更改将导致类似于记录以下错误之一的错误:| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +``` +ERROR in [email protected]:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] +ERROR in ch.qos.logback.core.joran.spi.Interpret[email protected]:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]] +``` + +#### 4.9.1.特定于配置文件的配置 + +``标记允许你根据活动 Spring 配置文件可选地包括或排除配置的部分。在``元素中的任何地方都支持配置文件部分。使用`name`属性指定哪个配置文件接受配置。``标记可以包含配置文件名(例如`staging`)或配置文件表达式。配置文件表达式允许表达更复杂的配置文件逻辑,例如`production & (eu-central | eu-west)`。有关更多详细信息,请查看[reference guide](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/core.html#beans-definition-profiles-java)。下面的清单显示了三个示例配置文件: + +``` + + + + + + + + + + + +``` + +#### 4.9.2.环境属性 + +``标记允许你公开 Spring `Environment`中的属性,以便在回传过程中使用。如果你希望在你的回登配置中访问`application.properties`文件中的值,那么这样做会很有用。该标记的工作方式与 Logback 的标准``标记类似。但是,不是直接指定`value`,而是指定属性的`source`(来自`Environment`)。如果需要将属性存储在`local`范围以外的其他地方,则可以使用`scope`属性。如果你需要一个回退值(如果该属性未在`Environment`中设置),则可以使用`defaultValue`属性。下面的示例展示了如何公开属性以便在回登过程中使用: + +``` + + + ${fluentHost} + ... + +``` + +| |`source`必须在烤肉串的情况下指定(例如`my.property-name`)。
但是,可以通过使用放松的规则将属性添加到`Environment`中。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 5. 国际化 + +Spring 启动支持本地化消息,以便你的应用程序能够迎合具有不同语言偏好的用户。默认情况下, Spring 引导在 Classpath 的根位置查找`messages`资源包的存在。 + +| |当配置的资源包的默认属性文件可用时,将应用自动配置。
如果你的资源包只包含特定于语言的属性文件,则需要添加默认的属性文件。`MessageSource`如果没有找到匹配任何配置的基名的属性文件,不会自动配置`MessageSource`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +可以使用`spring.messages`命名空间来配置资源包的 basename 以及其他几个属性,如下例所示: + +Properties + +``` +spring.messages.basename=messages,config.i18n.messages +spring.messages.fallback-to-system-locale=false +``` + +Yaml + +``` +spring: + messages: + basename: "messages,config.i18n.messages" + fallback-to-system-locale: false +``` + +| |`spring.messages.basename`支持以逗号分隔的位置列表,可以是包限定符,也可以是从 Classpath 根目录解析的资源。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------| + +有关更多支持的选项,请参见`MessageSource`。 + +## 6. JSON + +Spring Boot 提供了与三个 JSON 映射库的集成: + +* 格森 + +* Jackson + +* JSON-B + +Jackson 是首选的默认库。 + +### 6.1.Jackson + +提供了用于 Jackson 的自动配置,并且 Jackson 是`spring-boot-starter-json`的一部分。当 Jackson 在 Classpath 上时,`ObjectMapper` Bean 被自动配置。为[customizing the configuration of the `ObjectMapper`](howto.html#howto.spring-mvc.customize-jackson-objectmapper)提供了几个配置属性。 + +### 6.2.格森 + +提供了 GSON 的自动配置。当 GSON 在 Classpath 上时,`Gson` Bean 被自动配置。为定制配置提供了几个`spring.gson.*`配置属性。要获得更多的控制,可以使用一个或多个`GsonBuilderCustomizer`bean。 + +### 6.3.JSON-B + +提供了 JSON-B 的自动配置。当 JSON-B API 和实现都在 Classpath 上时,`Jsonb` Bean 将被自动配置。首选的 JSON-B 实现是 Apache Johnzon,它提供了依赖管理。 + +## 7. 任务执行和调度 + +在上下文中没有`Executor` Bean 的情况下, Spring 引导自动配置带有合理默认值的`ThreadPoolTaskExecutor`,该默认值可以自动关联到异步任务执行和 Spring MVC 异步请求处理。 + +| |如果你在上下文中定义了自定义的`Executor`,则常规的任务执行(即`@EnableAsync`)将透明地使用它,但是 Spring MVC 支持将不会被配置,因为它需要`applicationTaskExecutor`实现(名为`applicationTaskExecutor`)。
取决于你的目标安排,你可以将你的`Executor`更改为`ThreadPoolTaskExecutor`,或者同时定义`ThreadPoolTaskExecutor`和`AsyncConfigurer`来包装你的自定义`Executor`。
自动配置的
允许你轻松地创建实例,这些实例可以复制默认情况下自动配置的功能。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +线程池使用 8 个核心线程,这些线程可以根据负载的大小进行增长和收缩。可以使用`spring.task.execution`名称空间对这些默认设置进行微调,如下例所示: + +Properties + +``` +spring.task.execution.pool.max-size=16 +spring.task.execution.pool.queue-capacity=100 +spring.task.execution.pool.keep-alive=10s +``` + +Yaml + +``` +spring: + task: + execution: + pool: + max-size: 16 + queue-capacity: 100 + keep-alive: "10s" +``` + +这将线程池更改为使用有界队列,以便当队列已满(100 个任务)时,线程池最多增加到 16 个线程。当线程空闲 10 秒(而不是默认的 60 秒)时,会回收线程,因此池的收缩会更剧烈。 + +如果需要与计划的任务执行相关联,`ThreadPoolTaskScheduler`也可以自动配置(例如使用`ThreadPoolTaskExecutor`)。线程池默认使用一个线程,其设置可以使用`spring.task.scheduling`名称空间进行微调,如下例所示: + +Properties + +``` +spring.task.scheduling.thread-name-prefix=scheduling- +spring.task.scheduling.pool.size=2 +``` + +Yaml + +``` +spring: + task: + scheduling: + thread-name-prefix: "scheduling-" + pool: + size: 2 +``` + +如果需要创建自定义执行器或调度程序,则在上下文中可以使用`TaskExecutorBuilder` Bean 和`TaskSchedulerBuilder` Bean。 + +## 8. 测试 + +Spring 引导提供了许多实用工具和注释,以在测试应用程序时提供帮助。测试支持由两个模块提供:`spring-boot-test`包含核心项,`spring-boot-test-autoconfigure`支持测试的自动配置。 + +大多数开发人员使用`spring-boot-starter-test`“starter”,它既导入 Spring 引导测试模块,也导入 JUnit Jupiter、AssertJ、Hamcrest 和许多其他有用的库。 + +| |如果你有使用 JUnit4 的测试,可以使用 JUnit5 的 Vintage 引擎来运行它们,
要使用 Vintage 引擎,请添加对`junit-vintage-engine`的依赖项,如下例所示:

```TaskExecutorBuilder`


Junit-vintage-engine<1239"/>test=“1241”/>><1240">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +`hamcrest-core`被排除在外,而`org.hamcrest:hamcrest`是`spring-boot-starter-test`的一部分。 + +### 8.1.测试范围依赖项 + +`spring-boot-starter-test`“starter”(在`test``scope`中)包含以下提供的库: + +* [JUnit 5](https://junit.org/junit5/):用于单元测试 Java 应用程序的事实标准。 + +* [Spring Test](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#integration-testing)& Spring 引导测试: Spring 引导应用程序的实用工具和集成测试支持。 + +* [AssertJ](https://assertj.github.io/doc/):一个流畅的断言程序库。 + +* [Hamcrest](https://github.com/hamcrest/JavaHamcrest):匹配器对象(也称为约束或谓词)的库。 + +* [Mockito](https://site.mockito.org/):一个 Java 模拟框架。 + +* [JSONassert](https://github.com/skyscreamer/JSONassert):JSON 的断言程序库。 + +* [JsonPath](https://github.com/jayway/JsonPath):XPath for JSON。 + +在编写测试时,我们通常会发现这些公共库非常有用。如果这些库不适合你的需求,那么你可以添加自己的额外测试依赖项。 + +### 8.2.测试 Spring 应用程序 + +依赖注入的主要优点之一是它应该使代码更容易进行单元测试。你可以使用`new`运算符实例化对象,甚至不需要涉及 Spring。你也可以使用*模拟对象*来代替真正的依赖关系。 + +通常,你需要超越单元测试,开始集成测试(使用 Spring `ApplicationContext`)。能够在不需要部署应用程序或不需要连接到其他基础设施的情况下执行集成测试是非常有用的。 + +Spring 框架包括用于这种集成测试的专用测试模块。你可以直接将依赖项声明为`org.springframework:spring-test`,或者使用`spring-boot-starter-test`“starter”来传递地将其拉入。 + +如果你以前没有使用`spring-test`模块,那么你应该从阅读 Spring 框架参考文档的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testing)开始。 + +### 8.3.测试 Spring 引导应用程序 + +Spring 引导应用程序是一个 Spring `ApplicationContext`,因此,除了正常情况下使用 vanilla Spring 上下文所做的工作外,不需要做任何非常特殊的事情来测试它。 + +| |只有在使用`SpringApplication`创建它的情况下, Spring 启动的外部属性、日志记录和其他功能才默认安装在上下文中。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring Boot 提供了一个`@SpringBootTest`注释,其可以作为标准`spring-test``@ContextConfiguration`注释的替代项,当需要 Spring 启动功能时。注释的工作原理是[creating the `ApplicationContext` used in your tests through `SpringApplication`](#features.testing.spring-boot-applications.detecting-configuration)。除了`@SpringBootTest`之外,还为[creating the `ApplicationContext` used in your tests through `SpringApplication`](#features.testing.spring-boot-applications.detecting-configuration)的应用程序提供了许多其他注释。 + +| |如果你正在使用 JUnit4,请不要忘记在你的测试中也添加`@RunWith(SpringRunner.class)`,否则注释将被忽略。
如果你正在使用 JUnit5,没有必要将等价的`@ExtendWith(SpringExtension.class)`添加为`@SpringBootTest`,并且其他`@…​Test`注释已经用它注释了。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,`@SpringBootTest`不会启动服务器。可以使用`@SpringBootTest`的`webEnvironment`属性来进一步改进测试的运行方式: + +* `MOCK`(默认):加载 Web`ApplicationContext`并提供模拟 Web 环境。使用此注释时,嵌入式服务器不会启动。如果你的 Classpath 上没有可用的 Web 环境,则该模式将透明地返回到创建常规的非 Web`ApplicationContext`。它可以与[`@AutoConfigureMockMvc` or `@AutoConfigureWebTestClient`](#features.testing.spring-boot-applications.with-mock-environment)一起用于对 Web 应用程序进行基于模拟的测试。 + +* `RANDOM_PORT`:加载`WebServerApplicationContext`并提供一个真实的 Web 环境。嵌入式服务器在一个随机端口上启动和监听。 + +* `DEFINED_PORT`:加载`WebServerApplicationContext`并提供一个真实的 Web 环境。嵌入式服务器在定义的端口(从`application.properties`)或默认端口`8080`上启动和侦听。 + +* `NONE`:通过使用`SpringApplication`加载`ApplicationContext`,但不提供*任何*Web 环境(模拟或其他方式)。 + +| |如果你的测试是`@Transactional`,那么默认情况下,它会在每个测试方法的末尾回滚事务。
但是,由于使用这种安排,`RANDOM_PORT`或`DEFINED_PORT`隐式地提供了一个真正的 Servlet 环境,HTTP 客户机和服务器在单独的线程中运行,因此,在单独的事务中.
在服务器上发起的任何事务在这种情况下都不回滚。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果应用程序为管理服务器使用不同的端口,`@SpringBootTest`with`webEnvironment = WebEnvironment.RANDOM_PORT`也将在单独的随机端口上启动管理服务器。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.1.检测 Web 应用程序类型 + +Spring 如果 MVC 是可用的,则配置一个常规的基于 MVC 的应用程序上下文。如果你只有 Spring 个 WebFlux,我们将检测到它并配置一个基于 WebFlux 的应用程序上下文。 + +如果两者都存在, Spring MVC 优先。如果要在此场景中测试一个反应式 Web 应用程序,则必须设置`spring.main.web-application-type`属性: + +``` +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(properties = "spring.main.web-application-type=reactive") +class MyWebFluxTests { + + // ... + +} + +``` + +#### 8.3.2.检测测试配置 + +如果你熟悉 Spring 测试框架,那么你可能习惯于使用`@ContextConfiguration(classes=…​)`来指定要加载哪个 Spring `@Configuration`。或者,你可能经常在测试中使用嵌套的`@Configuration`类。 + +在测试 Spring 引导应用程序时,这通常不是必需的。 Spring 只要不显式地定义一个配置,Boot 的`@*Test`注释就会自动搜索你的主要配置。 + +搜索算法从包含测试的包开始工作,直到找到一个用`@SpringBootApplication`或`@SpringBootConfiguration`注释的类。只要以合理的方式[结构化你的代码](using.html#using.structuring-your-code),通常就可以找到你的主配置。 + +| |如果使用[测试注释以测试应用程序的一个更具体的部分](#features.testing.spring-boot-applications.autoconfigured-tests),你应该避免在[主方法的应用程序类](#features.testing.spring-boot-applications.user-configuration-and-slicing)上添加特定于特定区域的配置设置。


的底层组件扫描配置定义了排除过滤器如果你在你的`@SpringBootApplication`-注释类上使用显式的`@ComponentScan`指令,请注意这些过滤器将被禁用。
如果你正在使用切片,则应再次定义它们。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你想定制主配置,可以使用嵌套的`@TestConfiguration`类。不同于嵌套的`@Configuration`类,嵌套的`@TestConfiguration`类将被用来代替应用程序的主配置,除了应用程序的主配置之外,还使用嵌套的`@TestConfiguration`类。 + +| |Spring 的测试框架在测试之间缓存应用程序上下文。
因此,只要你的测试共享相同的配置(无论如何发现),加载上下文的可能耗时的过程只会发生一次。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.3.不包括测试配置 + +如果你的应用程序使用组件扫描(例如,如果你使用`@SpringBootApplication`或`@ComponentScan`),你可能会发现只为特定测试创建的顶级配置类在任何地方都会意外地被选中。 + +正如我们[早些时候见过](#features.testing.spring-boot-applications.detecting-configuration),`@TestConfiguration`可以用于对内部类的测试进行自定义的主要配置。当放置在顶级类上时,`@TestConfiguration`表示不应通过扫描来读取`src/test/java`中的类。然后,你可以在需要的地方显式地导入该类,如下面的示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +@SpringBootTest +@Import(MyTestsConfiguration.class) +class MyTests { + + @Test + void exampleTest() { + // ... + } + +} + +``` + +| |如果直接使用`@ComponentScan`(即不通过`@SpringBootApplication`),则需要用它注册`TypeExcludeFilter`。
详见[the Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/context/TypeExcludeFilter.html)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.4.使用应用程序参数 + +如果应用程序期望[arguments](#features.spring-application.application-arguments),则可以使用`@SpringBootTest`属性注入它们。 + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(args = "--app.test=one") +class MyApplicationArgumentTests { + + @Test + void applicationArgumentsPopulated(@Autowired ApplicationArguments args) { + assertThat(args.getOptionNames()).containsOnly("app.test"); + assertThat(args.getOptionValues("app.test")).containsOnly("one"); + } + +} + +``` + +#### 8.3.5.在模拟环境中进行测试 + +默认情况下,`@SpringBootTest`不会启动服务器,而是设置一个模拟环境来测试 Web 端点。 + +使用 Spring MVC,我们可以使用[`MockMvc`](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#spring-mvc-test-framework)或`WebTestClient`查询我们的 Web 端点,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class MyMockMvcTests { + + @Test + void testWithMockMvc(@Autowired MockMvc mvc) throws Exception { + mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World")); + } + + // If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient + @Test + void testWithWebTestClient(@Autowired WebTestClient webClient) { + webClient + .get().uri("/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("Hello World"); + } + +} + +``` + +| |如果你想只关注 Web 层,而不是启动一个完整的`ApplicationContext`,请考虑[using `@WebMvcTest` instead](#features.testing.spring-boot-applications.spring-mvc-tests)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +对于 Spring WebFlux 端点,你可以使用[`WebTestClient`](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#webtestclient-tests),如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest +@AutoConfigureWebTestClient +class MyMockWebTestClientTests { + + @Test + void exampleTest(@Autowired WebTestClient webClient) { + webClient + .get().uri("/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("Hello World"); + } + +} + +``` + +| |在模拟环境中进行测试通常比使用完整的 Servlet 容器运行更快。但是,由于模拟发生在 Spring MVC 层,因此依赖较低级别 Servlet 容器行为的代码不能直接使用 MockMVC 进行测试。例如,, Spring boot 的错误处理是基于 Servlet 容器提供的“错误页”支持。
这意味着,虽然你可以测试你的 MVC 层抛出和处理预期的异常,你无法直接测试呈现了特定的[自定义错误页面](web.html#web.servlet.spring-mvc.error-handling.error-pages)。
如果你需要测试这些较低级别的问题,你可以启动一个完全运行的服务器,如下一节所述。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.6.使用正在运行的服务器进行测试 + +如果你需要启动一个完整的正在运行的服务器,我们建议你使用随机端口。如果使用`@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`,则每次测试运行时都会随机选择一个可用的端口。 + +`@LocalServerPort`注释可用于[注入实际使用的端口](howto.html#howto.webserver.discover-port)到你的测试中。为了方便起见,需要对启动的服务器进行 REST 调用的测试还可以使用`@Autowire`a[`WebTestClient`](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#webtestclient-tests),它解析到运行中的服务器的相对链接,并附带用于验证响应的专用 API,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class MyRandomPortWebTestClientTests { + + @Test + void exampleTest(@Autowired WebTestClient webClient) { + webClient + .get().uri("/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("Hello World"); + } + +} + +``` + +| |`WebTestClient`可同时用于实时服务器和[模拟环境](#features.testing.spring-boot-applications.with-mock-environment)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------| + +此设置需要在 Classpath 上执行`spring-webflux`。如果不能或不会添加 WebFlux, Spring Boot 还提供了`TestRestTemplate`功能: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.client.TestRestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +class MyRandomPortTestRestTemplateTests { + + @Test + void exampleTest(@Autowired TestRestTemplate restTemplate) { + String body = restTemplate.getForObject("/", String.class); + assertThat(body).isEqualTo("Hello World"); + } + +} + +``` + +#### 8.3.7.自定义 WebTestClient + +要定制`WebTestClient` Bean,请配置`WebTestClientBuilderCustomizer` Bean。使用用于创建`WebTestClient`的`WebTestClient.Builder`调用任何这样的 bean。 + +#### 8.3.8.使用 JMX + +当测试上下文框架缓存上下文时,默认情况下禁用 JMX,以防止相同的组件在同一域上注册。如果这样的测试需要访问`MBeanServer`,也可以考虑将其标记为 dirty: + +``` +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(properties = "spring.jmx.enabled=true") +@DirtiesContext +class MyJmxTests { + + @Autowired + private MBeanServer mBeanServer; + + @Test + void exampleTest() throws MalformedObjectNameException { + assertThat(this.mBeanServer.getDomains()).contains("java.lang"); + // ... + } + +} + +``` + +#### 8.3.9.使用度量 + +无论你的 Classpath 是什么,在使用`@SpringBootTest`时,除了内存中备份的仪表注册中心外,仪表注册中心不会自动配置。 + +如果作为集成测试的一部分,需要将指标导出到不同的后端,请用`@AutoConfigureMetrics`对其进行注释。 + +#### 8.3.10.嘲笑和窥探豆子 + +在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,你可能在某个远程服务上有一个 facade,而在开发过程中它是不可用的。当你想要模拟在真实环境中可能很难触发的故障时,模拟也很有用。 + +Spring 引导包括一个`@MockBean`注释,该注释可用于在你的`ApplicationContext`中为 Bean 定义一个 mockito 模拟。你可以使用该注释来添加新的 bean 或替换单个现有的 Bean 定义。注释可以直接用于测试类、测试中的字段或`@Configuration`类和字段。当在字段上使用时,创建的模拟实例也会被注入。在每种测试方法之后,mock bean 都会自动重置。 + +| |如果你的测试使用 Spring boot 的测试注释之一(例如`@SpringBootTest`),则自动启用此功能,
若要以不同的方式使用此功能,必须显式地添加监听器,如下例所示:

``
导入 org.springframework.boot.test.mock.mockito.mockitostexecutionListener;
import org.springframework.moxecto.restexecutionlistener;importorg.contexframework.contextframework.contest.contexr=“;<95"/>contextcontextionr=1394;"myclass=| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例用模拟实现替换现有的`RemoteService` Bean: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +@SpringBootTest +class MyTests { + + @Autowired + private Reverser reverser; + + @MockBean + private RemoteService remoteService; + + @Test + void exampleTest() { + given(this.remoteService.getValue()).willReturn("spring"); + String reverse = this.reverser.getReverseValue(); // Calls injected RemoteService + assertThat(reverse).isEqualTo("gnirps"); + } + +} + +``` + +| |`@MockBean`不能用来模拟 Bean 在应用程序上下文刷新期间执行的行为。
在执行测试时,应用程序上下文刷新已经完成,现在配置模拟行为已经太晚了。
在这种情况下,我们建议使用`@Bean`方法来创建和配置模拟行为。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此外,你可以使用`@SpyBean`用 mockito`spy`包装任何现有的 Bean。有关详细信息,请参见[Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/test/mock/mockito/SpyBean.html)。 + +| |CGLIB 代理,例如为作用域 bean 创建的代理,将代理方法声明为`final`。
这将阻止 Mockito 正常运行,因为它无法在其默认配置中模拟或监视`final`方法。,
如果你想模拟或监视这样的 Bean 方法,通过将`org.mockito:mockito-inline`添加到应用程序的测试依赖项,将 Mockito 配置为使用其内联 mock maker。
这允许 Mockito 模拟和监视`final`方法。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Spring 的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,而`@MockBean`或`@SpyBean`的使用会影响缓存键,这很可能会增加上下文的数量。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你正在使用`@SpyBean`监视带有`@Cacheable`方法的 Bean,该方法通过名称引用参数,则你的应用程序必须使用`-parameters`编译。
这将确保在监视了 Bean 之后,参数名称对缓存基础结构是可用的。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |当你使用`@SpyBean`监视由 Spring 代理的 Bean 时,在某些情况下可能需要删除 Spring 的代理,例如在使用`given`或`when`设置期望时。
使用`AopTestUtils.getTargetObject(yourProxiedSpy)`来这样做。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.11.自动配置的测试 + +Spring Boot 的自动配置系统在应用程序中运行良好,但有时在测试中可能会有点太多。通常情况下,只加载测试应用程序“部分”所需的配置部分是有帮助的。例如,你可能希望测试 Spring MVC 控制器是否正确映射 URL,并且你不希望在这些测试中涉及数据库调用,或者你可能希望测试 JPA 实体,并且你对这些测试运行时的 Web 层不感兴趣。 + +`spring-boot-test-autoconfigure`模块包括许多注释,可用于自动配置此类“切片”。它们都以类似的方式工作,提供一个`@…​Test`注释,用于加载`ApplicationContext`和一个或多个`@AutoConfigure…​`注释,这些注释可用于自定义自动配置设置。 + +| |每个切片将组件扫描限制为适当的组件,并加载一组非常受限制的自动配置类。
如果需要排除其中之一,大多数`@…​Test`注释都提供了`excludeAutoConfiguration`属性。
或者,你可以使用`@ImportAutoConfiguration#exclude`。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |不支持在一个测试中使用多个`@…​Test`注释来包含多个“切片”。
如果需要多个“切片”,请选择其中一个`@…​Test`注释,并手动包含其他“切片”的`@AutoConfigure…​`注释。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |也可以使用带有标准`@SpringBootTest`注释的`@AutoConfigure…​`注释。
如果你对应用程序“切片”不感兴趣,但希望使用一些自动配置的测试 bean,则可以使用此组合。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.12.自动配置的 JSON 测试 + +要测试对象 JSON 序列化和反序列化是否如预期的那样工作,你可以使用`@JsonTest`注释。@jsontest` 自动配置可用的受支持的 JSON 映射器,它可以是以下库之一: + +* Jackson`ObjectMapper`,任何`@JsonComponent`bean 和任何 Jackson`Module`s + +* `Gson` + +* `Jsonb` + +| |由`@JsonTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果需要配置自动配置的元素,可以使用`@AutoConfigureJsonTesters`注释。 + +Spring 引导包括基于 AssertJ 的帮助器,这些帮助器与 JSONASSERT 和 JSONPATH 库一起工作,以检查 JSON 是否如预期的那样出现。`JacksonTester`、`GsonTester`、`JsonbTester`和`BasicJsonTester`类可以分别用于 Jackson、GSON、JSONB 和字符串。当使用`@JsonTest`时,测试类上的任何 helper 字段都可以是`@Autowired`。下面的示例展示了一个用于 Jackson 的测试类: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.json.JsonTest; +import org.springframework.boot.test.json.JacksonTester; + +import static org.assertj.core.api.Assertions.assertThat; + +@JsonTest +class MyJsonTests { + + @Autowired + private JacksonTester json; + + @Test + void serialize() throws Exception { + VehicleDetails details = new VehicleDetails("Honda", "Civic"); + // Assert against a `.json` file in the same package as the test + assertThat(this.json.write(details)).isEqualToJson("expected.json"); + // Or use JSON path based assertions + assertThat(this.json.write(details)).hasJsonPathStringValue("@.make"); + assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make").isEqualTo("Honda"); + } + + @Test + void deserialize() throws Exception { + String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}"; + assertThat(this.json.parse(content)).isEqualTo(new VehicleDetails("Ford", "Focus")); + assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford"); + } + +} + +``` + +| |JSON helper 类也可以在标准单元测试中直接使用。
要做到这一点,如果不使用`@Before`方法,请在你的`@JsonTest`方法中调用该 helper 的`initFields`方法。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果使用 Spring boot 的基于 AssertJ 的助手在给定的 JSON 路径上断言一个数字值,则可能无法根据类型使用`isEqualTo`。相反,你可以使用 AssertJ 的`satisfies`来断言该值与给定条件匹配。例如,下面的示例断言,在`0.01`的偏移量内,实际数字是一个接近`0.15`的浮点值。 + +``` +@Test +void someTest() throws Exception { + SomeObject value = new SomeObject(0.152f); + assertThat(this.json.write(value)).extractingJsonPathNumberValue("@.test.numberValue") + .satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f))); +} + +``` + +#### 8.3.13.自动配置的 Spring MVC 测试 + +要测试 Spring MVC 控制器是否如预期的那样工作,请使用`@WebMvcTest`注释。@Webmvctest` 自动配置 Spring MVC 基础架构,并将扫描的 bean 限制为`@Controller`,`@ControllerAdvice`,`@JsonComponent`,`GenericConverter`,`Filter`,`HandlerInterceptor`,,`WebMvcConfigurer`,,`WebMvcConfigurer`。当使用`@WebMvcTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |`@WebMvcTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果需要注册额外的组件,例如 Jackson`Module`,则可以在测试中使用`@Import`导入额外的配置类。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------| + +通常,`@WebMvcTest`仅限于单个控制器,并与`@MockBean`组合使用,以为所需的协作者提供模拟实现。 + +`@WebMvcTest`还自动配置`MockMvc`。Mock MVC 提供了一种强大的方法,可以快速测试 MVC 控制器,而无需启动完整的 HTTP 服务器。 + +| |你还可以通过使用`@AutoConfigureMockMvc`对
进行注释,在非 @@webmvctest`(例如`@SpringBootTest`)中自动配置`MockMvc`。
下面的示例使用`MockMvc`:| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(UserVehicleController.class) +class MyControllerTests { + + @Autowired + private MockMvc mvc; + + @MockBean + private UserVehicleService userVehicleService; + + @Test + void testExample() throws Exception { + given(this.userVehicleService.getVehicleDetails("sboot")) + .willReturn(new VehicleDetails("Honda", "Civic")); + this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN)) + .andExpect(status().isOk()) + .andExpect(content().string("Honda Civic")); + } + +} + +``` + +| |如果需要配置自动配置的元素(例如,当应用 Servlet 过滤器时),可以在`@AutoConfigureMockMvc`注释中使用属性。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果使用 htmlUnit 和 Selenium,Auto-Configuration 还提供了一个 htmlUnit Bean 和/或一个 Selenium Bean。以下示例使用了 htmlUnit: + +``` +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +@WebMvcTest(UserVehicleController.class) +class MyHtmlUnitTests { + + @Autowired + private WebClient webClient; + + @MockBean + private UserVehicleService userVehicleService; + + @Test + void testExample() throws Exception { + given(this.userVehicleService.getVehicleDetails("sboot")).willReturn(new VehicleDetails("Honda", "Civic")); + HtmlPage page = this.webClient.getPage("/sboot/vehicle.html"); + assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic"); + } + +} + +``` + +| |默认情况下, Spring boot 将`WebDriver`bean 放在一个特殊的“范围”中,以确保每次测试后驱动程序退出,并且注入了一个新的实例。
如果你不想要这种行为,可以将`@Scope("singleton")`添加到你的`WebDriver`定义中。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |由 Spring 引导创建的`webDriver`作用域将替换任何用户定义的同名作用域。
如果你定义自己的`webDriver`作用域,则在使用`@WebMvcTest`时,你可能会发现它停止工作。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果在 Classpath 上具有 Spring 安全性,`@WebMvcTest`也将扫描`WebSecurityConfigurer`bean。你可以使用 Spring Security 的测试支持,而不是完全禁用此类测试的安全性。有关如何使用 Spring Security 的`MockMvc`支持的更多详细信息,请参见*[howto.html](howto.html#howto.testing.with-spring-security)*how-to 一节。 + +| |有时编写 Spring MVC 测试是不够的; Spring 引导可以帮助你运行[使用实际服务器进行完整的端到端测试](#features.testing.spring-boot-applications.with-running-server)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.14.自动配置的 Spring WebFlux 测试 + +要测试[Spring WebFlux](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html)控制器是否按预期工作,可以使用`@WebFluxTest`注释。@WebFluxTest` 自动配置 Spring WebFlux 基础设施,并将扫描的 bean 限制为`@Controller`,`@ControllerAdvice`,`Converter`,`GenericConverter`,`WebFilter`,和`WebFluxConfigurer`。当使用`@WebFluxTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |由`@WebFluxTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果需要注册额外的组件,例如 Jackson`Module`,则可以在测试中使用`@Import`导入额外的配置类。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------| + +通常,`@WebFluxTest`仅限于单个控制器,并与`@MockBean`注释结合使用,以为所需的协作者提供模拟实现。 + +`@WebFluxTest`还自动配置[`WebTestClient`](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#webtestclient),这提供了一种强大的方法,可以快速测试 WebFlux 控制器,而无需启动完整的 HTTP 服务器。 + +| |你还可以通过使用`@AutoConfigureWebTestClient`对
进行注释来自动配置`WebTestClient`在非 @@webfluxtest`(例如`@SpringBootTest`)中的`WebTestClient`。下面的示例显示了一个同时使用`@WebFluxTest`和`WebTestClient`的类:| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.mockito.BDDMockito.given; + +@WebFluxTest(UserVehicleController.class) +class MyControllerTests { + + @Autowired + private WebTestClient webClient; + + @MockBean + private UserVehicleService userVehicleService; + + @Test + void testExample() throws Exception { + given(this.userVehicleService.getVehicleDetails("sboot")) + .willReturn(new VehicleDetails("Honda", "Civic")); + this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN).exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("Honda Civic"); + } + +} + +``` + +| |此设置仅由 WebFlux 应用程序支持,因为在模拟 Web 应用程序中使用`WebTestClient`目前仅与 WebFlux 一起工作。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------| + +| |`@WebFluxTest`无法检测通过 Functional Web Framework 注册的路由。
对于在上下文中测试`RouterFunction`bean,请考虑通过使用`@Import`或通过使用`@SpringBootTest`导入你自己的`RouterFunction`。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |`@WebFluxTest`无法检测到注册为`@Bean`类型的`SecurityWebFilterChain`的自定义安全配置。
要在测试中包含该配置,你将需要通过使用`@Import`或通过使用`@SpringBootTest`导入注册 Bean 的配置。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |有时编写 Spring WebFlux 测试是不够的; Spring Boot 可以帮助你运行[使用实际服务器进行完整的端到端测试](#features.testing.spring-boot-applications.with-running-server)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.15.自动配置的数据 Cassandra 测试 + +你可以使用`@DataCassandraTest`来测试 Cassandra 应用程序。默认情况下,它配置`CassandraTemplate`,扫描`@Table`类,并配置 Spring 数据 Cassandra 存储库。当使用`@DataCassandraTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包含`@ConfigurationProperties`bean。(关于使用 Cassandra 与 Spring boot 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.cassandra)”。 + +| |`@DataCassandraTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例展示了在 Spring boot 中使用 Cassandra 测试的典型设置: + +``` +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.cassandra.DataCassandraTest; + +@DataCassandraTest +class MyDataCassandraTests { + + @Autowired + private SomeRepository repository; + +} + +``` + +#### 8.3.16.自动配置的数据 JPA 测试 + +你可以使用`@DataJpaTest`注释来测试 JPA 应用程序。默认情况下,它扫描`@Entity`类并配置 Spring 数据 JPA 存储库。如果嵌入式数据库在 Classpath 上可用,那么它也会配置一个。默认情况下,通过将`spring.jpa.show-sql`属性设置为`true`来记录 SQL 查询。可以使用注释的`showSql()`属性禁用此功能。 + +当使用`@DataJpaTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |`@DataJpaTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,数据 JPA 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,你可以禁用测试或整个类的事务管理,如下所示: + +``` +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@DataJpaTest +@Transactional(propagation = Propagation.NOT_SUPPORTED) +class MyNonTransactionalTests { + + // ... + +} + +``` + +JPA 测试数据还可以注入 Bean,这为 JPA 标准提供了一种替代方法,即专门为测试而设计。 + +| |`TestEntityManager`还可以通过添加`@AutoConfigureTestEntityManager`自动配置到你的任何基于 Spring 的测试类。
这样做时,请确保你的测试是在事务中运行的,例如,在你的测试类或方法上添加`@Transactional`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果需要的话,也可以使用`JdbcTemplate`。下面的示例显示了使用中的`@DataJpaTest`注释: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; + +import static org.assertj.core.api.Assertions.assertThat; + +@DataJpaTest +class MyRepositoryTests { + + @Autowired + private TestEntityManager entityManager; + + @Autowired + private UserRepository repository; + + @Test + void testExample() throws Exception { + this.entityManager.persist(new User("sboot", "1234")); + User user = this.repository.findByUsername("sboot"); + assertThat(user.getUsername()).isEqualTo("sboot"); + assertThat(user.getEmployeeNumber()).isEqualTo("1234"); + } + +} + +``` + +内存中的嵌入式数据库通常可以很好地用于测试,因为它们速度快且不需要任何安装。但是,如果你更喜欢对真实的数据库运行测试,则可以使用`@AutoConfigureTestDatabase`注释,如下例所示: + +``` +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +@AutoConfigureTestDatabase(replace = Replace.NONE) +class MyRepositoryTests { + + // ... + +} + +``` + +#### 8.3.17.自动配置的 JDBC 测试 + +`@JdbcTest`类似于`@DataJpaTest`,但用于仅需要`DataSource`且不使用 Spring 数据 JDBC 的测试。默认情况下,它配置一个内存嵌入数据库和一个`JdbcTemplate`。常规的`@Component`和`@ConfigurationProperties`bean 在使用`@JdbcTest`注释时不进行扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |由`@JdbcTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用测试或整个类的事务管理,如下所示: + +``` +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@JdbcTest +@Transactional(propagation = Propagation.NOT_SUPPORTED) +class MyTransactionalTests { + +} + +``` + +如果你希望你的测试在真实的数据库中运行,那么你可以使用`@AutoConfigureTestDatabase`注释,就像使用`DataJpaTest`注释一样。(见“[Auto-configured Data JPA Tests](#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa)”。 + +#### 8.3.18.自动配置的数据 JDBC 测试 + +`@DataJdbcTest`类似于`@JdbcTest`,但用于使用 Spring 数据 JDBC 存储库的测试。默认情况下,它配置内存中的嵌入式数据库、`JdbcTemplate`和 Spring 数据 JDBC 存储库。当使用`@DataJdbcTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |`@DataJdbcTest`启用的自动配置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,数据 JDBC 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用一个测试或整个测试类的事务管理,如[在 JDBC 示例中显示](#features.testing.spring-boot-applications.autoconfigured-jdbc)。 + +如果你希望你的测试在真实的数据库中运行,那么你可以使用`@AutoConfigureTestDatabase`注释,就像使用`DataJpaTest`注释一样。(见“[Auto-configured Data JPA Tests](#features.testing.spring-boot-applications.autoconfigured-spring-data-jpa)”。 + +#### 8.3.19.自动配置的 Jooq 测试 + +你可以以与`@JdbcTest`类似的方式使用`@JooqTest`,但用于与 Jooq 相关的测试。由于 Jooq 在很大程度上依赖于与数据库模式相对应的基于 Java 的模式,因此使用了现有的`DataSource`。如果要用内存数据库替换它,可以使用`@AutoConfigureTestDatabase`来覆盖这些设置。(关于使用 Jooq 和 Spring boot 的更多信息,请参见本章前面的“[data.html](data.html#data.sql.jooq)”。)常规的`@Component`和`@ConfigurationProperties`bean 在使用`@JooqTest`注释时不会被扫描。@enableConfigrationProperties` 可以用来包括`@ConfigurationProperties`bean。 + +| |由`@JooqTest`启用的自动配置的列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +`@JooqTest`配置`DSLContext`。下面的示例显示了正在使用的`@JooqTest`注释: + +``` +import org.jooq.DSLContext; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jooq.JooqTest; + +@JooqTest +class MyJooqTests { + + @Autowired + private DSLContext dslContext; + + // ... + +} + +``` + +在默认情况下,Jooq 测试是事务性的,并在每个测试结束时回滚。如果这不是你想要的,那么你可以禁用一个测试或整个测试类的事务管理,如[在 JDBC 示例中显示](#features.testing.spring-boot-applications.autoconfigured-jdbc)。 + +#### 8.3.20.自动配置的数据 MongoDB 测试 + +你可以使用`@DataMongoTest`来测试 MongoDB 应用程序。默认情况下,它配置内存中的嵌入式 MongoDB(如果可用),配置`MongoTemplate`,扫描`@Document`类,并配置 Spring 数据 MongoDB 存储库。当使用`@DataMongoTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。(关于使用 Spring Boot 的 MongoDB 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.mongodb)”。 + +| |`@DataMongoTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的类显示了使用中的`@DataMongoTest`注释: + +``` +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.data.mongodb.core.MongoTemplate; + +@DataMongoTest +class MyDataMongoDbTests { + + @Autowired + private MongoTemplate mongoTemplate; + + // ... + +} + +``` + +内嵌入内存的 MongoDB 通常在测试中运行良好,因为它速度快,并且不需要任何开发人员安装。但是,如果你更喜欢在真正的 MongoDB 服务器上运行测试,那么你应该排除嵌入的 MongoDB 自动配置,如下例所示: + +``` +import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; + +@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class) +class MyDataMongoDbTests { + + // ... + +} + +``` + +#### 8.3.21.自动配置的数据 NEO4J 测试 + +你可以使用`@DataNeo4jTest`来测试 NEO4J 应用程序。默认情况下,它扫描`@Node`类,并配置 Spring 数据 NEO4j 存储库。当使用`@DataNeo4jTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。(有关在 Spring boot 中使用 NEO4j 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.neo4j)”。 + +| |`@DataNeo4jTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例显示了在 Spring 引导中使用 NEO4J 测试的典型设置: + +``` +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest; + +@DataNeo4jTest +class MyDataNeo4jTests { + + @Autowired + private SomeRepository repository; + + // ... + +} + +``` + +默认情况下,数据 NEO4J 测试是事务性的,并在每个测试结束时回滚。有关更多详细信息,请参见 Spring 框架参考文档中的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/testing.html#testcontext-tx-enabling-transactions)。如果这不是你想要的,那么你可以禁用测试或整个类的事务管理,如下所示: + +``` +import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@DataNeo4jTest +@Transactional(propagation = Propagation.NOT_SUPPORTED) +class MyDataNeo4jTests { + +} + +``` + +| |反应性访问不支持事务性测试。
如果使用这种样式,则必须如上所述配置`@DataNeo4jTest`测试。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.22.自动配置的数据 Redis 测试 + +你可以使用`@DataRedisTest`来测试 Redis 应用程序。默认情况下,它扫描`@RedisHash`类并配置 Spring 数据 Redis 存储库。当使用`@DataRedisTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。(有关在 Spring 引导下使用 Redis 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.redis)”。 + +| |`@DataRedisTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例显示了使用中的`@DataRedisTest`注释: + +``` +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest; + +@DataRedisTest +class MyDataRedisTests { + + @Autowired + private SomeRepository repository; + + // ... + +} + +``` + +#### 8.3.23.自动配置的数据 LDAP 测试 + +你可以使用`@DataLdapTest`来测试 LDAP 应用程序。默认情况下,它会配置内存中的嵌入式 LDAP(如果可用),配置`LdapTemplate`,扫描`@Entry`类,并配置 Spring 数据 LDAP 存储库。当使用`@DataLdapTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包含`@ConfigurationProperties`bean。(有关在 Spring boot 中使用 LDAP 的更多信息,请参见本章前面的“[data.html](data.html#data.nosql.ldap)”。 + +| |`@DataLdapTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例显示了使用中的`@DataLdapTest`注释: + +``` +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest; +import org.springframework.ldap.core.LdapTemplate; + +@DataLdapTest +class MyDataLdapTests { + + @Autowired + private LdapTemplate ldapTemplate; + + // ... + +} + +``` + +嵌入式 LDAP 通常在测试中运行良好,因为它速度快,不需要任何开发人员安装。但是,如果你更喜欢在真实的 LDAP 服务器上运行测试,则应该排除嵌入的 LDAP 自动配置,如下例所示: + +``` +import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration; +import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest; + +@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class) +class MyDataLdapTests { + + // ... + +} + +``` + +#### 8.3.24.自动配置的 REST 客户机 + +你可以使用`@RestClientTest`注释来测试 REST 客户机。默认情况下,它会自动配置 Jackson、GSON 和 JSONB 支持,配置`RestTemplateBuilder`,并添加对`MockRestServiceServer`的支持。当使用`@RestClientTest`注释时,常规的`@Component`和`@ConfigurationProperties`bean 不会被扫描。@enableConfigurationProperties` 可用于包括`@ConfigurationProperties`bean。 + +| |`@RestClientTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要测试的特定 bean 应该通过使用`value`或`components`的`components`属性来指定,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; +import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; + +@RestClientTest(RemoteVehicleDetailsService.class) +class MyRestClientTests { + + @Autowired + private RemoteVehicleDetailsService service; + + @Autowired + private MockRestServiceServer server; + + @Test + void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails() throws Exception { + this.server.expect(requestTo("/greet/details")).andRespond(withSuccess("hello", MediaType.TEXT_PLAIN)); + String greeting = this.service.callRestService(); + assertThat(greeting).isEqualTo("hello"); + } + +} + +``` + +#### 8.3.25.自动配置的 Spring REST DOCS 测试 + +你可以使用`@AutoConfigureRestDocs`注释在模拟 MVC、Rest Assured 或 WebTestClient 的测试中使用[Spring REST Docs](https://spring.io/projects/spring-restdocs)。它消除了对 Spring REST DOCS 中的 JUnit 扩展的需要。 + +`@AutoConfigureRestDocs`可以用来覆盖默认的输出目录(如果你正在使用 Maven,则使用 `target/generated-snippets’;如果你正在使用 Gradle,则使用`build/generated-snippets`)。它还可以用于配置出现在任何有文档的 URI 中的主机、方案和端口。 + +##### Spring 使用模拟 MVC 的 REST DOCS 测试的自动配置 + +`@AutoConfigureRestDocs`在测试基于 Servlet 的 Web 应用程序时,定制`MockMvc` Bean 以使用 Spring REST DOCS。你可以通过使用`@Autowired`注入它,并在测试中使用它,就像在使用模拟 MVC 和 Spring REST DOCS 时通常使用的那样,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@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,如以下示例所示: + +``` +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer; +import org.springframework.restdocs.templates.TemplateFormats; + +@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`: + +``` +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; + +@TestConfiguration(proxyBeanMethods = false) +public class MyResultHandlerConfiguration { + + @Bean + public RestDocumentationResultHandler restDocumentation() { + return MockMvcRestDocumentation.document("{method-name}"); + } + +} + +``` + +##### 使用 WebTestClient 自动配置的 Spring REST DOCS 测试 + +`@AutoConfigureRestDocs`在测试反应性 Web 应用程序时也可以与`WebTestClient`一起使用。你可以通过使用`@Autowired`注入它,并在测试中使用它,就像你通常使用`@WebFluxTest`和 Spring REST DOCS 时一样,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; + +@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,如以下示例所示: + +``` +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsWebTestClientConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer; + +@TestConfiguration(proxyBeanMethods = false) +public class MyRestDocsConfiguration implements RestDocsWebTestClientConfigurationCustomizer { + + @Override + public void customize(WebTestClientRestDocumentationConfigurer configurer) { + configurer.snippets().withEncoding("UTF-8"); + } + +} + +``` + +如果希望利用 Spring REST DOCS 对参数化输出目录的支持,则可以使用`WebTestClientBuilderCustomizer`来为每个实体交换结果配置消费者。下面的示例显示了正在定义的这样的`WebTestClientBuilderCustomizer`: + +``` +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.reactive.server.WebTestClientBuilderCustomizer; +import org.springframework.context.annotation.Bean; + +import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document; + +@TestConfiguration(proxyBeanMethods = false) +public class MyWebTestClientBuilderCustomizerConfiguration { + + @Bean + public WebTestClientBuilderCustomizer restDocumentation() { + return (builder) -> builder.entityExchangeResultConsumer(document("{method-name}")); + } + +} + +``` + +##### Spring 具有 REST 保证的 REST DOCS 测试的自动配置 + +`@AutoConfigureRestDocs`生成一个`RequestSpecification` Bean,预先配置为使用 Spring REST DOCS,可用于你的测试。你可以通过使用`@Autowired`注入它,并在你的测试中使用它,就像在使用 REST ASSURED 和 Spring REST DOCS 时通常使用它一样,如以下示例所示: + +``` +import io.restassured.specification.RequestSpecification; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document; + +@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,如以下示例所示: + +``` +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer; +import org.springframework.restdocs.templates.TemplateFormats; + +@TestConfiguration(proxyBeanMethods = false) +public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer { + + @Override + public void customize(RestAssuredRestDocumentationConfigurer configurer) { + configurer.snippets().withTemplateFormat(TemplateFormats.markdown()); + } + +} + +``` + +#### 8.3.26.自动配置的 Spring Web 服务测试 + +##### 自动配置的 Spring Web 服务客户端测试 + +你可以使用`@WebServiceClientTest`来测试使用 Spring Web 服务项目调用 Web 服务的应用程序。默认情况下,它配置一个模拟`WebServiceServer` Bean 并自动定制你的`WebServiceTemplateBuilder`。(有关使用 Spring 引导的 Web 服务的更多信息,请参见本章前面的“[io.html](io.html#io.webservices)”。 + +| |`@WebServiceClientTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例显示了使用中的`@WebServiceClientTest`注释: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.webservices.client.WebServiceClientTest; +import org.springframework.ws.test.client.MockWebServiceServer; +import org.springframework.xml.transform.StringSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.ws.test.client.RequestMatchers.payload; +import static org.springframework.ws.test.client.ResponseCreators.withPayload; + +@WebServiceClientTest(SomeWebService.class) +class MyWebServiceClientTests { + + @Autowired + private MockWebServiceServer server; + + @Autowired + private SomeWebService someWebService; + + @Test + void mockServerCall() { + this.server + .expect(payload(new StringSource(""))) + .andRespond(withPayload(new StringSource("200"))); + assertThat(this.someWebService.test()) + .extracting(Response::getStatus) + .isEqualTo(200); + } + +} + +``` + +##### 自动配置的 Spring Web 服务服务器测试 + +你可以使用`@WebServiceServerTest`来测试使用 Spring Web 服务项目实现 Web 服务的应用程序。默认情况下,它配置了一个`MockWebServiceClient` Bean,可用于调用你的 Web 服务端点。(有关使用 Spring 引导的 Web 服务的更多信息,请参见本章前面的“[io.html](io.html#io.webservices)”。 + +| |`@WebServiceServerTest`启用的自动配置设置列表可以是[在附录中找到](test-auto-configuration.html#appendix.test-auto-configuration)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例显示了使用中的`@WebServiceServerTest`注释: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest; +import org.springframework.ws.test.server.MockWebServiceClient; +import org.springframework.ws.test.server.RequestCreators; +import org.springframework.ws.test.server.ResponseMatchers; +import org.springframework.xml.transform.StringSource; + +@WebServiceServerTest(ExampleEndpoint.class) +class MyWebServiceServerTests { + + @Autowired + private MockWebServiceClient client; + + @Test + void mockServerCall() { + this.client + .sendRequest(RequestCreators.withPayload(new StringSource(""))) + .andExpect(ResponseMatchers.payload(new StringSource("42"))); + } + +} + +``` + +#### 8.3.27.附加的自动配置和切片 + +每个切片提供一个或多个`@AutoConfigure…​`注释,即定义了作为切片的一部分应该包含的自动配置。可以通过创建自定义`@AutoConfigure…​`注释或在测试中添加`@ImportAutoConfiguration`,在逐个测试的基础上添加额外的自动配置,如下例所示: + +``` +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration; +import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; + +@JdbcTest +@ImportAutoConfiguration(IntegrationAutoConfiguration.class) +class MyJdbcTests { + +} + +``` + +| |确保不使用常规的`@Import`注释来导入自动配置,因为它们是由 Spring 引导以特定方式处理的。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------| + +或者,可以通过在`META-INF/spring.factories`中注册它们,为任何使用切片注释的情况添加额外的自动配置,如以下示例所示: + +``` +org.springframework.boot.test.autoconfigure.jdbc.JdbcTest=com.example.IntegrationAutoConfiguration +``` + +| |切片或`@AutoConfigure…​`注释可以通过这种方式进行自定义,只要它是用`@ImportAutoConfiguration`进行元注释的。| +|---|------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.28.用户配置和切片 + +如果以一种合理的方式[构造你的代码](using.html#using.structuring-your-code),你的`@SpringBootApplication`类是[used by default](#features.testing.spring-boot-applications.detecting-configuration)作为测试的配置。 + +因此,重要的是,不要在应用程序的主类中浪费特定于其特定功能领域的配置设置。 + +假设你正在使用 Spring 批处理,并且你依赖于它的自动配置。你可以将你的`@SpringBootApplication`定义如下: + +``` +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@EnableBatchProcessing +public class MyApplication { + + // ... + +} + +``` + +因为这个类是测试的源配置,所以任何切片测试实际上都会尝试启动 Spring 批处理,这绝对不是你想要做的。推荐的方法是将该区域特定的配置移动到与应用程序处于同一级别的单独的`@Configuration`类,如下例所示: + +``` +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableBatchProcessing +public class MyBatchConfiguration { + + // ... + +} + +``` + +| |根据应用程序的复杂性,你可以有一个用于自定义的`@Configuration`类,或者每个域区域有一个类。
后一种方法允许你在你的一个测试中启用它,如果需要的话,使用`@Import`注释。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +测试片从扫描中排除`@Configuration`类。例如,对于`@WebMvcTest`,以下配置将不包括在测试切片加载的应用程序上下文中给定的`WebMvcConfigurer` Bean: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration(proxyBeanMethods = false) +public class MyWebConfiguration { + + @Bean + public WebMvcConfigurer testConfigurer() { + return new WebMvcConfigurer() { + // ... + }; + } + +} + +``` + +但是,下面的配置将导致测试切片加载自定义`WebMvcConfigurer`。 + +``` +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Component +public class MyWebMvcConfigurer implements WebMvcConfigurer { + + // ... + +} + +``` + +另一个令人困惑的原因是扫描。假设,当你以一种合理的方式构造代码时,你需要扫描一个额外的包。你的应用程序可能类似于以下代码: + +``` +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan({ "com.example.app", "com.example.another" }) +public class MyApplication { + + // ... + +} + +``` + +这样做有效地覆盖了默认的组件扫描指令,其副作用是无论你选择的是哪一片,都要扫描这两个包。例如,`@DataJpaTest`似乎会突然扫描应用程序的组件和用户配置。同样,将自定义指令移动到一个单独的类是解决此问题的一个好方法。 + +| |如果这不是你的一个选项,那么你可以在测试的层次结构中的某个地方创建一个`@SpringBootConfiguration`,以便使用它。
或者,你可以为你的测试指定一个源,这将禁用查找缺省源的行为。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.29.使用 Spock 测试 Spring 引导应用程序 + +Spock2.x 可用于测试 Spring 引导应用程序。为此,在应用程序的构建中添加对 Spock 的`spock-spring`模块的依赖关系。有关更多详细信息,请参见[the documentation for Spock’s Spring module](https://spockframework.org/spock/docs/2.0/modules.html#_spring_module)。 + +### 8.4.测试实用程序 + +在测试应用程序时通常有用的一些测试实用程序类被打包为`spring-boot`的一部分。 + +#### 8.4.1.ConfigDataApplicationContextInitializer + +`ConfigDataApplicationContextInitializer`是一个`ApplicationContextInitializer`,你可以将其应用到你的测试中来加载 Spring boot`application.properties`文件。当你不需要`@SpringBootTest`提供的全套功能时,可以使用它,如下例所示: + +``` +import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = Config.class, initializers = ConfigDataApplicationContextInitializer.class) +class MyConfigFileTests { + + // ... + +} + +``` + +| |单独使用`ConfigDataApplicationContextInitializer`并不提供对`@Value("${…​}")`注入的支持。
其唯一的工作是确保`application.properties`文件被加载到 Spring 的`Environment`中。
对于`@Value`的支持,你需要另外配置一个`PropertySourcesPlaceholderConfigurer`,或者使用`@SpringBootTest`,它为你自动配置了一个。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.4.2.测试 PropertyValues + +`TestPropertyValues`可以让你快速地将属性添加到`ConfigurableEnvironment`或`ConfigurableApplicationContext`。你可以使用`key=value`字符串调用它,如下所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.mock.env.MockEnvironment; + +import static org.assertj.core.api.Assertions.assertThat; + +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`是一个 JUnit`Extension`,你可以使用它来捕获`System.out`和`System.err`输出。要使用添加`@ExtendWith(OutputCaptureExtension.class)`并将`CapturedOutput`作为参数注入到你的测试类构造函数或测试方法中,方法如下: + +``` +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +@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`及其状态代码来检测此类错误。 + +| |Spring Framework5.0 提供了一个新的`WebTestClient`,它对[WebFlux 集成测试](#features.testing.spring-boot-applications.spring-webflux-tests)和[WebFlux 和 MVC 端到端测试](#features.testing.spring-boot-applications.with-running-server)都有效。
它为断言提供了一个流畅的 API,而不像`TestRestTemplate`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +建议使用 Apache HTTP 客户机(版本 4.3.2 或更好),但不是强制的。如果你的 Classpath 上有这样的配置,则`TestRestTemplate`通过适当地配置客户机来响应。如果你确实使用了 Apache 的 HTTP 客户机,则启用了一些额外的测试友好特性: + +* 重定向不会被跟踪(因此你可以断言响应位置)。 + +* cookie 被忽略(因此模板是无状态的)。 + +`TestRestTemplate`可以在集成测试中直接实例化,如以下示例所示: + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.ResponseEntity; + +import static org.assertj.core.api.Assertions.assertThat; + +class MyTests { + + private TestRestTemplate template = new TestRestTemplate(); + + @Test + void testRequest() throws Exception { + ResponseEntity headers = this.template.getForEntity("https://myhost.example.com/example", String.class); + assertThat(headers.getHeaders().getLocation()).hasHost("other.example.com"); + } + +} + +``` + +或者,如果使用`@SpringBootTest`注释和`WebEnvironment.RANDOM_PORT`或`WebEnvironment.DEFINED_PORT`,则可以插入一个完全配置的`TestRestTemplate`并开始使用它。如果需要,可以通过`RestTemplateBuilder` Bean 应用额外的自定义。任何未指定主机和端口的 URL 都会自动连接到嵌入式服务器,如以下示例所示: + +``` +import java.time.Duration; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpHeaders; + +import static org.assertj.core.api.Assertions.assertThat; + +@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 启动时进行拾取。 + +自动配置可以与一个“启动器”相关联,该启动器提供自动配置代码以及你将与之一起使用的典型库。我们首先介绍构建自己的自动配置所需了解的内容,然后继续讨论[创建自定义启动器所需的典型步骤](#features.developing-auto-configuration.custom-starter)。 + +| |可以使用[demo project](https://github.com/snicoll-demos/spring-boot-master-auto-configuration)来展示如何一步一步地创建一个启动程序。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 9.1.理解自动配置的 bean + +在引擎盖下,自动配置是通过标准的`@Configuration`类实现的。附加的`@Conditional`注释用于在应用自动配置时进行约束。通常,自动配置类使用`@ConditionalOnClass`和`@ConditionalOnMissingBean`注释。这确保了自动配置仅在找到相关类以及尚未声明自己的`@Configuration`时才适用。 + +你可以浏览[`spring-boot-autoconfigure`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure)的源代码,查看 Spring 提供的`@Configuration`类(参见[`META-INF/spring.factories`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories)文件)。 + +### 9.2.定位自动配置候选项 + +Spring 引导检查在你发布的 jar 中是否存在`META-INF/spring.factories`文件。该文件应该在`EnableAutoConfiguration`键下列出你的配置类,如下例所示: + +``` +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\ +com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration +``` + +| |自动配置必须以*只是*的方式加载。
确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。
此外,自动配置类不应使组件扫描能够找到其他组件。应该使用
特定的`@Import`s。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果需要按特定顺序应用配置,则可以使用[`@autofigureafter`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureAfter.java)或[@autofigurebefore](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigureBefore.java)注释。例如,如果你提供了特定于 Web 的配置,那么你的类可能需要在`WebMvcAutoConfiguration`之后应用。 + +如果你希望订购某些不应该相互有任何直接了解的自动配置,也可以使用`@AutoConfigureOrder`。该注释与常规的`@Order`注释具有相同的语义,但为自动配置类提供了专用的顺序。 + +与标准的`@Configuration`类一样,应用自动配置类的顺序只会影响其 bean 的定义顺序。随后创建这些 bean 的顺序不受影响,并且由每个 Bean 的依赖关系和任何`@DependsOn`关系决定。 + +### 9.3.条件注释 + +你几乎总是希望在自动配置类中包含一个或多个`@Conditional`注释。`@ConditionalOnMissingBean`注释是一个常见的示例,如果开发人员对你的默认值不满意,它将允许他们覆盖自动配置。 + +Spring 引导包括许多`@Conditional`注释,你可以通过注释`@Configuration`类或单独的`@Bean`方法在自己的代码中重用这些注释。这些注释包括: + +* [类条件](#features.developing-auto-configuration.condition-annotations.class-conditions) + +* [Bean Conditions](#features.developing-auto-configuration.condition-annotations.bean-conditions) + +* [财产条件](#features.developing-auto-configuration.condition-annotations.property-conditions) + +* [资源条件](#features.developing-auto-configuration.condition-annotations.resource-conditions) + +* [Web 应用程序条件](#features.developing-auto-configuration.condition-annotations.web-application-conditions) + +* [SPEL 表达条件](#features.developing-auto-configuration.condition-annotations.spel-conditions) + +#### 9.3.1.类条件 + +`@ConditionalOnClass`和`@ConditionalOnMissingClass`注释让`@Configuration`类基于特定类的存在或不存在而被包括。由于批注元数据是通过使用[ASM](https://asm.ow2.io/)来解析的,因此可以使用`value`属性来引用真正的类,即使该类实际上可能不会出现在正在运行的应用程序上 Classpath。如果你希望通过使用`String`值来指定类名,那么也可以使用`name`属性。 + +这种机制不以同样的方式应用于`@Bean`方法,在这种方法中,返回类型通常是条件的目标:在方法的条件应用之前,JVM 将装载类和可能处理的方法引用,如果类不存在,这些引用将失败。 + +要处理此场景,可以使用一个单独的`@Configuration`类来隔离该条件,如以下示例所示: + +``` +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +// 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`作为在这种情况下未处理的类的引用。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 9.3.2. Bean 条件 + +`@ConditionalOnBean`和`@ConditionalOnMissingBean`注释允许基于特定 bean 的存在或不存在来包含 Bean。可以使用`value`属性按类型指定 bean,也可以使用`name`属性按名称指定 bean。`search`属性允许你限制在搜索 bean 时应该考虑的`ApplicationContext`层次结构。 + +当放置在`@Bean`方法上时,目标类型默认为该方法的返回类型,如以下示例所示: + +``` +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public SomeService someService() { + return new SomeService(); + } + +} + +``` + +在前面的示例中,如果`SomeService`类型的 Bean 中的`someService`已经包含在`ApplicationContext`中,则将创建`someService` Bean。 + +| |你需要非常小心添加 Bean 定义的顺序,因为这些条件是基于到目前为止已处理的内容进行评估的。,由于这个原因,在类级别上使用这些条件与用注释标记每个包含`@Bean`方法之间的唯一区别是如果条件不匹配,则前者阻止将`@Configuration`类注册为 Bean。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在声明`@Bean`方法时,在方法的返回类型中提供尽可能多的类型信息,例如,
,如果 Bean 的 Concrete 类实现了一个接口,那么 Bean 方法的返回类型应该是 Concrete 类而不是接口。在方法中提供尽可能多的类型信息在使用 Bean 条件时尤其重要,因为它们的评估只能依赖于在方法签名中可用的类型信息。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 9.3.3.财产条件 + +`@ConditionalOnProperty`注释允许基于 Spring Environment 属性包含配置。使用`prefix`和`name`属性来指定应该检查的属性。默认情况下,任何存在且不等于`false`的属性都是匹配的。还可以使用`havingValue`和`matchIfMissing`属性创建更高级的检查。 + +#### 9.3.4.资源条件 + +`@ConditionalOnResource`注释仅允许在存在特定资源时包含配置。可以通过使用通常的 Spring 约定来指定资源,如以下示例所示:`file:/home/user/test.dat`。 + +#### 9.3.5.Web 应用程序条件 + +`@ConditionalOnWebApplication`和`@ConditionalOnNotWebApplication`注释允许根据应用程序是否为“Web 应用程序”而包括配置。基于 Servlet 的 Web 应用程序是任何使用 Spring `WebApplicationContext`、定义`session`作用域或具有`ConfigurableWebEnvironment`的应用程序。反应性 Web 应用程序是任何使用`ReactiveWebApplicationContext`或具有`ConfigurableReactiveWebEnvironment`的应用程序。 + +`@ConditionalOnWarDeployment`注释允许根据应用程序是否是部署到容器中的传统 WAR 应用程序来包含配置。对于使用嵌入式服务器运行的应用程序,此条件将不匹配。 + +#### 9.3.6.SPEL 表达条件 + +`@ConditionalOnExpression`注释允许基于[SpEL expression](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/core.html#expressions)的结果包含配置。 + +| |在表达式中引用 Bean 将导致 Bean 在上下文刷新处理中很早就被初始化。
因此, Bean 将不符合后处理的条件(例如配置属性绑定),并且其状态可能不完整。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 9.4.测试你的自动配置 + +自动配置可能受到许多因素的影响:用户配置(`@ Bean ` 定义和`Environment`自定义)、条件评估(存在特定库),以及其他因素。具体地说,每个测试都应该创建一个定义良好的`ApplicationContext`,它代表了这些定制的组合。 + +`ApplicationContextRunner`通常被定义为用于收集基础、公共配置的测试类的字段。下面的示例确保始终调用`MyServiceAutoConfiguration`: + +``` +private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class)); + +``` + +| |如果必须定义多个自动配置,则不需要对它们的声明进行排序,因为它们的调用顺序与运行应用程序时的顺序完全相同。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +每个测试都可以使用 Runner 来表示特定的用例。例如,下面的示例调用一个用户配置(“userconfiguration”),并检查自动配置是否正确备份。调用`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`在自动配置测试中打印报告。 + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener; +import org.springframework.boot.logging.LogLevel; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +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.超越 Classpath + +还可以测试在运行时不存在特定类和/或包时会发生什么。 Spring 具有`FilteredClassLoader`的引导船,该引导船可以很容易地被跑步者使用。在下面的示例中,我们断言,如果`MyService`不存在,则自动配置将被正确禁用: + +``` +@Test +void serviceIsIgnoredIfLibraryIsNotPresent() { + this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class)) + .run((context) -> assertThat(context).doesNotHaveBean("myService")); +} + +``` + +### 9.5.创建自己的启动器 + +典型的 Spring 引导启动器包含用于自动配置和自定义给定技术的基础架构的代码,我们将其称为“Acme”。为了使其易于扩展,可以将专用名称空间中的许多配置键公开到环境中。最后,提供了一个单一的“启动”依赖项,以帮助用户尽可能轻松地启动。 + +具体地说,自定义启动器可以包含以下内容: + +* `autoconfigure`模块,其中包含“acme”的自动配置代码。 + +* `starter`模块提供了对`autoconfigure`模块的依赖关系,以及“acme”和任何通常有用的附加依赖关系。简而言之,添加 starter 应该提供开始使用该库所需的一切。 + +这种在两个模块中的分离是绝对没有必要的。如果“Acme”有几种口味、选项或可选功能,那么最好分离自动配置,因为你可以清楚地表达这样一个事实,即某些功能是可选的。此外,你还可以创建一个启动器,提供有关这些可选依赖项的意见。同时,其他人只能依赖`autoconfigure`模块,并根据不同的意见制作自己的启动器。 + +如果自动配置相对简单,并且没有可选功能,那么合并启动器中的两个模块肯定是一个选项。 + +#### 9.5.1.命名 + +你应该确保为你的启动器提供一个正确的名称空间。即使使用不同的 Maven `groupId`,也不要以`spring-boot`开始模块名称。我们可能会在将来为你自动配置的东西提供官方支持。 + +根据经验,你应该以 starter 的名称命名一个组合模块。例如,假设你正在为“acme”创建一个启动器,并且将自动配置模块命名为`acme-spring-boot`,并将启动器命名为`acme-spring-boot-starter`。如果只有一个模块合并了这两个模块,请将其命名为`acme-spring-boot-starter`。 + +#### 9.5.2.配置键 + +如果你的启动器提供了配置键,请为它们使用唯一的命名空间。特别是,不要在 Spring 引导使用的名称空间中包含你的键(例如`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); + + // getters/setters ... + + 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”开头描述。 + +* 对于`boolean`类型,以“是否”或“启用”开始描述。 + +* 对于基于集合的类型,以“逗号分隔的列表”开始描述。 + +* 使用`java.time.Duration`而不是`long`,并描述缺省单位,如果它与毫秒不同,例如“如果没有指定持续时间后缀,将使用秒”。 + +* 不要在描述中提供默认值,除非必须在运行时确定默认值。 + +请确保[触发元数据生成](configuration-metadata.html#appendix.configuration-metadata.annotation-processor),以便你的密钥也可以使用 IDE 辅助。你可能需要查看生成的元数据(`meta-inf/ Spring-configuration-metadata.json`),以确保你的键被正确地记录下来。在兼容的 IDE 中使用自己的启动器也是验证元数据质量的一个好主意。 + +#### 9.5.3.“自动配置”模块 + +`autoconfigure`模块包含了启动该库所需的所有内容。它还可能包含配置键定义(例如`@ConfigurationProperties`)和任何回调接口,可用于进一步定制组件的初始化方式。 + +| |你应该将库的依赖关系标记为可选的,这样你就可以更容易地在项目中包含`autoconfigure`模块。
如果你这样做,那么库就不会被提供,并且在默认情况下, Spring back off。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring Boot 使用注释处理器在元数据文件(`meta-inf/ Spring-autofigure-metadata.properties`)中收集关于自动配置的条件。如果该文件存在,它将被用来急切地过滤不匹配的自动配置,这将提高启动时间。建议在包含自动配置的模块中添加以下依赖项: + +``` + + org.springframework.boot + spring-boot-autoconfigure-processor + true + +``` + +如果直接在应用程序中定义了自动配置,请确保配置`spring-boot-maven-plugin`,以防止`repackage`目标将依赖项添加到 fat jar 中: + +``` + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.springframework.boot + spring-boot-autoconfigure-processor + + + + + + + +``` + +对于 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" +} +``` + +#### 9.5.4.启动器模块 + +起动器实际上是一个空的 jar。它的唯一目的是提供使用该库所需的依赖项。你可以把它看作是对开始工作需要什么的一种固执己见的看法。 + +不要对添加了启动器的项目进行假设。如果你正在自动配置的库通常需要其他启动器,那么也应该提及它们。如果可选依赖项的数量很高,那么提供一组适当的*默认值*依赖项可能会很困难,因为你应该避免包含对于库的典型使用来说不必要的依赖项。换句话说,你不应该包括可选的依赖关系。 + +| |无论哪种方式,你的启动程序都必须直接或间接地引用核心 Spring 启动程序(` Spring-boot-starter`)(如果你的启动程序依赖于另一个启动程序,则无需添加它),
如果一个项目是仅用你的自定义启动程序创建的, Spring Boot 的核心功能将因核心启动器的出现而受到尊重。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 10. Kotlin 支持 + +[Kotlin](https://kotlinlang.org)是一种针对 JVM(和其他平台)的静态类型语言,它允许编写简洁而优雅的代码,同时提供[互操作性](https://kotlinlang.org/docs/reference/java-interop.html)用 Java 编写的现有库。 + +Spring Boot 通过利用其他 Spring 项目中的支持来提供 Kotlin 支持,例如 Spring 框架、 Spring 数据和反应堆。有关更多信息,请参见[Spring Framework Kotlin support documentation](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/languages.html#kotlin)。 + +启动 Spring boot 和 Kotlin 的最简单方法是遵循[这个全面的教程](https://spring.io/guides/tutorials/spring-boot-kotlin/)。你可以使用[start.spring.io](https://start.spring.io/#!language=kotlin)创建新的 Kotlin 项目。如果你需要支持,可以随时加入[Kotlin Slack](https://slack.kotlinlang.org/)的 # Spring 频道,或者使用`spring`和`kotlin`标签在[Stack Overflow](https://stackoverflow.com/questions/tagged/spring+kotlin)上提问。 + +### 10.1.所需经费 + +Spring 启动需要至少 Kotlin 1.3.x 并且通过依赖管理管理管理管理合适的 Kotlin 版本。要使用 Kotlin,`org.jetbrains.kotlin:kotlin-stdlib`和`org.jetbrains.kotlin:kotlin-reflect`必须存在于 Classpath 上。也可以使用`kotlin-stdlib`的变体`kotlin-stdlib-jdk7`和`kotlin-stdlib-jdk8`。 + +由于[Kotlin classes are final by default](https://discuss.kotlinlang.org/t/classes-final-by-default/166),你可能想要配置[kotlin-spring](https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support)插件,以便自动打开 Spring-注释的类,以便可以代理它们。 + +在 Kotlin 中序列化/反序列化 JSON 数据需要[Jackson’s Kotlin module](https://github.com/FasterXML/jackson-module-kotlin)。当在 Classpath 上找到它时,它会自动注册。如果 Jackson 和 Kotlin 存在但 Jackson 模块 Kotlin 不存在,则记录警告消息。 + +| |默认情况下,如果在[start.spring.io](https://start.spring.io/#!language=kotlin)上引导一个 Kotlin 项目,就会提供这些依赖项和插件。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 10.2.零安全 + +Kotlin 的关键特征之一是[null-safety](https://kotlinlang.org/docs/reference/null-safety.html)。它在编译时处理`null`值,而不是将问题推迟到运行时并遇到`NullPointerException`。这有助于消除常见的错误源,而无需支付`Optional`之类包装器的费用。 Kotlin 还允许使用如[comprehensive guide to null-safety in Kotlin](https://www.baeldung.com/kotlin-null-safety)中所描述的具有可空的值的函数结构。 + +虽然 Java 不允许在其类型系统中表示空安全,但 Spring Framework、 Spring Data 和 Reactor 现在通过对工具友好的注释为其 API 提供了空安全。默认情况下,来自 Kotlin 中使用的 Java API 的类型被识别为[platform types](https://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types),对其放松了空检查。[Kotlin’s support for JSR 305 annotations](https://kotlinlang.org/docs/reference/java-interop.html#jsr-305-support)与空性注释相结合,为 Kotlin 中相关的 Spring API 提供了空安全性。 + +可以通过添加带有以下选项的`-Xjsr305`编译器标志来配置 JSR305 检查:`-Xjsr305={strict|warn|ignore}`。默认行为与`-Xjsr305=warn`相同。在从 Spring API 推断出的 Kotlin 类型中,`strict`值需要考虑到空安全性,但是在使用时应该知道 Spring API 的无效性声明即使在较小的版本之间也可以发展,并且将来可能会添加更多的检查)。 + +| |目前还不支持泛型类型参数、varargs 和数组元素的可空性。
有关最新信息,请参见[SPR-15942](https://jira.spring.io/browse/SPR-15942)。
还请注意, Spring Boot 自己的 API 是[尚未注释](https://github.com/spring-projects/spring-boot/issues/10712)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 10.3. Kotlin 空气污染指数 + +#### 10.3.1.运行应用程序 + +Spring 引导提供了一种惯用的方式来运行带有`runApplication(*args)`的应用程序,如以下示例所示: + +``` +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +class MyApplication + +fun main(args: Array) { + runApplication(*args) +} + +``` + +这是`SpringApplication.run(MyApplication::class.java, *args)`的直接替换。它还允许定制应用程序,如以下示例所示: + +``` +runApplication(*args) { + setBannerMode(OFF) +} + +``` + +#### 10.3.2.扩展 + +Kotlin [extensions](https://kotlinlang.org/docs/reference/extensions.html)提供了扩展具有附加功能的现有类的能力。 Spring 引导 Kotlin API 利用这些扩展为现有的 API 添加新的 Kotlin 特定的便利。 + +`TestRestTemplate`扩展,类似于 Spring 框架中为`RestOperations`提供的扩展,在 Spring 框架中。在其他事情中,扩展使得有可能利用 Kotlin 具体化的类型参数。 + +### 10.4.依赖管理 + +为了避免混合 Kotlin 依赖于 Classpath 的不同版本, Spring 引导导入 Kotlin BOM。 + +有了 Maven, Kotlin 版本可以通过设置`kotlin.version`属性来定制,并且为`kotlin-maven-plugin`提供插件管理。在 Gradle 中, Spring 引导插件自动将`kotlin.version`与 Kotlin 插件的版本对齐。 + +Spring Boot 还通过导入 Kotlin 协程 BOM 来管理协程依赖的版本。可以通过设置`kotlin-coroutines.version`属性来定制该版本。 + +| |如果一个 Kotlin 项目至少对[start.spring.io](https://start.spring.io/#!language=kotlin)具有一个反应性依赖项,则默认情况下提供`org.jetbrains.kotlinx:kotlinx-coroutines-reactor`依赖项。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 10.5.@configrationProperties + +`@ConfigurationProperties`与[@constructorbinding](#features.external-config.typesafe-configuration-properties.constructor-binding)组合使用时,支持具有不可变`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 + ) +} + +``` + +| |要使用注释处理器生成[你自己的元数据](configuration-metadata.html#appendix.configuration-metadata.annotation-processor),[“kapt”应该配置](https://kotlinlang.org/docs/reference/kapt.html)带有`spring-boot-configuration-processor`依赖关系的
注意,由于 Kapt 提供的模型中的限制,一些功能(例如检测默认值或不推荐的项)无法工作。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 10.6.测试 + +虽然可以使用 JUnit4 来测试 Kotlin 代码,但 JUnit5 是默认提供的,并且是推荐的。JUnit5 允许一个测试类被实例化一次,并在类的所有测试中重用。这使得在非静态方法上使用`@BeforeAll`和`@AfterAll`注释成为可能,这非常适合 Kotlin。 + +要模拟 Kotlin 类,建议使用[MockK](https://mockk.io/)。如果你需要`Mockk`mockito specific[`@MockBean` and `@SpyBean` annotations](#features.testing.spring-boot-applications.mocking-beans)的等价物,则可以使用[SpringMockK](https://github.com/Ninja-Squad/springmockk),这提供了类似的`@MockkBean`和`@SpykBean`注释。 + +### 10.7.资源 + +#### 10.7.1.进一步阅读 + +* [Kotlin language reference](https://kotlinlang.org/docs/reference/) + +* [Kotlin Slack](https://kotlinlang.slack.com/)(带专用 # Spring 频道) + +* [Stackoverflow with `spring` and `kotlin` tags](https://stackoverflow.com/questions/tagged/spring+kotlin) + +* [Try Kotlin in your browser](https://try.kotlinlang.org/) + +* [Kotlin blog](https://blog.jetbrains.com/kotlin/) + +* [Awesome Kotlin](https://kotlin.link/) + +* [Tutorial: building web applications with Spring Boot and Kotlin](https://spring.io/guides/tutorials/spring-boot-kotlin/) + +* [Developing Spring Boot applications with Kotlin](https://spring.io/blog/2016/02/15/developing-spring-boot-applications-with-kotlin) + +* [A Geospatial Messenger with Kotlin, Spring Boot and PostgreSQL](https://spring.io/blog/2016/03/20/a-geospatial-messenger-with-kotlin-spring-boot-and-postgresql) + +* [Introducing Kotlin support in Spring Framework 5.0](https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0) + +* [Spring Framework 5 Kotlin APIs, the functional way](https://spring.io/blog/2017/08/01/spring-framework-5-kotlin-apis-the-functional-way) + +#### 10.7.2.例子 + +* [spring-boot-kotlin-demo](https://github.com/sdeleuze/spring-boot-kotlin-demo):常规 Spring 引导 + Spring 数据 JPA 项目 + +* [mixit](https://github.com/mixitconf/mixit): Spring Boot2+WebFlux+Reactive Spring Data MongoDB + +* [spring-kotlin-fullstack](https://github.com/sdeleuze/spring-kotlin-fullstack):使用 Kotlin2js 代替 JavaScript 或 TypeScript 作为前端的 webflux Kotlin fullstack 示例 + +* [spring-petclinic-kotlin](https://github.com/spring-petclinic/spring-petclinic-kotlin): Spring PetClinic 样本申请的 Kotlin 版本 + +* [spring-kotlin-deepdive](https://github.com/sdeleuze/spring-kotlin-deepdive):将 Boot1.0+Java 逐步迁移到 Boot2.0+ Kotlin + +* [spring-boot-coroutines-demo](https://github.com/sdeleuze/spring-boot-coroutines-demo):协程样本项目 + +## 11. 接下来读什么? + +如果你想更多地了解本节中讨论的任何类,请参见[Spring Boot API documentation](https://docs.spring.io/spring-boot/docs/2.6.4/api/),或者你也可以浏览[源代码直接](https://github.com/spring-projects/spring-boot/tree/v2.6.4)。如果你有具体的问题,请参阅[how-to](howto.html#howto)部分。 + +如果你对 Spring Boot 的核心功能感到满意,那么你可以继续阅读[可投入生产的功能](actuator.html#actuator)。 + diff --git a/docs/spring-boot/getting-help.md b/docs/spring-boot/getting-help.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c1d86f832fa16d39e85d2970b19f6d7dbc60ea65 100644 --- a/docs/spring-boot/getting-help.md +++ b/docs/spring-boot/getting-help.md @@ -0,0 +1,15 @@ +# 获得帮助 + +如果你的靴子有问题,我们愿意提供帮助。 + +* 尝试[how-to 文档](howto.html#howto)。它们为最常见的问题提供了解决方案。 + +* 学习基础知识。 Spring Boot 建立在许多其他 Spring 项目之上。查看[spring.io](https://spring.io)网站以获取大量的参考文档。如果你从 Spring 开始,请尝试[guides](https://spring.io/guides)中的一个。 + +* 问一个问题。我们监视[stackoverflow.com](https://stackoverflow.com)中带有[`spring-boot`](https://stackoverflow.com/tags/spring-boot)标记的问题。 + +* 使用 Spring boot 在[github.com/spring-projects/spring-boot/issues](https://github.com/spring-projects/spring-boot/issues)上报告错误。 + +注: + +所有的启动都是开源的,包括文档。如果你发现 DOCS 存在问题或希望改进它们,请[get involved](https://github.com/spring-projects/spring-boot/tree/v2.6.4)。 \ No newline at end of file diff --git a/docs/spring-boot/getting-started.md b/docs/spring-boot/getting-started.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..948100e9f2f216c19e2c057d1df8a47cc59a2b7d 100644 --- a/docs/spring-boot/getting-started.md +++ b/docs/spring-boot/getting-started.md @@ -0,0 +1,461 @@ +# 开始 + +如果你要开始使用 Spring boot,或者一般的“ Spring”,那么请阅读这一部分。它回答了基本的“什么?”、“怎么做?”和“为什么?”的问题。它包括对 Spring 启动的介绍,以及安装说明。然后,我们将带领你构建你的第一个 Spring 启动应用程序,并在此讨论一些核心原则。 + +## 1. 引入 Spring 引导 + +Spring 引导帮助你创建可以运行的独立的、基于生产级别 Spring 的应用程序。我们对 Spring 平台和第三方库持有一种固执己见的观点,这样你就可以在开始时尽量减少麻烦。大多数 Spring 启动应用程序只需要很少的 Spring 配置。 + +你可以使用 Spring boot 来创建 Java 应用程序,这些应用程序可以通过使用`java -jar`或更传统的 WAR 部署来启动。我们还提供了一个运行“ Spring 脚本”的命令行工具。 + +我们的主要目标是: + +* 为所有 Spring 开发提供一种从根本上更快且广泛获得的入门体验。 + +* 在需求开始偏离默认值时,不要拘泥于常规,但要尽快摆脱。 + +* 提供一系列非功能特性,这些特性是大型项目(如嵌入式服务器、安全性、度量、健康检查和外部化配置)所共有的。 + +* 绝对不需要代码生成,也不需要 XML 配置。 + +## 2. 系统要求 + +Spring Boot2.6.4 要求[Java 8](https://www.java.com)并且兼容到并包括 Java17。[Spring Framework 5.3.16](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/)或以上也是必需的。 + +为以下构建工具提供了明确的构建支持: + +|Build Tool|版本| +|----------|---------------------| +| Maven |3.5+| +| Gradle |6.8.x、6.9.x 和 7.x| + +### 2.1. Servlet 集装箱 + +Spring 启动支持以下嵌入式 Servlet 容器: + +| Name |Servlet 版本| +|------------|---------------| +| Tomcat 9.0 | 4.0 | +| Jetty 9.4 | 3.1 | +| Jetty 10.0 | 4.0 | +|Undertow 2.0| 4.0 | + +你还可以将 Spring 引导应用程序部署到任何 Servlet 3.1+ 兼容容器。 + +## 3. 安装 Spring 启动 + +Spring 启动可以与“经典”Java 开发工具一起使用,也可以作为命令行工具安装。无论哪种方式,你都需要[Java SDK v1.8](https://www.java.com)或更高的值。在开始之前,你应该使用以下命令检查当前的 Java 安装: + +``` +$ java -version +``` + +如果你是 Java 开发的新手,或者你想要尝试 Spring 引导,那么你可能想要首先尝试[Spring Boot CLI](#getting-started.installing.cli)(命令行接口)。否则,请继续阅读“经典”安装说明。 + +### 3.1.Java 开发人员的安装说明 + +你可以以与任何标准 Java 库相同的方式使用 Spring boot。要做到这一点,在你的 Classpath 中包含适当的`spring-boot-*.jar`文件。 Spring 启动不需要任何特殊的工具集成,因此可以使用任何 IDE 或文本编辑器。此外, Spring 引导应用程序没有什么特别之处,因此你可以像运行任何其他 Java 程序一样运行和调试 Spring 引导应用程序。 + +尽管你*Coul*复制了 Spring 引导 JAR,但我们通常建议你使用支持依赖管理的构建工具(例如 Maven 或 Gradle)。 + +#### 3.1.1. Maven 安装 + +Spring 引导与 Apache Maven 3.3 或更高版本兼容。如果尚未安装 Maven,则可以按照[maven.apache.org](https://maven.apache.org)中的说明进行操作。 + +| |在许多操作系统上, Maven 都可以安装一个包管理器。
如果你使用 OSX Homebrew,请尝试`brew install maven`。
Ubuntu 用户可以运行`sudo apt-get install maven`。
具有[Chocolatey](https://chocolatey.org/)的 Windows 用户可以从提升的(管理员)提示符中运行`choco install maven`。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 引导依赖项使用`org.springframework.boot``groupId`。通常,你的 Maven POM 文件从`spring-boot-starter-parent`项目继承,并声明一个或多个[“Starters”](using.html#using.build-systems.starters)的依赖关系。 Spring 引导还提供了一个可选的[Maven plugin](build-tool-plugins.html#build-tool-plugins.maven)来创建可执行 JAR。 + +关于开始使用 Spring boot 和 Maven 的更多详细信息,可以在 Maven 插件的参考指南[入门部分](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#getting-started)中找到。 + +#### 3.1.2. Gradle 安装 + +Spring 引导与 Gradle 6.8、6.9 和 7.x 兼容。如果尚未安装 Gradle,则可以按照[gradle.org](https://gradle.org)中的说明进行操作。 + +Spring 启动依赖项可以通过使用`org.springframework.boot``group`来声明。通常,你的项目声明依赖于一个或多个[“Starters”](using.html#using.build-systems.starters)。 Spring Boot 提供了一个有用的[Gradle plugin](build-tool-plugins.html#build-tool-plugins.gradle),可用于简化依赖项声明并创建可执行 JAR。 + +Gradle 包装纸 + +当你需要构建一个项目时, Gradle 包装器提供了一种很好的“获取” Gradle 方式。它是一个小的脚本和库,你可以在代码旁边提交它来引导构建过程。详见[docs.gradle.org/current/userguide/gradle\_wrapper.html](https://docs.gradle.org/current/userguide/gradle_wrapper.html)。 + +关于开始使用 Spring boot 和 Gradle 的更多详细信息,可以在 Gradle 插件的参考指南[入门部分](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#getting-started)中找到。 + +### 3.2.安装 Spring boot cli + +Spring 引导 CLI(命令行界面)是一种命令行工具,你可以使用它来快速地使用 Spring 进行原型设计。它允许你运行[Groovy](https://groovy-lang.org/)脚本,这意味着你有一个熟悉的类似 Java 的语法,而不需要那么多样板代码。 + +你不需要使用 CLI 来处理 Spring 引导,但是这是一种在没有 IDE 的情况下使 Spring 应用程序脱离地面的快速方法。 + +#### 3.2.1.手动安装 + +你可以从 Spring 软件库下载 Spring CLI 发行版: + +* [spring-boot-cli-2.6.4-bin.zip](https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.6.4/spring-boot-cli-2.6.4-bin.zip) + +* [spring-boot-cli-2.6.4-bin.tar.gz](https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.6.4/spring-boot-cli-2.6.4-bin.tar.gz) + +尖端[快照分布](https://repo.spring.io/snapshot/org/springframework/boot/spring-boot-cli/)也是可用的。 + +一旦下载,请按照解压缩归档文件中的[INSTALL.txt](https://raw.githubusercontent.com/spring-projects/spring-boot/v2.6.4/spring-boot-project/spring-boot-cli/src/main/content/INSTALL.txt)说明进行操作。总之,在`.zip`文件中的`bin/`目录中有一个`spring`脚本(用于 Windows 的 ` Spring.bat`)。或者,你可以在`java -jar`文件中使用`.jar`(该脚本可以帮助你确保 Classpath 设置正确)。 + +#### 3.2.2.用 SDKMAN 安装! + +SDKMAN!(软件开发工具包管理器)可以用于管理各种二进制 SDK 的多个版本,包括 Groovy 和 Spring 引导 CLI。得到 SDKMAN!从[sdkman.io](https://sdkman.io)开始,使用以下命令安装 Spring boot: + +``` +$ sdk install springboot +$ spring --version +Spring CLI v2.6.4 +``` + +如果你为 CLI 开发了功能,并希望访问你构建的版本,请使用以下命令: + +``` +$ sdk install springboot dev /path/to/spring-boot/spring-boot-cli/target/spring-boot-cli-2.6.4-bin/spring-2.6.4/ +$ sdk default springboot dev +$ spring --version +Spring CLI v2.6.4 +``` + +前面的指令安装一个`spring`的本地实例,称为`dev`实例。它指向你的目标构建位置,因此每次重新生成 Spring 引导时,`spring`都是最新的。 + +你可以通过运行以下命令看到它: + +``` +$ sdk ls springboot + +================================================================================ +Available Springboot Versions +================================================================================ +> + dev +* 2.6.4 + +================================================================================ ++ - local version +* - installed +> - currently in use +================================================================================ +``` + +#### 3.2.3.OSX Homebrew 安装 + +如果你在 Mac 上使用[Homebrew](https://brew.sh/),则可以使用以下命令安装 Spring boot cli: + +``` +$ brew tap spring-io/tap +$ brew install spring-boot +``` + +Homebrew 将`spring`安装到`/usr/local/bin`。 + +| |如果你没有看到公式,那么你安装的 BREW 可能已经过时。
在这种情况下,运行`brew update`,然后再试一次。| +|---|---------------------------------------------------------------------------------------------------------------------------------| + +#### 3.2.4. MacPorts 安装 + +如果你在 Mac 上使用[MacPorts](https://www.macports.org/),则可以使用以下命令安装 Spring boot cli: + +``` +$ sudo port install spring-boot-cli +``` + +#### 3.2.5.命令行完成 + +Spring 引导 CLI 包括为[BASH](https://en.wikipedia.org/wiki/Bash_%28Unix_shell%29)和[zsh](https://en.wikipedia.org/wiki/Z_shell)shell 提供命令完成的脚本。你可以在任何 shell 中`source`该脚本(也称为`spring`),或者将其放入你的个人或系统范围的 bash 完成初始化中。在 Debian 系统上,系统范围的脚本位于`/shell-completion/bash`中,当新的 shell 启动时,该目录中的所有脚本都会被执行。例如,如果你已经通过使用 SDKMAN!安装了脚本,要手动运行该脚本,请使用以下命令: + +``` +$ . ~/.sdkman/candidates/springboot/current/shell-completion/bash/spring +$ spring + grab help jar run test version +``` + +| |如果你使用 Homebrew 或 MacPorts 安装 Spring 引导 CLI,那么命令行完成脚本将自动注册到你的 shell 中。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 3.2.6.Windows Scoop 安装 + +如果你在 Windows 上并使用[Scoop](https://scoop.sh/),则可以使用以下命令安装 Spring boot cli: + +``` +> scoop bucket add extras +> scoop install springboot +``` + +Scoop 将`spring`安装到`~/scoop/apps/springboot/current/bin`。 + +| |如果你没有看到应用程序清单,那么你安装的 Scoop 可能已经过期。
在这种情况下,运行`scoop update`,然后再试一次。| +|---|----------------------------------------------------------------------------------------------------------------------------------------| + +#### 3.2.7.快速启动 Spring CLI 示例 + +你可以使用下面的 Web 应用程序来测试你的安装。首先,创建一个名为`app.groovy`的文件,如下所示: + +``` +@RestController +class ThisWillActuallyRun { + + @RequestMapping("/") + String home() { + "Hello World!" + } + +} + +``` + +然后从一个 shell 中运行它,如下所示: + +``` +$ spring run app.groovy +``` + +| |应用程序的第一次运行很慢,因为依赖项是下载的。
后续运行要快得多。| +|---|----------------------------------------------------------------------------------------------------------------| + +在你最喜欢的 Web 浏览器中打开`[localhost:8080](http://localhost:8080)`。你应该会看到以下输出: + +``` +Hello World! +``` + +## 4. 开发你的第一个 Spring 启动应用程序 + +这一部分描述了如何开发一个小型的“Hello World!”Web 应用程序,该应用程序突出了 Spring Boot 的一些关键功能。我们使用 Maven 来构建这个项目,因为大多数 IDE 都支持它。 + +| |[spring.io](https://spring.io)网站包含许多使用 Spring 引导的“入门”[guides](https://spring.io/guides),
如果需要解决特定问题,首先检查那里。

你可以通过转到[start.spring.io](https://start.spring.io)并从依赖项搜索器中选择“web”启动器来缩短以下步骤。
这样做会生成一个新的项目结构,以便你可以[立即开始编码](#getting-started.first-application.code)。
检查[start.spring.io user guide](https://github.com/spring-io/start.spring.io/blob/main/USING.adoc)以获取更多详细信息。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +在开始之前,打开一个终端并运行以下命令,以确保安装了有效的 Java 和 Maven 版本: + +``` +$ java -version +java version "1.8.0_102" +Java(TM) SE Runtime Environment (build 1.8.0_102-b14) +Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14, mixed mode) +``` + +``` +$ mvn -v +Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-17T14:33:14-04:00) +Maven home: /usr/local/Cellar/maven/3.3.9/libexec +Java version: 1.8.0_102, vendor: Oracle Corporation +``` + +| |这个示例需要在它自己的目录中创建。
后续的说明假设你已经创建了一个合适的目录,并且它是你当前的目录。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.1.创建 POM + +我们需要从创建一个 Maven `pom.xml`文件开始。`pom.xml`是用于构建项目的配方。打开你最喜欢的文本编辑器,并添加以下内容: + +``` + + + 4.0.0 + + com.example + myproject + 0.0.1-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.6.4 + + + + + +``` + +前面的列表应该为你提供一个可工作的构建。你可以通过运行`mvn package`来测试它(目前,你可以忽略“ jar 将为空-没有内容被标记为包含!”警告)。 + +| |此时,你可以将项目导入到 IDE 中(大多数现代 Java IDE 都包含对 Maven 的内置支持)。
为了简单起见,我们将继续在此示例中使用纯文本编辑器。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.2.添加 Classpath 依赖项 + +Spring Boot 提供了许多“启动器”,允许你将罐子添加到你的 Classpath。我们的烟雾测试应用程序使用 POM 中`parent`部分中的`spring-boot-starter-parent`。`spring-boot-starter-parent`是一种特殊的启动器,它提供了有用的 Maven 默认值。它还提供了[“扶养-管理”](using.html#using.build-systems.dependency-management)节,以便你可以省略“有福的”依赖项的`version`标记。 + +其他“启动器”提供了在开发特定类型的应用程序时可能需要的依赖关系。因为我们正在开发一个 Web 应用程序,所以我们添加了一个`spring-boot-starter-web`依赖项。在此之前,我们可以通过运行以下命令查看当前的内容: + +``` +$ mvn dependency:tree + +[INFO] com.example:myproject:jar:0.0.1-SNAPSHOT +``` + +`mvn dependency:tree`命令打印项目依赖关系的树表示。你可以看到`spring-boot-starter-parent`本身不提供依赖关系。要添加必要的依赖关系,请编辑`pom.xml`,并在`parent`部分下面添加`spring-boot-starter-web`依赖关系: + +``` + + + org.springframework.boot + spring-boot-starter-web + + +``` + +如果再次运行`mvn dependency:tree`,你将看到现在有许多额外的依赖关系,包括 Tomcat Web 服务器和 Spring 引导本身。 + +### 4.3.编写代码 + +为了完成我们的应用程序,我们需要创建一个单独的 Java 文件。默认情况下, Maven 从`src/main/java`编译源代码,因此你需要创建该目录结构,然后添加一个名为`src/main/java/MyApplication.java`的文件,以包含以下代码: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@EnableAutoConfiguration +public class MyApplication { + + @RequestMapping("/") + String home() { + return "Hello World!"; + } + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +虽然这里没有太多的代码,但发生了很多事情。在接下来的几节中,我们将介绍重要的部分。 + +#### 4.3.1.restcontroller 和 @requestmapping 注释 ### + +我们的`MyApplication`类的第一个注释是`@RestController`。这被称为*刻板印象*注释。它为阅读代码的人提供了提示,并为 Spring 提供了类扮演特定角色的提示。在这种情况下,我们的类是一个 Web`@Controller`,因此 Spring 在处理传入的 Web 请求时考虑它。 + +`@RequestMapping`注释提供了“路由”信息。它告诉 Spring 任何带有`/`路径的 HTTP 请求都应该映射到`home`方法。`@RestController`注释告诉 Spring 将生成的字符串直接呈现给调用方。 + +| |`@RestController`和`@RequestMapping`注释是 Spring MVC 注释(它们不特定于 Spring 引导)。
有关更多详细信息,请参见 Spring 参考文档中的[MVC section](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 4.3.2.@enableAutoConfiguration 注释 ### + +第二个类级注释是`@EnableAutoConfiguration`。这个注释告诉 Spring 引导“猜测”你想要如何配置 Spring,基于你添加的 jar 依赖关系。由于`spring-boot-starter-web`添加了 Tomcat 和 Spring MVC,因此自动配置假定你正在开发一个 Web 应用程序,并相应地设置 Spring。 + +起动器和自动配置 + +自动配置的设计是为了与“启动器”很好地配合,但这两个概念并没有直接联系在一起。你可以自由地选择 jar 启动器之外的依赖项。 Spring 启动仍然会尽力自动配置你的应用程序。 + +#### 4.3.3.“主”法 + +我们应用程序的最后一部分是`main`方法。对于应用程序入口点,这是一种遵循 Java 约定的标准方法。通过调用`run`,我们的主方法委托给 Spring boot 的`SpringApplication`类。`SpringApplication` 引导我们的应用程序,启动 Spring,然后启动自动配置的 Tomcat Web 服务器。我们需要将`MyApplication.class`作为参数传递给`run`方法,以告诉`SpringApplication`哪个是主要的 Spring 组件。还会传递`args`数组,以公开任何命令行参数。 + +### 4.4.运行示例 + +在这一点上,你的应用程序应该可以工作。因为你使用了`spring-boot-starter-parent` POM,所以你有了一个有用的`run`目标,你可以使用它来启动应用程序。从根项目目录中键入`mvn spring-boot:run`以启动应用程序。你应该会看到类似于以下内容的输出: + +``` +$ mvn spring-boot:run + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.6.4) +....... . . . +....... . . . (log output here) +....... . . . +........ Started MyApplication in 2.222 seconds (JVM running for 6.514) +``` + +如果将 Web 浏览器打开到`[localhost:8080](http://localhost:8080)`,应该会看到以下输出: + +``` +Hello World! +``` + +要优雅地退出应用程序,请按`ctrl-c`。 + +### 4.5.创建可执行文件 jar + +我们通过创建一个完全自包含的可执行文件 jar 来完成我们的示例,该文件可以在生产环境中运行。可执行 JAR(有时称为“FAT JAR”)是包含你的已编译类以及代码需要运行的所有 jar 依赖项的归档文件。 + +可执行 JAR 和 Java + +Java 没有提供一种标准的方式来加载嵌套的 jar 文件( jar 文件本身包含在 jar 中)。如果你希望分发一个自包含的应用程序,这可能会有问题。 + +为了解决这个问题,许多开发人员使用“UBER”JAR。 jar UBER 将应用程序的所有依赖项中的所有类打包到一个单独的归档文件中。这种方法的问题在于,很难看到应用程序中有哪些库。如果在多个 JAR 中使用相同的文件名(但具有不同的内容),也可能会有问题。 + +Spring boot 需要[不同的方法](executable-jar.html#appendix.executable-jar),并让你实际上直接嵌套罐子。 + +要创建可执行文件 jar,我们需要将`spring-boot-maven-plugin`添加到我们的`pom.xml`中。要做到这一点,请在`dependencies`部分下方插入以下行: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + + +``` + +| |`spring-boot-starter-parent` POM 包括``配置以绑定`repackage`目标。
如果不使用父 POM,则需要自己声明此配置。
详情请参见[插件文档](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#getting-started)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +保存`pom.xml`并从命令行中运行`mvn package`,如下所示: + +``` +$ mvn package + +[INFO] Scanning for projects... +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] Building myproject 0.0.1-SNAPSHOT +[INFO] ------------------------------------------------------------------------ +[INFO] .... .. +[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ myproject --- +[INFO] Building jar: /Users/developer/example/spring-boot-example/target/myproject-0.0.1-SNAPSHOT.jar +[INFO] +[INFO] --- spring-boot-maven-plugin:2.6.4:repackage (default) @ myproject --- +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +``` + +如果查看`target`目录,应该会看到`myproject-0.0.1-SNAPSHOT.jar`。该文件的大小应该在 10MB 左右。如果想要查看内部,可以使用`jar tvf`,如下所示: + +``` +$ jar tvf target/myproject-0.0.1-SNAPSHOT.jar +``` + +你还应该在`target`目录中看到一个名为`myproject-0.0.1-SNAPSHOT.jar.original`的小得多的文件。这是在 Spring 引导重新打包之前 Maven 创建的原始 jar 文件。 + +要运行该应用程序,请使用`java -jar`命令,如下所示: + +``` +$ java -jar target/myproject-0.0.1-SNAPSHOT.jar + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.6.4) +....... . . . +....... . . . (log output here) +....... . . . +........ Started MyApplication in 2.536 seconds (JVM running for 2.864) +``` + +和前面一样,要退出应用程序,请按`ctrl-c`。 + +## 5. 接下来读什么? + +希望这一部分提供了一些 Spring 引导基础知识,并帮助你编写自己的应用程序。如果你是一位面向任务类型的开发人员,那么你可能希望跳到[spring.io](https://spring.io),并遵循一些[getting started](https://spring.io/guides/)指南,这些指南解决了特定的“我如何使用 Spring?”问题。我们还有 Spring 特定于引导的“[How-to](howto.html#howto)”参考文档。 + +否则,下一个逻辑步骤是读取*[using.html](using.html#using)*。如果你真的不耐烦了,你也可以跳到前面去读*[Spring Boot features](features.html#features)*。 diff --git a/docs/spring-boot/howto.md b/docs/spring-boot/howto.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4a0b0f7e4ff40969f98f9d186874d4643e3521bd 100644 --- a/docs/spring-boot/howto.md +++ b/docs/spring-boot/howto.md @@ -0,0 +1,3123 @@ +# “操作指南” + +这一部分提供了一些常见的“我如何做到这一点…”问题的答案,这些问题在使用 Spring Boot 时经常会出现。它的覆盖范围并不全面,但确实涵盖了相当多的内容。 + +如果你有一个我们在此未讨论的特定问题,你可能想要检查[stackoverflow.com](https://stackoverflow.com/tags/spring-boot),以查看是否有人已经提供了答案。这也是一个提出新问题的好地方(请使用`spring-boot`标签)。 + +我们也非常乐意扩展这一部分。如果你想添加“how-to”,请给我们发送[pull request](https://github.com/spring-projects/spring-boot/tree/v2.6.4)。 + +## 1. Spring 引导应用程序 + +本节包括与 Spring 引导应用程序直接相关的主题。 + +### 1.1.创建自己的故障分析器 + +[“故障分析仪”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/diagnostics/FailureAnalyzer.html)是一种很好的方法,可以在启动时拦截异常,并将其转换为人类可读的消息,包装在[“失效分析”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/diagnostics/FailureAnalysis.html)中。 Spring Boot 为与应用程序上下文相关的异常、JSR-303 验证等提供了这样的分析器。你也可以创建自己的。 + +`AbstractFailureAnalyzer`是`FailureAnalyzer`的一个方便的扩展,用于检查要处理的异常中是否存在指定的异常类型。你可以从中进行扩展,以便你的实现只有在异常实际存在时才有机会处理该异常。如果由于任何原因无法处理异常,则返回`null`,以便给另一个实现一个处理异常的机会。 + +`FailureAnalyzer`实现必须在`META-INF/spring.factories`中注册。以下示例寄存器`ProjectConstraintViolationFailureAnalyzer`: + +``` +org.springframework.boot.diagnostics.FailureAnalyzer=\ +com.example.ProjectConstraintViolationFailureAnalyzer +``` + +| |如果你需要访问`BeanFactory`或`Environment`,则你的`FailureAnalyzer`可以分别实现`BeanFactoryAware`或`EnvironmentAware`。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.2.故障排除自动配置 + +引导自动配置尽力“做正确的事情”,但有时事情会失败,而且很难讲出原因。 + +有一个真正有用的`ConditionEvaluationReport`可用于任何 Spring 引导`ApplicationContext`。如果启用`DEBUG`日志输出,就可以看到它。如果使用`spring-boot-actuator`(参见[《执行器》一章](actuator.html#actuator)),还会有一个`conditions`端点在 JSON 中呈现该报告。使用该端点调试应用程序,并查看在运行时启动 Spring 时添加了哪些特性(哪些特性没有添加)。 + +通过查看源代码和 Javadoc,可以回答更多的问题。在阅读代码时,请记住以下经验法则: + +* 查找名为`*AutoConfiguration`的类,并阅读它们的源代码。请特别注意`@Conditional*`注释,以找出它们启用的功能以及它们启用的时间。将`--debug`添加到命令行或系统属性`-Ddebug`,可以在控制台上获得在应用程序中做出的所有自动配置决策的日志。在启用了执行器的正在运行的应用程序中,查看`conditions`端点(`/执行器/条件’或等效的 JMX)以获得相同的信息。 + +* 查找`@Configuration属性`的类(例如[“serverproperties”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/Server属性.java)),并从那里读取可用的外部配置选项。`@Configuration属性`注释具有`name`属性,该属性充当外部属性的前缀。因此,`Server属性`具有`prefix="server"`并且其配置属性是`server.port`,`server.address`,以及其他。在启用了执行器的正在运行的应用程序中,查看`configprops`端点。 + +* 在`Binder`上查找`bind`方法的使用情况,以轻松的方式显式地从`Environment`中提取配置值。它常与前缀一起使用。 + +* 查找直接绑定到`Environment`的`@Value`注释。 + +* 查找响应 SPEL 表达式打开和关闭特性的`@ConditionalOnExpression`注释,通常使用从`Environment`解析的占位符进行评估。 + +### 1.3.在启动前自定义环境或应用程序上下文 ## + +a`SpringApplication`具有`ApplicationListeners`和`ApplicationContextInitializers`,它们用于对上下文或环境应用自定义。 Spring 引导从`META-INF/spring.factories`加载许多这样的自定义以供内部使用。注册额外定制的方法不止一种: + +* 从编程的角度来看,通过在`SpringApplication`上调用`addListeners`和`addInitializers`方法,每个应用程序都可以运行它。 + +* 声明性地,通过设置`context.initializer.classes`或`context.listener.classes`属性,为每个应用程序设置。 + +* 声明性地,对于所有应用程序,通过添加`META-INF/spring.factories`并打包一个 jar 文件,所有应用程序都将其用作一个库。 + +`SpringApplication`将一些特殊的`ApplicationEvents`发送给侦听器(有些甚至在创建上下文之前),然后为`ApplicationContext`发布的事件注册侦听器。有关完整列表,请参见“ Spring 引导功能”部分中的“[应用程序事件和监听器](features.html#features.spring-application.application-events-and-listeners)”。 + +在使用`EnvironmentPostProcessor`刷新应用程序上下文之前,还可以自定义`Environment`。每个实现都应该在`META-INF/spring.factories`中注册,如下例所示: + +``` +org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor +``` + +该实现可以加载任意文件并将其添加到`Environment`中。例如,下面的示例从 Classpath 加载 YAML 配置文件: + +``` +import java.io.IOException; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.boot.env.YamlPropertySourceLoader; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.util.Assert; + +public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor { + + private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + Resource path = new ClassPathResource("com/example/myapp/config.yml"); + PropertySource propertySource = loadYaml(path); + environment.getPropertySources().addLast(propertySource); + } + + private PropertySource loadYaml(Resource path) { + Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist"); + try { + return this.loader.load("custom-resource", path).get(0); + } + catch (IOException ex) { + throw new IllegalStateException("Failed to load yaml configuration from " + path, ex); + } + } + +} + +``` + +| |`Environment`已经准备好了所有常用的属性源。因此,可以从环境中获取文件的位置。
前面的示例在列表的末尾添加了`custom-resource`属性源。在任何通常的其他位置中定义的键都具有优先权。
一个自定义实现可以定义另一个顺序。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |虽然在`@SpringBootApplication`上使用`@PropertySource`似乎是在`Environment`中加载自定义资源的一种方便方法,我们不建议这样做。
这样的属性源在应用程序上下文被刷新之前不会被添加到`Environment`。
这对于配置某些属性(如`logging.*`和`spring.main.*`)已经太晚了,因为这些属性是在开始刷新之前读取的。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.4.构建应用程序上下文层次结构(添加父上下文或根上下文)### + +你可以使用`ApplicationBuilder`类来创建父/子`ApplicationContext`层次结构。有关更多信息,请参见“ Spring 引导功能”部分中的“[features.html](features.html#features.spring-application.fluent-builder-api)”。 + +### 1.5.创建一个非 Web 应用程序 + +Spring 并非所有的应用程序都必须是 Web 应用程序(或 Web 服务)。如果你希望在`main`方法中执行一些代码,并且还需要引导一个应用程序来设置要使用的基础设施,那么你可以使用 Spring 引导的`SpringApplication`功能。一个`SpringApplication`会改变它的`ApplicationContext`类,这取决于它是否认为需要一个 Web 应用程序。你可以做的第一件事是将与服务器相关的依赖关系(例如 Servlet API)从 Classpath 中去除。如果你不能这样做(例如,你从相同的代码库运行两个应用程序),那么你可以在`SpringApplication`实例上显式调用`setWebApplicationType(WebApplicationType.NONE)`或设置`applicationContextClass`属性(通过 Java API 或使用外部属性)。希望作为业务逻辑运行的应用程序代码可以实现为`CommandLineRunner`,并作为`@Bean`定义放入上下文。 + +## 2. 属性和配置 + +本节包括有关设置和读取属性、配置设置及其与 Spring 启动应用程序的交互的主题。 + +### 2.1.在构建时自动展开属性 + +你可以使用现有的构建配置,而不是对项目的构建配置中也指定的一些属性进行硬编码,从而自动扩展它们。这在 Maven 和 Gradle 中都是可能的。 + +#### 2.1.1.使用 Maven #### 进行自动属性扩展 + +通过使用资源筛选,你可以从 Maven 项目中自动扩展属性。如果使用`spring-boot-starter-parent`,则可以使用`@[[email protected]](/cdn-cgi/l/email-protection)`占位符引用 Maven“项目属性”,如以下示例所示: + +属性 + +``` +[email protected]@ +[email protected]@ +``` + +Yaml + +``` +app: + encoding: "@[email protected]" + java: + version: "@[email protected]" +``` + +| |只有生产配置是以这种方式进行筛选的(换句话说,在`src/test/resources`上不应用筛选)。| +|---|---------------------------------------------------------------------------------------------------------------------| + +| |如果启用`addResources`标志,`spring-boot:run`目标可以直接将`src/main/resources`添加到 Classpath 中(用于热重载目的)。
这样做绕过了资源过滤和此功能。,相反,
,你可以使用`exec:java`目标或自定义插件的配置。
有关更多详细信息,请参见[插件使用页面](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#getting-started)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果不使用启动父级,则需要在`pom.xml`的``元素中包含以下元素: + +``` + + + src/main/resources + true + + +``` + +还需要在``中包含以下元素: + +``` + + org.apache.maven.plugins + maven-resources-plugin + 2.7 + + + @ + + false + + +``` + +| |如果在配置中使用标准 Spring 占位符(例如`${placeholder}`),则`useDefaultDelimiters`属性很重要。
如果该属性未设置为`false`,则可以通过构建对其进行扩展。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.2.使用 Gradle #### 进行自动属性扩展 + +通过配置 Java 插件的`processResources`任务,你可以从 Gradle 项目中自动扩展属性,如下例所示: + +``` +processResources { + expand(project.properties) +} +``` + +然后,你可以使用占位符来引用你的 Gradle 项目的属性,如以下示例所示: + +属性 + +``` +app.name=${name} +app.description=${description} +``` + +Yaml + +``` +app: + name: "${name}" + description: "${description}" +``` + +| |Gradle 的`expand`方法使用 Groovy 的`SimpleTemplateEngine`,它转换`${..}`令牌。
的`${..}`样式与 Spring 自己的属性占位符机制冲突。
将 Spring 属性占位符与自动展开一起使用,将 Spring 属性占位符转义如下:`\${..}`。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.2.将 SpringApplication 的配置外部化 ### + +`SpringApplication`具有 Bean 属性设置器,因此你可以在创建应用程序时使用其 Java API 来修改其行为。或者,你可以通过在`spring.main.*`中设置属性来外部化配置。例如,在`application.properties`中,你可能有以下设置: + +属性 + +``` +spring.main.web-application-type=none +spring.main.banner-mode=off +``` + +Yaml + +``` +spring: + main: + web-application-type: "none" + banner-mode: "off" +``` + +然后 Spring 启动横幅不会在启动时打印,并且应用程序不会启动嵌入式 Web 服务器。 + +在外部配置中定义的属性覆盖并替换用 Java API 指定的值,但主要源是一个明显的例外。主源是提供给`SpringApplication`构造函数的那些源: + +``` +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(MyApplication.class); + application.setBannerMode(Banner.Mode.OFF); + application.run(args); + } + +} + +``` + +或将`sources(…​)`方法转换为`SpringApplicationBuilder`: + +``` +import org.springframework.boot.Banner; +import org.springframework.boot.builder.SpringApplicationBuilder; + +public class MyApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .bannerMode(Banner.Mode.OFF) + .sources(MyApplication.class) + .run(args); + } + +} + +``` + +给出了上面的示例,如果我们有以下配置: + +属性 + +``` +spring.main.sources=com.example.MyDatabaseConfig,com.example.MyJmsConfig +spring.main.banner-mode=console +``` + +Yaml + +``` +spring: + main: + sources: "com.example.MyDatabaseConfig,com.example.MyJmsConfig" + banner-mode: "console" +``` + +实际的应用程序将显示横幅(被配置重写),并为`ApplicationContext`使用三个源。应用程序来源如下: + +1. `MyApplication`(来自代码) + +2. `MyDatabaseConfig`(来自外部配置) + +3. `MyJmsConfig`(来自外部配置) + +### 2.3.更改应用程序外部属性的位置 ### + +默认情况下,来自不同来源的属性以定义的顺序添加到 Spring `Environment`中(确切的顺序请参见“ Spring 引导功能”部分中的“[features.html](features.html#features.external-config)”)。 + +你还可以提供以下系统属性(或环境变量)来更改行为: + +* `spring.config.name`:默认为`application`作为文件名的根。 + +* `spring.config.location`:要加载的文件(例如 Classpath 资源或 URL)。为该文档设置了一个单独的`Environment`属性源,它可以被系统属性、环境变量或命令行覆盖。 + +无论你在环境中设置了什么, Spring 引导总是加载`application.properties`,如上文所述。默认情况下,如果使用 YAML,那么扩展名为“.yml”的文件也会添加到列表中。 + +Spring 引导日志记录在`DEBUG`级别加载的配置文件和在`TRACE`级别未找到的候选文件。 + +有关更多详细信息,请参见[“配置文件应用程序监听器”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java)。 + +### 2.4.使用“short”命令行参数 + +有些人喜欢使用(例如)`--port=9000`而不是`--server.port=9000`来在命令行上设置配置属性。你可以通过在`application.properties`中使用占位符来启用此行为,如下例所示: + +属性 + +``` +server.port=${port:8080} +``` + +Yaml + +``` +server: + port: "${port:8080}" +``` + +| |如果从`spring-boot-starter-parent` POM 继承,则`maven-resources-plugins`的默认筛选标记已从`${*}`更改为`@`(即,`@[[email protected]](/cdn-cgi/l/email-protection)`而不是`${maven.token}`),以防止与 Spring 样式的占位符发生冲突。
如果你已经直接为`application.properties`启用了 Maven 筛选,那么你可能还希望将默认的筛选标记更改为使用[其他分隔符](https://maven.apache.org/plugins/maven-resources-plugin/resources-mojo.html#delimiters)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在这种特殊情况下,端口绑定工作在 PaaS 环境中,例如 Heroku 或 Cloud Foundry。
在这两个平台中,`PORT`环境变量被自动设置,并且 Spring 可以绑定到`Environment`属性的大写同义词。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.5.对外部属性使用 YAML + +YAML 是 JSON 的超集,因此是一种方便的语法,用于以层次结构格式存储外部属性,如以下示例所示: + +``` +spring: + application: + name: "cruncher" + datasource: + driver-class-name: "com.mysql.jdbc.Driver" + url: "jdbc:mysql://localhost/test" +server: + port: 9000 +``` + +创建一个名为`application.yml`的文件,并将其放在 Classpath 的根目录中。然后将`snakeyaml`添加到你的依赖项( Maven 坐标`org.yaml:snakeyaml`,如果你使用`spring-boot-starter`)。将 YAML 文件解析为一个 Java`Map`(类似于 JSON 对象),并且 Spring 引导使映射变平,使其具有一级深度并具有周期分隔的键,就像许多人习惯于使用 Java 中的`属性`文件一样。 + +前面的示例 YAML 对应于下面的`application.properties`文件: + +``` +spring.application.name=cruncher +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.url=jdbc:mysql://localhost/test +server.port=9000 +``` + +有关 YAML 的更多信息,请参见“ Spring Boot Features”部分中的“[features.html](features.html#features.external-config.yaml)”。 + +### 2.6.设置活动的 Spring 配置文件 + +Spring `Environment`对此有一个 API,但通常需要设置一个系统属性(` Spring.profiles.active`)或一个 OS 环境变量(`spring_profiles_active`)。此外,你还可以使用`-D`参数启动应用程序(请记住将其放在主类或 jar 归档文件之前),如下所示: + +``` +$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar +``` + +在 Spring 引导中,还可以在`application.properties`中设置活动配置文件,如以下示例所示: + +属性 + +``` +spring.profiles.active=production +``` + +Yaml + +``` +spring: + profiles: + active: "production" +``` + +这种方式的值集被系统属性或环境变量设置代替,而不是被`SpringApplicationBuilder.profiles()`方法代替。因此,可以使用后一个 Java API 来增强配置文件,而无需更改默认值。 + +有关更多信息,请参见“ Spring 引导功能”部分中的“[features.html](features.html#features.profiles)”。 + +### 2.7.设置默认配置文件名 + +默认配置文件是一个配置文件,如果没有配置文件处于活动状态,则启用该配置文件。默认情况下,默认配置文件的名称是`default`,但可以使用系统属性(` Spring.profiles.default`)或 OS 环境变量(`spring_profiles_default`)更改它。 + +在 Spring boot 中,还可以在`application.properties`中设置默认的配置文件名,如下例示例所示: + +属性 + +``` +spring.profiles.default=dev +``` + +Yaml + +``` +spring: + profiles: + default: "dev" +``` + +有关更多信息,请参见“ Spring 引导功能”部分中的“[features.html](features.html#features.profiles)”。 + +### 2.8.根据环境 ### 更改配置 + +Spring 启动支持多文档 YAML 和属性文件(详见[features.html](features.html#features.external-config.files.multi-document)),这些文件可以根据活动的配置文件有条件地被激活。 + +如果文档包含`spring.config.activate.on-profile`键,那么配置文件值(用逗号分隔的配置文件列表或配置文件表达式)将被馈入 Spring `Environment.acceptsProfiles()`方法。如果配置文件表达式匹配,那么该文档将包含在最终的合并中(否则不包括),如以下示例所示: + +属性 + +``` +server.port=9000 +#--- +spring.config.activate.on-profile=development +server.port=9001 +#--- +spring.config.activate.on-profile=production +server.port=0 +``` + +Yaml + +``` +server: + port: 9000 +--- +spring: + config: + activate: + on-profile: "development" +server: + port: 9001 +--- +spring: + config: + activate: + on-profile: "production" +server: + port: 0 +``` + +在前面的示例中,缺省端口是 9000。但是,如果称为“开发”的 Spring 配置文件是活动的,那么端口是 9001。如果“production”是活动的,那么端口是 0。 + +| |文档按照遇到它们的顺序合并。
后面的值覆盖前面的值。| +|---|--------------------------------------------------------------------------------------------------------------| + +### 2.9.发现外部属性的内置选项 ### + +Spring 引导在运行时将`application.properties`(或`.yml`文件和其他地方)的外部属性绑定到应用程序中。在单个位置中不存在(从技术上讲也不可能有)所有受支持的属性的详尽列表,因为贡献可以来自你的 Classpath 上的其他 jar 文件。 + +具有执行器功能的正在运行的应用程序具有`configprops`端点,该端点通过`@Configuration属性`显示所有可用的绑定和绑定属性。 + +附录包括一个[` 申请.财产’](application-properties.html#appendix.application-properties)示例,其中列出了 Spring boot 支持的最常见的属性。最终的列表来自搜索`@Configuration属性`和`@Value`注释的源代码,以及偶尔使用`Binder`。有关加载属性的确切顺序的更多信息,请参见“[features.html](features.html#features.external-config)”。 + +## 3. 嵌入式 Web 服务器 + +Spring 每个引导 Web 应用程序包括嵌入式 Web 服务器。这个特性导致了许多操作问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。这一节回答了这些问题。 + +### 3.1.使用另一个 Web 服务器 + +Spring 许多启动程序都包含默认的嵌入式容器。 + +* 对于 Servlet 堆栈应用程序,`spring-boot-starter-web`通过包括`spring-boot-starter-tomcat`而包括 Tomcat,但是你可以使用`spring-boot-starter-jetty`或`spring-boot-starter-undertow`来代替。 + +* 对于反应性堆栈应用程序,`spring-boot-starter-webflux`通过包括`spring-boot-starter-reactor-netty`而包括了反应器网,但是你可以使用`spring-boot-starter-tomcat`、`spring-boot-starter-jetty`或`spring-boot-starter-undertow`来代替。 + +当切换到不同的 HTTP 服务器时,你需要将缺省依赖项交换为你需要的依赖项。 Spring 为了帮助完成这一过程,Boot 为每个受支持的 HTTP 服务器提供了一个单独的启动器。 + +下面的 Maven 示例显示了如何排除 Tomcat 并包括 Jetty 用于 Spring MVC 的方法: + +``` + + 3.1.0 + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + + org.springframework.boot + spring-boot-starter-jetty + +``` + +| |Servlet API 的版本已被重写,因为与 Tomcat 9 和 Undertow 2 不同, Jetty 9.4 不支持 Servlet 4.0。
如果你希望使用 Jetty 10,它确实支持 Servlet 4.0,请覆盖`jetty.version`属性,而不是`servlet-api.version`属性。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的 Gradle 示例配置了必要的依赖关系和[模块更换](https://docs.gradle.org/current/userguide/resolution_rules.html#sec:module_replacement),以使用 Undertow 代替 Spring WebFlux 的反应堆网络: + +``` +dependencies { + implementation "org.springframework.boot:spring-boot-starter-undertow" + implementation "org.springframework.boot:spring-boot-starter-webflux" + modules { + module("org.springframework.boot:spring-boot-starter-reactor-netty") { + replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Reactor Netty") + } + } +} +``` + +| |`spring-boot-starter-reactor-netty`是使用`WebClient`类所必需的,因此即使需要包含不同的 HTTP 服务器,也可能需要保持对 Netty 的依赖。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.2.禁用 Web 服务器 + +如果你的 Classpath 包含启动 Web 服务器所需的位, Spring 启动将自动启动它。要禁用此行为,请在你的`application.properties`中配置`WebApplicationType`,如以下示例所示: + +属性 + +``` +spring.main.web-application-type=none +``` + +Yaml + +``` +spring: + main: + web-application-type: "none" +``` + +### 3.3.更改 HTTP 端口 + +在独立应用程序中,主 HTTP 端口默认为`8080`,但可以设置为`server.port`(例如,在`application.properties`中或作为系统属性)。由于放松了`Environment`值的绑定,你还可以使用`SERVER_PORT`(例如,作为 OS 环境变量)。 + +要完全关闭 HTTP 端点,但仍然创建`WebApplicationContext`,请使用`server.port=-1`(这样做有时对测试很有用)。 + +有关更多详细信息,请参见“ Spring 引导功能”部分中的“[web.html](web.html#web.servlet.embedded-container.customizing)”,或[“serverproperties”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/Server属性.java)源代码。 + +### 3.4.使用随机未分配的 HTTP 端口 + +要扫描自由端口(使用 OS native 来防止冲突),请使用`server.port=0`。 + +### 3.5.在运行时发现 HTTP 端口 + +你可以从日志输出或从`WebServerApplicationContext`通过其`WebServer`访问服务器运行的端口。要获得这个结果并确保它已被初始化,最好的方法是添加`@Bean`类型的`ApplicationListener`,并在事件发布时将容器从事件中拉出。 + +使用`@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`的测试也可以通过使用`@LocalServerPort`注释将实际端口注入字段,如以下示例所示: + +``` +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class MyWebIntegrationTests { + + @LocalServerPort + int port; + + // ... + +} + +``` + +| |`@LocalServerPort`是`@Value("${local.server.port}")`的元注释。
不要尝试在常规的应用程序中注入端口。
正如我们刚才看到的,只有在容器被初始化之后才会设置该值。
与测试相反,应用程序代码回调是早期处理的(在该值实际可用之前)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.6.启用 HTTP 响应压缩 + +Jetty、 Tomcat 和 Undertow 支持 HTTP 响应压缩。它可以在`application.properties`中启用,如下所示: + +属性 + +``` +server.compression.enabled=true +``` + +Yaml + +``` +server: + compression: + enabled: true +``` + +默认情况下,响应的长度必须至少为 2048 字节,才能执行压缩。你可以通过设置`server.compression.min-response-size`属性来配置此行为。 + +默认情况下,只有当响应的内容类型是以下类型之一时,响应才会被压缩: + +* `text/html` + +* `text/xml` + +* `text/plain` + +* `text/css` + +* `text/javascript` + +* `application/javascript` + +* `application/json` + +* `application/xml` + +你可以通过设置`server.compression.mime-types`属性来配置此行为。 + +### 3.7.配置 SSL + +可以通过设置各种`server.ssl.*`属性来声明性地配置 SSL,通常是在`application.properties`或`application.yml`中。下面的示例显示了在`application.properties`中设置 SSL 属性: + +属性 + +``` +server.port=8443 +server.ssl.key-store=classpath:keystore.jks +server.ssl.key-store-password=secret +server.ssl.key-password=another-secret +``` + +Yaml + +``` +server: + port: 8443 + ssl: + key-store: "classpath:keystore.jks" + key-store-password: "secret" + key-password: "another-secret" +``` + +有关所有支持的属性的详细信息,请参见[`Ssl`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/Ssl.java)。 + +使用像前面示例那样的配置意味着应用程序不再支持端口 8080 的普通 HTTP 连接器。 Spring 引导不支持通过`application.properties`同时配置 HTTP 连接器和 HTTPS 连接器。如果你想同时拥有这两种功能,那么你需要以编程方式配置其中的一种。我们建议使用`application.properties`来配置 HTTPS,因为 HTTP 连接器是两个中更容易通过编程进行配置的。 + +### 3.8.配置 HTTP/2 + +你可以使用`server.http2.enabled`配置属性在 Spring 引导应用程序中启用 HTTP/2 支持。同时支持`h2`(http/2over TLS)和`h2c`(http/2over TCP)。若要使用`h2`,还必须启用 SSL。当未启用 SSL 时,将使用`h2c`。`h2`支持的细节取决于所选的 Web 服务器和应用程序环境,因为所有 JDK8 版本都不支持该协议。 + +#### 3.8.1.HTTP/2with Tomcat + +Spring 在使用 JDK9 或更高版本时,默认情况下,Boot 附带 Tomcat 9.0.x,它支持`h2c`开箱即用和`h2`开箱即用。或者,如果`libtcnative`库及其依赖项安装在主机操作系统上,则`h2`可以在 JDK8 上使用。 + +必须使库目录对 JVM 库路径可用(如果不是已经可用的话)。你可以使用 JVM 参数(如`-Djava.library.path=/usr/local/opt/tomcat-native/lib`)来实现此目的。在[official Tomcat documentation](https://tomcat.apache.org/tomcat-9.0-doc/apr.html)中有更多关于此的内容。 + +在启用了 HTTP/2 和 SSL 的情况下,在 JDK8 上启动 Tomcat 9.0.x,但如果没有本机支持,则会记录以下错误: + +``` +ERROR 8787 --- [ main] o.a.coyote.http11.Http11NioProtocol : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN. +``` + +这个错误并不是致命的,应用程序仍然以 HTTP/1.1SSL 支持启动。 + +#### 3.8.2.HTTP/2with Jetty + +对于 HTTP/2 支持, Jetty 需要额外的`org.eclipse.jetty.http2:http2-server`依赖关系。要使用`h2c`,不需要其他依赖项。要使用`h2`,还需要根据你的部署选择以下依赖项之一: + +* `org.eclipse.jetty:jetty-alpn-java-server`用于运行在 JDK9+ 上的应用程序 + +* `org.eclipse.jetty:jetty-alpn-openjdk8-server`用于在 JDK8U252+ 上运行的应用程序 + +* `org.eclipse.jetty:jetty-alpn-conscrypt-server`和不需要 JDK 的[Conscrypt 图书馆](https://www.conscrypt.org/) + +#### 3.8.3.带有反应堆网络的 HTTP/2 + +`spring-boot-webflux-starter`默认情况下使用 reactor netty 作为服务器。使用 JDK8 或更高版本,Reactor Netty 支持`h2c`,没有额外的依赖关系。使用 JDK9 或更高版本的 JDK 支持,Reactor Netty 支持`h2`。对于 JDK8 环境或最佳运行时性能,此服务器还支持带有本机库的`h2`。要实现这一点,你的应用程序需要有一个额外的依赖关系。 + +Spring 引导管理`io.netty:netty-tcnative-boringssl-static`“UBER jar”的版本,其中包含所有平台的本机库。开发人员可以选择只使用分类器导入所需的依赖项(参见[Netty 官方文档](https://netty.io/wiki/forked-tomcat-native.html))。 + +#### 3.8.4.HTTP/2with Undertow + +截至 Undertow 1.4.0+,`h2`和`h2c`在 JDK8 上都是支持的,没有任何额外的依赖关系。 + +### 3.9.配置 Web 服务器 + +通常,你应该首先考虑使用许多可用配置键中的一个,并通过在`application.properties`或`application.yml`文件中添加新条目来定制 Web 服务器。见“[发现外部属性的内置选项](#howto.properties-and-configuration.discover-build-in-options-for-external-properties)”)。在这里,`server.*`命名空间非常有用,对于服务器特定的特性,它包括`server.tomcat.*`、`server.jetty.*`等命名空间。参见[应用程序-properties.html](application-properties.html#appendix.application-properties)列表。 + +前面的部分已经涵盖了许多常见的用例,例如压缩、SSL 或 HTTP/2。但是,如果你的用例不存在配置键,那么你应该查看[“WebServerFactoryCustomizer”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/web/server/WebServerFactoryCustomizer.html)。你可以声明这样的组件并获得对与你的选择相关的服务器工厂的访问:你应该为所选择的服务器( Tomcat、 Jetty、反应器网络、 Undertow)和所选择的 Web 堆栈( Servlet 或反应式)选择该变体。 + +下面的示例用于使用`spring-boot-starter-web`( Servlet 堆栈)的 Tomcat: + +``` +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer { + + @Override + public void customize(TomcatServletWebServerFactory factory) { + // customize the factory here + } + +} + +``` + +| |Spring boot 在内部使用该基础设施来自动配置服务器。
自动配置的`WebServerFactoryCustomizer`bean 具有`0`的顺序,并且将在任何用户定义的定制程序之前进行处理,除非它具有明确的顺序,该顺序另有规定。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +一旦你使用 Customizer 访问了`WebServerFactory`,就可以使用它来配置特定的部分,例如连接器、服务器资源或服务器本身——所有这些都使用特定于服务器的 API。 + +此外, Spring Boot 还提供: + +| Server | Servlet stack |反应式堆栈| +|--------|---------------------------------|----------------------------------| +| Tomcat | `TomcatServletWebServerFactory` |`TomcatReactiveWebServerFactory`| +| Jetty | `JettyServletWebServerFactory` |`JettyReactiveWebServerFactory`| +|Undertow|`UndertowServletWebServerFactory`|`UndertowReactiveWebServerFactory`| +|Reactor | N/A |`NettyReactiveWebServerFactory`| + +作为最后的手段,你还可以声明你自己的`WebServerFactory` Bean,这将覆盖 Spring Boot 提供的那个。当你这样做时,自动配置的自定义程序仍然应用于你的自定义工厂,因此请小心使用该选项。 + +### 3.10.向应用程序添加 Servlet、过滤器或侦听器 + +在 Servlet 堆栈应用程序中,即使用`spring-boot-starter-web`的情况下,有两种方法可以将`Servlet`、`Filter`、`ServletContextListener`以及 Servlet API 支持的其他侦听器添加到你的应用程序中: + +* [Add a Servlet, Filter, or Listener by Using a Spring Bean](#howto.webserver.add-servlet-filter-listener.spring-bean) + +* [Add Servlets, Filters, and Listeners by Using Classpath Scanning](#howto.webserver.add-servlet-filter-listener.using-scanning) + +#### 3.10.1.通过使用 Spring Bean #### 来添加 Servlet、过滤器或侦听器 + +要通过使用 Spring Bean 来添加`Servlet`、`Filter`或 Servlet `*Listener`,你必须为它提供一个`@Bean`定义。当你想要注入配置或依赖项时,这样做会非常有用。但是,你必须非常小心,不要让它们引起太多其他 bean 的急于初始化,因为它们必须在应用程序生命周期的早期安装到容器中。(例如,让它们依赖于你的`DataSource`或 JPA 配置不是一个好主意。)你可以通过在首次使用 bean 时而不是在初始化时懒洋洋地初始化 bean 来解决这些限制。 + +在过滤器和 servlet 的情况下,你还可以通过添加`FilterRegistrationBean`或`ServletRegistrationBean`来添加映射和 init 参数,而不是添加或添加到基础组件中。 + +| |如果在过滤器注册中没有指定`dispatcherType`,则使用`REQUEST`。
这与 Servlet 规范的默认 Dispatcher 类型一致。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------| + +像任何其他 Spring Bean 一样,你可以定义 Servlet 过滤器 bean 的顺序;请确保检查“[web.html](web.html#web.servlet.embedded-container.servlets-filters-listeners.beans)”部分。 + +##### 禁用 Servlet 或过滤器的注册 ##### + +作为[前面描述的](#howto.webserver.add-servlet-filter-listener.spring-bean),任何`Servlet`或`Filter`bean 都会自动注册到 Servlet 容器中。要禁用特定`Filter`或`Servlet` Bean 的注册,请为其创建注册 Bean 并将其标记为禁用,如以下示例所示: + +``` +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyFilterConfiguration { + + @Bean + public FilterRegistrationBean registration(MyFilter filter) { + FilterRegistrationBean registration = new FilterRegistrationBean<>(filter); + registration.setEnabled(false); + return registration; + } + +} + +``` + +#### 3.10.2.使用 Classpath 扫描 #### 添加 servlet、过滤器和侦听器 # + +`@WebServlet`,`@WebFilter`,和`@WebListener`注解的类可以通过注解一个`@Configuration`类和`@ServletComponentScan`类,并指定包含你想要注册的组件的包,自动地在嵌入式 Servlet 容器中注册。默认情况下,`@ServletComponentScan`从带注释的类的包中扫描。 + +### 3.11.配置访问日志 + +访问日志可以通过 Tomcat、 Undertow 和 Jetty 各自的名称空间进行配置。 + +例如,下面的设置使用[custom pattern](https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Access_伐木)对 Tomcat 进行日志访问。 + +属性 + +``` +server.tomcat.basedir=my-tomcat +server.tomcat.accesslog.enabled=true +server.tomcat.accesslog.pattern=%t %a %r %s (%D ms) +``` + +Yaml + +``` +server: + tomcat: + basedir: "my-tomcat" + accesslog: + enabled: true + pattern: "%t %a %r %s (%D ms)" +``` + +| |日志的默认位置是相对于 Tomcat 基本目录的`logs`目录,
默认情况下,`logs`目录是一个临时目录,因此你可能希望修复 Tomcat 的基本目录,或者对日志使用一个绝对路径,
在前面的示例中,相对于应用程序的工作目录,日志在`my-tomcat/logs`中可用。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Undertow 的访问日志记录可以以类似的方式进行配置,如以下示例所示: + +属性 + +``` +server.undertow.accesslog.enabled=true +server.undertow.accesslog.pattern=%t %a %r %s (%D ms) +``` + +Yaml + +``` +server: + undertow: + accesslog: + enabled: true + pattern: "%t %a %r %s (%D ms)" +``` + +日志相对于应用程序的工作目录存储在`logs`目录中。你可以通过设置`server.undertow.accesslog.dir`属性来定制此位置。 + +最后, Jetty 的访问日志也可以配置如下: + +属性 + +``` +server.jetty.accesslog.enabled=true +server.jetty.accesslog.filename=/var/log/jetty-access.log +``` + +Yaml + +``` +server: + jetty: + accesslog: + enabled: true + filename: "/var/log/jetty-access.log" +``` + +默认情况下,日志被重定向到`System.err`。有关更多详细信息,请参见 Jetty 文档。 + +### 3.12.运行在前端代理服务器后面 + +如果你的应用程序运行在代理、负载均衡器或云中,那么请求信息(如主机、端口、方案……)可能会在此过程中发生变化。你的应用程序可能运行在`10.10.10.10:8080`上,但是 HTTP 客户端应该只看到`example.org`。 + +[RFC7239“转发头”](https://tools.ietf.org/html/rfc7239)定义了`Forwarded`HTTP 报头;代理可以使用这个报头来提供关于原始请求的信息。你可以将你的应用程序配置为读取这些标头,并在创建链接并将其发送到 HTTP302 响应、JSON 文档或 HTML 页面中的客户端时自动使用这些信息。也有非标准的标题,如`X-Forwarded-Host`,`X-Forwarded-Port`,`X-Forwarded-Proto`,`X-Forwarded-Ssl`,和`X-Forwarded-Prefix`。 + +如果代理添加了常用的`X-Forwarded-For`和`X-Forwarded-Proto`头,则将`server.forward-headers-strategy`设置为`NATIVE`就足以支持这些。有了这个选项,Web 服务器本身就支持这个特性;你可以查看它们的特定文档来了解特定的行为。 + +如果这还不够, Spring 框架提供了[ForwardedHeaderFilter](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#filters-forwarded-headers)。通过将`server.forward-headers-strategy`设置为`FRAMEWORK`,可以将其注册为应用程序中的 Servlet 过滤器。 + +| |如果你正在使用 Tomcat 并在代理上终止 SSL,则`server.tomcat.redirect-context-root`应该设置为`false`。
这允许在执行任何重定向之前执行`X-Forwarded-Proto`头。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你的应用程序在 Cloud Foundry 或 Heroku 中运行,则`server.forward-headers-strategy`属性默认为`NATIVE`。在所有其他实例中,它默认为
。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 3.12.1.自定义 Tomcat 的代理配置 + +如果使用 Tomcat,则可以另外配置用于携带“转发”信息的标题的名称,如以下示例所示: + +属性 + +``` +server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header +server.tomcat.remoteip.protocol-header=x-your-protocol-header +``` + +Yaml + +``` +server: + tomcat: + remoteip: + remote-ip-header: "x-your-remote-ip-header" + protocol-header: "x-your-protocol-header" +``` + +Tomcat 还配置有与要被信任的内部代理相匹配的默认正则表达式。默认情况下,`10/8`、`192.168/16`、`169.254/16`和`127/8`中的 IP 地址是受信任的。你可以通过向`application.properties`添加一个条目来定制阀门的配置,如下例所示: + +属性 + +``` +server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3} +``` + +Yaml + +``` +server: + tomcat: + remoteip: + internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}" +``` + +| |你可以通过将`internal-proxies`设置为空来信任所有代理(但在生产中不要这样做)。| +|---|------------------------------------------------------------------------------------------------------| + +通过关闭自动关闭(为此,请设置`server.forward-headers-strategy=NONE`)并使用`WebServerFactoryCustomizer` Bean 添加一个新的阀门实例,可以完全控制 Tomcat 的`RemoteIpValve`的配置。 + +### 3.13. Tomcat 启用多个连接器 + +你可以将`org.apache.catalina.connector.Connector`添加到`TomcatServletWebServerFactory`,这可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如以下示例所示: + +``` +import java.io.IOException; +import java.net.URL; + +import org.apache.catalina.connector.Connector; +import org.apache.coyote.http11.Http11NioProtocol; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ResourceUtils; + +@Configuration(proxyBeanMethods = false) +public class MyTomcatConfiguration { + + @Bean + public WebServerFactoryCustomizer sslConnectorCustomizer() { + return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createSslConnector()); + } + + private Connector createSslConnector() { + Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); + Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); + try { + URL keystore = ResourceUtils.getURL("keystore"); + URL truststore = ResourceUtils.getURL("truststore"); + connector.setScheme("https"); + connector.setSecure(true); + connector.setPort(8443); + protocol.setSSLEnabled(true); + protocol.setKeystoreFile(keystore.toString()); + protocol.setKeystorePass("changeit"); + protocol.setTruststoreFile(truststore.toString()); + protocol.setTruststorePass("changeit"); + protocol.setKeyAlias("apitester"); + return connector; + } + catch (IOException ex) { + throw new IllegalStateException("Fail to create ssl connector", ex); + } + } + +} + +``` + +### 3.14.使用 Tomcat 的 LegacyCookieProcessor + +默认情况下, Spring 引导所使用的嵌入式 Tomcat 不支持 cookie 格式的“版本 0”,因此你可能会看到以下错误: + +``` +java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value +``` + +如果可能的话,你应该考虑更新你的代码,以便只存储符合后来的 Cookie 规范的值。但是,如果不能更改 cookie 的编写方式,则可以将 Tomcat 配置为使用`LegacyCookieProcessor`。要切换到`LegacyCookieProcessor`,请使用一个`WebServerFactoryCustomizer` Bean,它会添加一个`TomcatContextCustomizer`,如以下示例所示: + +``` +import org.apache.tomcat.util.http.LegacyCookieProcessor; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyLegacyCookieProcessorConfiguration { + + @Bean + public WebServerFactoryCustomizer cookieProcessorCustomizer() { + return (factory) -> factory + .addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor())); + } + +} + +``` + +### 3.15.启用 Tomcat 的 MBean 注册中心 + +默认情况下,嵌入式 Tomcat 的 MBean 注册中心被禁用。这最大限度地减少了 Tomcat 的内存占用。如果你希望使用 Tomcat 的 MBean,以便 Micrometer 可以使用它们来公开度量,那么你必须使用`server.tomcat.mbeanregistry.enabled`属性来这样做,如以下示例所示: + +属性 + +``` +server.tomcat.mbeanregistry.enabled=true +``` + +Yaml + +``` +server: + tomcat: + mbeanregistry: + enabled: true +``` + +### 3.16.使用 Undertow 启用多个侦听器 + +向`UndertowServletWebServerFactory`添加`UndertowBuilderCustomizer`,并向`Builder`添加侦听器,如以下示例所示: + +``` +import io.undertow.Undertow.Builder; + +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyUndertowConfiguration { + + @Bean + public WebServerFactoryCustomizer undertowListenerCustomizer() { + return (factory) -> factory.addBuilderCustomizers(this::addHttpListener); + } + + private Builder addHttpListener(Builder builder) { + return builder.addHttpListener(8080, "0.0.0.0"); + } + +} + +``` + +### 3.17.使用 @serverendPoint## 创建 WebSocket 端点 + +如果要在使用嵌入式容器的 Spring 引导应用程序中使用`@ServerEndpoint`,则必须声明单个`ServerEndpointExporter``@Bean`,如以下示例所示: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration(proxyBeanMethods = false) +public class MyWebSocketConfiguration { + + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + +} + +``` + +Bean 在前面的示例中所示的任何`@ServerEndpoint`注释的 bean 与底层 WebSocket 容器一起注册。当部署到独立的 Servlet 容器时,该角色由 Servlet 容器初始化器执行,并且不需要`ServerEndpointExporter` Bean。 + +## 4. Spring MVC + +Spring 启动具有包括 Spring MVC 的许多启动器。请注意,一些启动器包括对 Spring MVC 的依赖,而不是直接包括它。这一部分回答了关于 Spring MVC 和 Spring Boot 的常见问题。 + +### 4.1.编写 JSON REST 服务 + +Spring 在 Spring 引导应用程序中的任何 Spring 默认情况下都应该呈现 JSON 响应,只要 Jackson2 在 Classpath 上,如以下示例所示: + +``` +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class MyController { + + @RequestMapping("/thing") + public MyThing thing() { + return new MyThing(); + } + +} + +``` + +只要`MyThing`可以被 Jackson2 序列化(对于正常的 POJO 或 Groovy 对象为真),那么`[localhost:8080/thing](http://localhost:8080/thing)`默认情况下就提供了它的 JSON 表示。请注意,在浏览器中,你有时可能会看到 XML 响应,因为浏览器倾向于发送更喜欢 XML 的 Accept 头。 + +### 4.2.编写 XML REST 服务 + +如果在 Classpath 上有 Jackson 的 XML 扩展(`Jackson-DataFormat-XML`),则可以使用它来呈现 XML 响应。我们为 JSON 使用的前一个示例将起作用。要使用 Jackson 的 XML 渲染器,请向项目添加以下依赖项: + +``` + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + +``` + +如果 Jackson 的 XML 扩展是不可用的,而 JAXB 是可用的,则可以使用附加的要求将`MyThing`注释为`@XmlRootElement`来呈现 XML,如以下示例所示: + +``` +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +public class MyThing { + + private String name; + + // getters/setters ... + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} + +``` + +JAXB 只能在 Java8 的开箱即用中使用。如果你使用的是新一代的 Java,那么可以在项目中添加以下依赖项: + +``` + + org.glassfish.jaxb + jaxb-runtime + +``` + +| |要让服务器呈现 XML 而不是 JSON,你可能必须发送`Accept: text/xml`报头(或使用浏览器)。| +|---|------------------------------------------------------------------------------------------------------------------------| + +### 4.3.自定义 JacksonObjectMapper + +Spring MVC(客户端和服务器端)使用`HttpMessageConverters`在 HTTP 交换中协商内容转换。如果 Jackson 位于 Classpath 上,则你已经获得了由`Jackson2ObjectMapperBuilder`提供的默认转换器,其实例是为你自动配置的。 + +`ObjectMapper`(或`XmlMapper`用于 JacksonXML 转换器)实例(默认情况下创建)具有以下定制属性: + +* `MapperFeature.DEFAULT_VIEW_INCLUSION`已禁用 + +* `DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES`已禁用 + +* `SerializationFeature.WRITE_DATES_AS_TIMESTAMPS`已禁用 + +Spring 引导还具有一些功能,以使其更容易自定义这种行为。 + +你可以通过使用环境来配置`ObjectMapper`和`XmlMapper`实例。Jackson 提供了一套广泛的 On/Off 功能,可用于配置其处理的各个方面。这些特征以六个枚举(在 Jackson 中)进行描述,这些枚举映射到环境中的属性上: + +| Enum | Property |价值观| +|-------------------------------------------------------|-----------------------------------------------|--------------------------------------------------------------| +|`com.fasterxml.jackson.databind.DeserializationFeature`|`spring.jackson.deserialization.`|`true`, `false`| +| `com.fasterxml.jackson.core.JsonGenerator.Feature` | `spring.jackson.generator.` |`true`, `false`| +| `com.fasterxml.jackson.databind.MapperFeature` | `spring.jackson.mapper.` |`true`, `false`| +| `com.fasterxml.jackson.core.JsonParser.Feature` | `spring.jackson.parser.` |`true`, `false`| +| `com.fasterxml.jackson.databind.SerializationFeature` | `spring.jackson.serialization.` |`true`, `false`| +|`com.fasterxml.jackson.annotation.JsonInclude.Include` | `spring.jackson.default-property-inclusion` |`always`, `non_null`, `non_absent`, `non_default`, `non_empty`| + +例如,要启用 pretty print,请设置`spring.jackson.serialization.indent_output=true`。注意,由于使用了[relaxed binding](features.html#features.external-config.typesafe-configuration-properties.relaxed-binding),`indent_output`的情况不必匹配对应的枚举常数的情况,即`INDENT_OUTPUT`。 + +这种基于环境的配置应用于自动配置的`Jackson2ObjectMapperBuilder` Bean 并应用于通过使用构建器创建的任何映射器,包括自动配置的`ObjectMapper` Bean。 + +上下文的`Jackson2ObjectMapperBuilder`可以由一个或多个`Jackson2ObjectMapperBuilderCustomizer`bean 自定义。可以订购这样的定制程序 bean(Boot 自己的定制程序的订单为 0),从而在 Boot 的定制之前和之后都可以应用额外的定制程序。 + +类型`com.fasterxml.jackson.databind.Module`的任何 bean 都会自动注册到自动配置的`Jackson2ObjectMapperBuilder`中,并应用于它创建的任何`ObjectMapper`实例。这提供了一种全局机制,用于在向应用程序添加新特性时贡献自定义模块。 + +如果要完全替换缺省的`ObjectMapper`,可以定义该类型的`@Bean`并将其标记为`@Primary`,或者,如果你更喜欢基于构建器的方法,可以定义`Jackson2ObjectMapperBuilder``@Bean`。请注意,在这两种情况下,这样做都会禁用`ObjectMapper`的所有自动配置。 + +如果你提供任何`@Beans`类型的`MappingJackson2HttpMessageConverter`,它们将替换 MVC 配置中的默认值。此外,还提供了`HttpMessageConverters`类型的方便 Bean(如果使用默认的 MVC 配置,则始终可用)。它有一些有用的方法来访问默认的和用户增强的消息转换器。 + +有关更多详细信息,请参见“[自定义 @responsebody 呈现](#howto.spring-mvc.customize-responsebody-rendering)”部分和[“WebMVCAutoConfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java)源代码。 + +### 4.4.自定义 @responsebody 呈现 + +Spring 使用`HttpMessageConverters`来呈现`@ResponseBody`(或来自`@RestController`的响应)。你可以通过在 Spring 引导上下文中添加适当类型的 bean 来贡献额外的转换器。如果你添加的 Bean 是一种无论如何默认情况下都会包含的类型(例如用于 JSON 转换的`MappingJackson2HttpMessageConverter`),那么它将替换默认值。 Bean 类型`HttpMessageConverters`的便利是提供的,并且如果你使用默认的 MVC 配置,它总是可用的。它有一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果你想手动将它们注入到自定义的`RestTemplate`中,它可能是有用的)。 + +正如在正常的 MVC 使用中一样,你提供的任何`WebMvcConfigurer`bean 也可以通过覆盖`configureMessageConverters`方法来贡献转换器。然而,与普通的 MVC 不同,你只能提供所需的额外转换器(因为 Spring Boot 使用相同的机制来贡献其默认值)。最后,如果你通过提供你自己的`@EnableWebMvc`配置来 OPT 出 Spring 引导默认的 MVC 配置,那么你可以通过使用`getMessageConverters`from`WebMvcConfigurationSupport`完全控制并手动执行所有操作。 + +有关更多详细信息,请参见[“WebMVCAutoConfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java)源代码。 + +### 4.5.处理多部分文件上传 + +Spring Boot 包含 Servlet 3`javax.servlet.http.Part`API 以支持上载文件。默认情况下, Spring 引导配置 Spring MVC,在单个请求中,每个文件的最大大小为 1MB,文件数据的最大大小为 10MB。你可以使用`Multipart属性`类中公开的属性重写这些值、存储中间数据的位置(例如,存储到`/tmp`目录)以及将数据刷新到磁盘的阈值。例如,如果你想指定文件是无限的,那么将`spring.servlet.multipart.max-file-size`属性设置为`-1`。 + +当你希望在 Spring MVC 控制器处理程序方法中以`@RequestParam`类型的`MultipartFile`注释参数的形式接收多部分编码的文件数据时,多部分支持是有帮助的。 + +有关更多详细信息,请参见[“多元构型”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfiguration.java)源代码。 + +| |建议对多部分上传使用容器的内置支持,而不是引入额外的依赖关系,如 Apache Commons 文件上传。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.6.关闭 Spring MVC DispatcherServlet + +默认情况下,所有内容都是从应用程序的根目录提供的。如果你更愿意映射到另一条路径,那么可以按以下方式配置一条路径: + +Properties + +``` +spring.mvc.servlet.path=/mypath +``` + +Yaml + +``` +spring: + mvc: + servlet: + path: "/mypath" +``` + +如果你有额外的 servlet,你可以为每个 servlet 声明`@Bean`或`ServletRegistrationBean`类型的`@Bean`,并且 Spring 引导将透明地将它们注册到容器中。因为 servlet 是以这种方式注册的,所以它们可以映射到`DispatcherServlet`的子上下文,而无需调用它。 + +自己配置`DispatcherServlet`是不寻常的,但是如果你确实需要这样做,则必须提供类型`DispatcherServletPath`的`@Bean`,以提供自定义`DispatcherServlet`的路径。 + +### 4.7.关闭默认的 MVC 配置 + +要完全控制 MVC 配置,最简单的方法是使用`@EnableWebMvc`注释提供自己的`@Configuration`。这样做,所有的 MVC 配置都在你的手中。 + +### 4.8.自定义视解析程序 + +a`ViewResolver`是 Spring MVC 的核心组件,将`@Controller`中的视图名称转换为实际的`View`实现。请注意,`ViewResolvers`主要用于 UI 应用程序,而不是 REST 风格的服务(a`View`不用于呈现 a`@ResponseBody`)。有许多`ViewResolver`的实现方式可供选择,并且 Spring 本身并不确定应该使用哪些实现方式。 Spring 另一方面,引导为你安装一个或两个,这取决于它在 Classpath 和应用程序上下文中发现的内容。`DispatcherServlet`使用它在应用程序上下文中找到的所有解析器,依次尝试每个解析器,直到得到一个结果。如果你添加了你自己的,那么你必须知道你的解析器添加的顺序和位置。 + +`WebMvcAutoConfiguration`将以下`ViewResolvers`添加到上下文中: + +* 名为“defaultViewResolver”的`InternalResourceViewResolver`。这一个定位可以通过使用`DefaultServlet`呈现的物理资源(如果你使用静态资源和 JSP 页面,则包括静态资源和 JSP 页面)。它将一个前缀和后缀应用到视图名,然后在 Servlet 上下文中查找具有该路径的物理资源(默认值都是空的,但是可以通过`spring.mvc.view.prefix`和`spring.mvc.view.suffix`进行外部配置)。你可以通过提供相同类型的 Bean 来覆盖它。 + +* 一个名为“BeannameViewResolver”的`BeanNameViewResolver`。这是视图解析器链中的一个有用的成员,并获取与正在解析的`View`同名的任何 bean。不需要重写或替换它。 + +* 只有当存在**Are**类型的 bean 时,才会添加名为“viewresolver”的`ContentNegotiatingViewResolver`。这是一个复合解析器,将其委托给所有其他解析器,并试图找到与客户机发送的“Accept”HTTP 头匹配的项。有一个有用的[blog about `ContentNegotiatingViewResolver`](https://spring.io/blog/2013/06/03/content-negotiation-using-views),你可能想学习更多,你也可以查看源代码了解详细信息。你可以通过定义一个名为“ViewResolver”的 Bean 来关闭自动配置的`ContentNegotiatingViewResolver`。 + +* 如果你使用 ThymeLeaf,那么你还有一个名为“ThymeLeafViewResolver”的`ThymeleafViewResolver`。它通过使用前缀和后缀环绕视图名来查找资源。前缀是`spring.thymeleaf.prefix`,后缀是`spring.thymeleaf.suffix`。前缀和后缀的值分别默认为“ Classpath:/templates/”和“.html”。你可以通过提供同名的 Bean 来覆盖`ThymeleafViewResolver`。 + +* 如果使用 Freemarker,还会有一个名为“freemarkerviewresolver”的`FreeMarkerViewResolver`。通过使用前缀和后缀围绕视图名,它在加载程序路径(外部化为`spring.freemarker.templateLoaderPath`,并具有默认值‘ Classpath:/templates/’)中查找资源。前缀外部化为`spring.freemarker.prefix`,后缀外部化为`spring.freemarker.suffix`。前缀和后缀的默认值分别为空和“.ftlh”。你可以通过提供同名的 Bean 来覆盖`FreeMarkerViewResolver`。 + +* 如果你使用 Groovy 模板(实际上,如果`groovy-templates`在你的 Classpath 上),那么你还有一个名为“GroovyMarkupViewResolver”的`GroovyMarkupViewResolver`。它通过使用前缀和后缀(外部化为`spring.groovy.template.prefix`和`spring.groovy.template.suffix`)包围视图名,在加载程序路径中查找资源。前缀和后缀的默认值分别为“ Classpath:/templates/”和“.TPL”。可以通过提供同名的 Bean 来覆盖`GroovyMarkupViewResolver`。 + +* 如果你使用 Mustache,你也有一个`MustacheViewResolver`名为“MustacheViewResolver”。它通过使用前缀和后缀环绕视图名来查找资源。前缀是`spring.mustache.prefix`,后缀是`spring.mustache.suffix`。前缀和后缀的值分别默认为“ Classpath:/templates/”和“.mustache”。你可以通过提供同名的 Bean 来覆盖`MustacheViewResolver`。 + +有关更多详细信息,请参见以下部分: + +* [“WebMVCAutoConfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java) + +* [胸腺叶自动构型](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java) + +* [FreemarkerAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java) + +* [GroovytemplateAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java) + +## 5. 泽西岛 + +### 5.1.使用 Spring 安全性保护 Jersey 端点 + +Spring 安全性可用于保护基于 Jersey 的 Web 应用程序,其方式与其可用于保护基于 Spring MVC 的 Web 应用程序的方式大致相同。但是,如果你希望在 Jersey 中使用 Spring Security 的方法级安全性,则必须将 Jersey 配置为使用`setStatus(int)`而不是`sendError(int)`。这可以防止 Jersey 在 Spring Security 有机会向客户端报告身份验证或授权失败之前提交响应。 + +在应用程序的`ResourceConfig` Bean 上,`jersey.config.server.response.setStatusOverSendError`属性必须设置为`true`,如以下示例所示: + +``` +import java.util.Collections; + +import org.glassfish.jersey.server.ResourceConfig; + +import org.springframework.stereotype.Component; + +@Component +public class JerseySetStatusOverSendErrorConfig extends ResourceConfig { + + public JerseySetStatusOverSendErrorConfig() { + register(Endpoint.class); + setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true)); + } + +} + +``` + +### 5.2.在使用另一个 Web 框架的同时使用 Jersey + +要将 Jersey 与另一个 Web 框架(例如 Spring MVC)一起使用,应该对其进行配置,以便允许另一个框架处理它无法处理的请求。首先,通过配置`spring.jersey.type`值为`filter`的应用程序属性,将 Jersey 配置为使用过滤器而不是 Servlet。其次,将你的`ResourceConfig`配置为转发会导致 404 的请求,如下面的示例所示。 + +``` +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletProperties; + +import org.springframework.stereotype.Component; + +@Component +public class JerseyConfig extends ResourceConfig { + + public JerseyConfig() { + register(Endpoint.class); + property(ServletProperties.FILTER_FORWARD_ON_404, true); + } + +} + +``` + +## 6. HTTP 客户端 + +Spring Boot 提供了许多与 HTTP 客户端一起工作的启动器。本节回答与使用它们有关的问题。 + +### 6.1.配置 RESTTemplate 以使用代理 + +如[io.html](io.html#io.rest-client.resttemplate.customization)中所述,可以使用`RestTemplateCustomizer`和`RestTemplateBuilder`来构建定制的`RestTemplate`。这是创建配置为使用代理的`RestTemplate`的推荐方法。 + +代理配置的确切细节取决于所使用的底层客户机请求工厂。 + +### 6.2.配置由基于反应堆网络的 WebClient 使用的 TCPClient### + +当反应堆网络是在 Classpath 上时,基于反应堆网络的`WebClient`是自动配置的。要定制客户机对网络连接的处理,请提供`ClientHttpConnector` Bean。下面的示例配置了 60 秒的连接超时,并添加了`ReadTimeoutHandler`: + +``` +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import reactor.netty.http.client.HttpClient; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.http.client.reactive.ReactorResourceFactory; + +@Configuration(proxyBeanMethods = false) +public class MyReactorNettyClientConfiguration { + + @Bean + ClientHttpConnector clientHttpConnector(ReactorResourceFactory resourceFactory) { + HttpClient httpClient = HttpClient.create(resourceFactory.getConnectionProvider()) + .runOn(resourceFactory.getLoopResources()) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000) + .doOnConnected((connection) -> connection.addHandlerLast(new ReadTimeoutHandler(60))); + return new ReactorClientHttpConnector(httpClient); + } + +} + +``` + +| |注意对连接提供程序和事件循环资源使用`ReactorResourceFactory`。
这确保了接收请求的服务器和发出请求的客户机有效地共享资源。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 7. Logging + +Spring 启动没有强制的日志依赖关系,除了 Commons 日志 API,它通常由 Spring Framework 的`spring-jcl`模块提供。要使用[Logback](https://logback.qos.ch),需要在 Classpath 上包含它和`spring-jcl`。推荐的实现方法是通过启动器,所有启动器都依赖于`spring-boot-starter-logging`。对于 Web 应用程序,你只需要`spring-boot-starter-web`,因为它在传递上依赖于日志启动器。如果你使用 Maven,则以下依赖项将为你添加日志记录: + +``` + + org.springframework.boot + spring-boot-starter-web + +``` + +Spring 引导具有`LoggingSystem`的抽象,该抽象尝试基于 Classpath 的内容来配置日志记录。如果可以登录,它是第一个选择。 + +如果你需要对日志记录进行的唯一更改是设置各种日志记录器的级别,那么你可以在`application.properties`中使用“logging.level”前缀进行更改,如下例所示: + +Properties + +``` +logging.level.org.springframework.web=debug +logging.level.org.hibernate=error +``` + +Yaml + +``` +logging: + level: + org.springframework.web: "debug" + org.hibernate: "error" +``` + +还可以使用`logging.file.name`设置要将日志写入的文件的位置(除了控制台)。 + +要配置日志系统的更细粒度的设置,你需要使用所讨论的`LoggingSystem`所支持的本机配置格式。默认情况下, Spring boot 会从系统的默认位置(例如`classpath:logback.xml`用于回传)获取本机配置,但是你可以通过使用`logging.config`属性设置配置文件的位置。 + +### 7.1.配置日志记录的回录 + +如果你需要在`application.properties`所能实现的范围之外,对回录应用自定义,那么你将需要添加一个标准的回录配置文件。你可以将`logback.xml`文件添加到你的 Classpath 根目录中,以便进行回登查找。如果你想使用[Spring Boot Logback extensions](features.html#features.logging.logback-extensions),也可以使用`logback-spring.xml`。 + +| |在一些细节上,注销文档有一个[包含配置的专用部分](https://logback.qos.ch/manual/configuration.html)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------| + +Spring Boot 提供了许多从你自己的配置中执行`included`的回登配置。这些包括被设计为允许某些公共 Spring 引导约定被重新应用。 + +以下文件在`org/springframework/boot/logging/logback/`下提供: + +* `defaults.xml`-提供转换规则、模式属性和公共记录器配置。 + +* `console-appender.xml`-使用`CONSOLE_LOG_PATTERN`添加`ConsoleAppender`。 + +* `file-appender.xml`-在适当的设置下使用`FILE_LOG_PATTERN`和`ROLLING_FILE_NAME_PATTERN`添加`RollingFileAppender`。 + +此外,还提供了一个遗留的`base.xml`文件,以便与 Spring boot 的早期版本兼容。 + +一个典型的自定义`logback.xml`文件看起来是这样的: + +``` + + + + + + + + + +``` + +你的回录配置文件还可以使用`LoggingSystem`为你创建的系统属性: + +* `${PID}`:当前进程 ID。 + +* `${LOG_FILE}`:在 Boot 的外部配置中是否设置了`logging.file.name`。 + +* `${LOG_PATH}`:是否在 Boot 的外部配置中设置了`logging.file.path`(代表用于保存日志文件的目录)。 + +* `${LOG_EXCEPTION_CONVERSION_WORD}`:在 Boot 的外部配置中是否设置了`logging.exception-conversion-word`。 + +* `${ROLLING_FILE_NAME_PATTERN}`:在 Boot 的外部配置中是否设置了`logging.pattern.rolling-file-name`。 + +Spring 启动还通过使用定制的回录转换器在控制台上(但不是在日志文件中)提供一些漂亮的 ANSI 彩色终端输出。有关示例,请参见`defaults.xml`配置中的`CONSOLE_LOG_PATTERN`。 + +如果 Groovy 位于 Classpath 上,那么你也应该能够使用`logback.groovy`配置回发。如果存在,此设置将被优先考虑。 + +| |Spring Groovy 配置不支持扩展名。
任何`logback-spring.groovy`文件都不会被检测到。| +|---|--------------------------------------------------------------------------------------------------------------------------| + +#### 7.1.1.为仅文件输出配置回录 + +如果要禁用控制台日志记录并只将输出写到文件,则需要一个自定义的`logback-spring.xml`,它导入`file-appender.xml`,但不导入`console-appender.xml`,如下例所示: + +``` + + + + + + + + + +``` + +你还需要将`logging.file.name`添加到你的`application.properties`或`application.yaml`中,如以下示例所示: + +Properties + +``` +logging.file.name=myapplication.log +``` + +Yaml + +``` +logging: + file: + name: "myapplication.log" +``` + +### 7.2.为日志配置 log4j + +Spring 如果在 Classpath 上,则启动支持[Log4j 2](https://logging.apache.org/log4j/2.x/)用于日志配置。如果你使用启动器来组装依赖项,那么你必须排除注销,然后包括 log4j2。如果不使用启动器,则除了 log4j2 之外,还需要提供(至少)`spring-jcl`。 + +推荐的路径是通过启动器,尽管它需要进行一些微调。下面的示例显示了如何在 Maven 中设置启动器: + +``` + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + +``` + +Gradle 提供了几种不同的方式来设置启动器。一种方法是使用[模块更换](https://docs.gradle.org/current/userguide/resolution_rules.html#sec:module_replacement)。为此,在 log4j2starter 上声明一个依赖项,并告诉 Gradle 默认的日志启动器的任何出现都应该被 log4j2starter 替换,如下面的示例所示: + +``` +dependencies { + implementation "org.springframework.boot:spring-boot-starter-log4j2" + modules { + module("org.springframework.boot:spring-boot-starter-logging") { + replacedBy("org.springframework.boot:spring-boot-starter-log4j2", "Use Log4j2 instead of Logback") + } + } +} +``` + +| |log4j 启动器将常见日志需求的依赖关系收集在一起(例如 Tomcat 使用`java.util.logging`但使用 log4j2 配置输出)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |要确保使用`java.util.logging`执行的调试日志被路由到 log4j2 中,可以通过将`java.util.logging.manager`系统属性设置为`org.apache.logging.log4j.jul.LogManager`来配置其[JDK 日志适配器](https://logging.apache.org/log4j/2.x/log4j-jul/index.html)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 7.2.1.使用 YAML 或 JSON 配置 log4j2 + +除了其默认的 XML 配置格式,Log4j2 还支持 YAML 和 JSON 配置文件。要将 log4j2 配置为使用替代的配置文件格式,请向 Classpath 添加适当的依赖项,并将配置文件命名为与所选文件格式匹配的文件,如以下示例所示: + +|Format|依赖关系| File names | +|------|----------------------------------------------------------------------------------------------------------|----------------------------| +| YAML |`com.fasterxml.jackson.core:jackson-databind` + `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml`|`log4j2.yaml` + `log4j2.yml`| +| JSON |`com.fasterxml.jackson.core:jackson-databind`|`log4j2.json` + `log4j2.jsn`| + +#### 7.2.2.使用复合配置配置来配置 log4j2 + +log4j2 支持将多个配置文件合并为一个复合配置。要在 Spring 引导中使用这种支持,可以使用一个或多个辅助配置文件的位置配置`logging.log4j2.config.override`。辅助配置文件将与主配置合并,无论主配置的源是 Spring boot 的默认值、标准位置(如`log4j.xml`),还是由`logging.config`属性配置的位置。 + +## 8. 数据访问 + +Spring 引导包括许多用于处理数据源的启动器。本节回答与此相关的问题。 + +### 8.1.配置自定义数据源 + +要配置自己的`DataSource`,请在配置中定义该类型的`@Bean`。 Spring 启动在需要的任何地方重用你的`DataSource`,包括数据库初始化。如果需要外部化某些设置,可以将`DataSource`绑定到环境(请参见“[features.html](features.html#features.external-config.typesafe-configuration-properties.third-party-configuration)”)。 + +下面的示例展示了如何在 Bean 中定义数据源: + +``` +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyDataSourceConfiguration { + + @Bean + @ConfigurationProperties(prefix = "app.datasource") + public SomeDataSource dataSource() { + return new SomeDataSource(); + } + +} + +``` + +下面的示例展示了如何通过设置属性来定义数据源: + +Properties + +``` +app.datasource.url=jdbc:h2:mem:mydb +app.datasource.username=sa +app.datasource.pool-size=30 +``` + +Yaml + +``` +app: + datasource: + url: "jdbc:h2:mem:mydb" + username: "sa" + pool-size: 30 +``` + +假设`SomeDataSource`具有 URL、用户名和池大小的常规 JavaBean 属性,则在`DataSource`对其他组件可用之前,将自动绑定这些设置。 + +Spring Boot 还提供了一种实用程序生成器类,称为,其可用于创建标准数据源之一(如果它在 Classpath 上)。构建器可以基于在 Classpath 上可用的内容来检测要使用的一个。它还基于 JDBC URL 自动检测驱动程序。 + +下面的示例展示了如何使用`DataSourceBuilder`创建数据源: + +``` +import javax.sql.DataSource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyDataSourceConfiguration { + + @Bean + @ConfigurationProperties("app.datasource") + public DataSource dataSource() { + return DataSourceBuilder.create().build(); + } + +} + +``` + +要运行带有`DataSource`的应用程序,你所需要的只是连接信息。还可以提供特定于池的设置。检查将在运行时使用的实现,以获得更多详细信息。 + +下面的示例展示了如何通过设置属性来定义 JDBC 数据源: + +Properties + +``` +app.datasource.url=jdbc:mysql://localhost/test +app.datasource.username=dbuser +app.datasource.password=dbpass +app.datasource.pool-size=30 +``` + +Yaml + +``` +app: + datasource: + url: "jdbc:mysql://localhost/test" + username: "dbuser" + password: "dbpass" + pool-size: 30 +``` + +然而,有一个问题。由于连接池的实际类型未公开,因此在你的自定义`DataSource`的元数据中不会生成任何键,并且 IDE 中也没有可用的补全功能(因为`DataSource`接口不公开任何属性)。另外,如果你碰巧在 Classpath 上有 hikari,则此基本设置将不工作,因为 hikari 没有`url`属性(但是有`jdbcUrl`属性)。在这种情况下,你必须按以下方式重写配置: + +Properties + +``` +app.datasource.jdbc-url=jdbc:mysql://localhost/test +app.datasource.username=dbuser +app.datasource.password=dbpass +app.datasource.pool-size=30 +``` + +Yaml + +``` +app: + datasource: + jdbc-url: "jdbc:mysql://localhost/test" + username: "dbuser" + password: "dbpass" + pool-size: 30 +``` + +你可以通过强制连接池使用并返回一个专用实现(而不是`DataSource`)来解决此问题。你无法在运行时更改实现,但选项列表将是显式的。 + +下面的示例显示了如何使用`DataSourceBuilder`创建`HikariDataSource`: + +``` +import com.zaxxer.hikari.HikariDataSource; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyDataSourceConfiguration { + + @Bean + @ConfigurationProperties("app.datasource") + public HikariDataSource dataSource() { + return DataSourceBuilder.create().type(HikariDataSource.class).build(); + } + +} + +``` + +你甚至可以通过利用`DataSourceProperties`为你做的事情来更进一步——也就是说,如果没有提供 URL,则提供一个默认的嵌入式数据库,该数据库具有合理的用户名和密码。你可以很容易地从任何`DataSourceProperties`对象的状态初始化`DataSourceBuilder`,因此你还可以插入 Spring boot 自动创建的数据源。但是,这会将你的配置拆分成两个名称空间:`url`,`username`,`password`,`type`,`driver`在`spring.datasource`上,其余的则在你的自定义名称空间上。为了避免这种情况,你可以在自定义名称空间上重新定义自定义`DataSourceProperties`,如下例所示: + +``` +import com.zaxxer.hikari.HikariDataSource; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration(proxyBeanMethods = false) +public class MyDataSourceConfiguration { + + @Bean + @Primary + @ConfigurationProperties("app.datasource") + public DataSourceProperties dataSourceProperties() { + return new DataSourceProperties(); + } + + @Bean + @ConfigurationProperties("app.datasource.configuration") + public HikariDataSource dataSource(DataSourceProperties properties) { + return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + } + +} + +``` + +该设置将你的*同步*与 Spring boot 在默认情况下为你提供的功能相同,只是选择了一个专用的连接池(在代码中),并且其设置在`app.datasource.configuration`子名称空间中公开。因为`DataSourceProperties`正在为你处理`url`/`jdbcurl’转换,所以你可以将其配置如下: + +Properties + +``` +app.datasource.url=jdbc:mysql://localhost/test +app.datasource.username=dbuser +app.datasource.password=dbpass +app.datasource.configuration.maximum-pool-size=30 +``` + +Yaml + +``` +app: + datasource: + url: "jdbc:mysql://localhost/test" + username: "dbuser" + password: "dbpass" + configuration: + maximum-pool-size: 30 +``` + +| |Spring Boot 将向`spring.datasource.hikari`公开特定于光的设置。
该示例使用更通用的`configuration`子名称空间,因为该示例不支持多个数据源实现。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |因为你的自定义配置选择使用 hikari,所以`app.datasource.type`没有任何效果。
在实践中,构建器是用你可能在那里设置的任何值初始化的,然后通过调用`.type()`来重写。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有关更多详细信息,请参见“ Spring 引导特性”部分中的“[data.html](data.html#data.sql.datasource)”和[“数据源自动配置”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java)类。 + +### 8.2.配置两个数据源 + +如果需要配置多个数据源,则可以应用上一节中描述的相同技巧。但是,你必须将一个`DataSource`实例标记为`@Primary`,因为未来的各种自动配置都希望能够根据类型获得一个实例。 + +如果你创建自己的`DataSource`,那么自动配置就会后退。在下面的示例中,我们提供了*确切*与在主数据源上提供的自动配置相同的功能集: + +``` +import com.zaxxer.hikari.HikariDataSource; +import org.apache.commons.dbcp2.BasicDataSource; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration(proxyBeanMethods = false) +public class MyDataSourcesConfiguration { + + @Bean + @Primary + @ConfigurationProperties("app.datasource.first") + public DataSourceProperties firstDataSourceProperties() { + return new DataSourceProperties(); + } + + @Bean + @Primary + @ConfigurationProperties("app.datasource.first.configuration") + public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) { + return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + } + + @Bean + @ConfigurationProperties("app.datasource.second") + public BasicDataSource secondDataSource() { + return DataSourceBuilder.create().type(BasicDataSource.class).build(); + } + +} + +``` + +| |`firstDataSourceProperties`必须标记为`@Primary`,以便数据库初始化器功能使用你的副本(如果你使用初始化器)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------| + +这两个数据源也都是为高级定制而设计的。例如,你可以将它们配置如下: + +Properties + +``` +app.datasource.first.url=jdbc:mysql://localhost/first +app.datasource.first.username=dbuser +app.datasource.first.password=dbpass +app.datasource.first.configuration.maximum-pool-size=30 + +app.datasource.second.url=jdbc:mysql://localhost/second +app.datasource.second.username=dbuser +app.datasource.second.password=dbpass +app.datasource.second.max-total=30 +``` + +Yaml + +``` +app: + datasource: + first: + url: "jdbc:mysql://localhost/first" + username: "dbuser" + password: "dbpass" + configuration: + maximum-pool-size: 30 + + second: + url: "jdbc:mysql://localhost/second" + username: "dbuser" + password: "dbpass" + max-total: 30 +``` + +你也可以将相同的概念应用到次要的`DataSource`,如下例所示: + +``` +import com.zaxxer.hikari.HikariDataSource; +import org.apache.commons.dbcp2.BasicDataSource; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration(proxyBeanMethods = false) +public class MyCompleteDataSourcesConfiguration { + + @Bean + @Primary + @ConfigurationProperties("app.datasource.first") + public DataSourceProperties firstDataSourceProperties() { + return new DataSourceProperties(); + } + + @Bean + @Primary + @ConfigurationProperties("app.datasource.first.configuration") + public HikariDataSource firstDataSource(DataSourceProperties firstDataSourceProperties) { + return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + } + + @Bean + @ConfigurationProperties("app.datasource.second") + public DataSourceProperties secondDataSourceProperties() { + return new DataSourceProperties(); + } + + @Bean + @ConfigurationProperties("app.datasource.second.configuration") + public BasicDataSource secondDataSource( + @Qualifier("secondDataSourceProperties") DataSourceProperties secondDataSourceProperties) { + return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource.class).build(); + } + +} + +``` + +前面的示例在自定义名称空间上配置两个数据源,其逻辑与 Spring 引导在自动配置中使用的逻辑相同。请注意,每个`configuration`子名称空间根据所选的实现提供高级设置。 + +### 8.3.使用 Spring 数据存储库 + +Spring 数据可以创建各种类型的`@Repository`接口的实现方式。 Spring 启动为你处理所有这些,只要那些`@Repositories`包含在你的`@EnableAutoConfiguration`类的同一个包(或子包)中。 + +对于许多应用程序,你所需要的只是将正确的 Spring 数据依赖关系放在你的 Classpath 上。对于 JPA 有`spring-boot-starter-data-jpa`,对于 MongoDB 有`spring-boot-starter-data-mongodb`,对于受支持的技术还有其他各种启动器。要开始,请创建一些存储库接口来处理`@Entity`对象。 + +Spring Boot 尝试猜测你的`@Repository`定义的位置,基于它找到的`@EnableAutoConfiguration`。要获得更多的控制,使用`@EnableJpaRepositories`注释(来自 Spring data JPA)。 + +有关 Spring 数据的更多信息,请参见[Spring Data project page](https://spring.io/projects/spring-data)。 + +### 8.4.将 @Entity 定义与 Spring 配置 ### 分开 + +Spring Boot 尝试猜测你的`@Entity`定义的位置,基于它找到的`@EnableAutoConfiguration`。要获得更多的控制,可以使用`@EntityScan`注释,如以下示例所示: + +``` +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@EnableAutoConfiguration +@EntityScan(basePackageClasses = City.class) +public class MyApplication { + + // ... + +} + +``` + +### 8.5.配置 JPA 属性 + +Spring 数据 JPA 已经提供了一些独立于供应商的配置选项(例如用于 SQL 日志记录的那些选项),并且 Spring 启动公开了这些选项以及用于 Hibernate 的更多一些作为外部配置属性。其中一些是根据上下文自动检测的,因此你不必设置它们。 + +`spring.jpa.hibernate.ddl-auto`是一种特殊情况,因为根据运行时条件,它有不同的默认值。如果使用了嵌入式数据库,并且没有模式管理器(例如 Liquibase 或 Flyway)处理`DataSource`,则默认为`create-drop`。在所有其他情况下,它默认为`none`。 + +JPA 提供程序检测要使用的方言。如果你喜欢自己设置方言,请设置`spring.jpa.database-platform`属性。 + +下面的示例显示了最常见的设置选项: + +Properties + +``` +spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy +spring.jpa.show-sql=true +``` + +Yaml + +``` +spring: + jpa: + hibernate: + naming: + physical-strategy: "com.example.MyPhysicalNamingStrategy" + show-sql: true +``` + +此外,当创建本地`EntityManagerFactory`时,`spring.jpa.properties.*`中的所有属性都将作为正常的 JPA 属性(去掉前缀)通过。 + +| |你需要确保在`spring.jpa.properties.*`下定义的名称与你的 JPA 提供程序所期望的名称完全匹配,
Spring boot 将不会尝试对这些条目进行任何放松的绑定。

,例如,如果要配置 Hibernate 的批处理大小,则必须使用`spring.jpa.properties.hibernate.jdbc.batch_size`。
如果使用其他形式,例如`batchSize`或`batch-size`,则 Hibernate 将不应用该设置。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |如果你需要对 Hibernate 属性应用高级定制,请考虑注册一个`HibernatePropertiesCustomizer` Bean,在创建`EntityManagerFactory`之前将调用它。
这优先于自动配置所应用的任何内容。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 8.6.配置 Hibernate 命名策略 + +Hibernate 使用[两种不同的命名策略](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#naming)将名称从对象模型映射到相应的数据库名称。通过分别设置`spring.jpa.hibernate.naming.physical-strategy`和`spring.jpa.hibernate.naming.implicit-strategy`属性,可以配置物理实现和隐式策略实现的完全限定类名。或者,如果`ImplicitNamingStrategy`或`PhysicalNamingStrategy`bean 在应用程序上下文中可用, Hibernate 将自动配置为使用它们。 + +默认情况下, Spring 引导使用`CamelCaseToUnderscoresNamingStrategy`配置物理命名策略。使用这种策略,所有的点都被替换为下划线,驼峰外壳也被替换为下划线。此外,默认情况下,所有的表名都是用小写字母生成的。例如,将`TelephoneNumber`实体映射到`telephone_number`表。如果你的模式需要混合大小写标识符,请定义一个自定义`CamelCaseToUnderscoresNamingStrategy` Bean,如下例所示: + +``` +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyHibernateConfiguration { + + @Bean + public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() { + return new CamelCaseToUnderscoresNamingStrategy() { + + @Override + protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) { + return false; + } + + }; + } + +} + +``` + +如果你更喜欢使用 Hibernate 5 的默认值,请设置以下属性: + +``` +spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl +``` + +或者,你可以配置以下内容 Bean: + +``` +import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +class MyHibernateConfiguration { + + @Bean + PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() { + return new PhysicalNamingStrategyStandardImpl(); + } + +} + +``` + +有关更多详细信息,请参见[“HibernatejpaAutoConfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.java)和[“jpabaseconfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java)。 + +### 8.7.配置 Hibernate 二级缓存 + +Hibernate [二级缓存](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#caching)可以被配置为用于缓存提供程序的范围。 Hibernate 与其配置以再次查找缓存提供程序,不如尽可能提供上下文中可用的那个。 + +要使用 JCache 执行此操作,首先要确保`org.hibernate:hibernate-jcache`在 Classpath 上可用。然后,添加一个`HibernatePropertiesCustomizer` Bean,如以下示例所示: + +``` +import org.hibernate.cache.jcache.ConfigSettings; + +import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; +import org.springframework.cache.jcache.JCacheCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyHibernateSecondLevelCacheConfiguration { + + @Bean + public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) { + return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager()); + } + +} + +``` + +这个定制程序将配置 Hibernate 使用与应用程序使用的相同的`CacheManager`。也可以使用单独的`CacheManager`实例。详见[the Hibernate user guide](https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#caching-provider-jcache)。 + +### 8.8.在 Hibernate 组件 ### 中使用依赖注入 + +默认情况下, Spring 引导注册了`BeanContainer`实现,该实现使用`BeanFactory`,以便转换器和实体侦听器可以使用常规的依赖注入。 + +你可以通过注册删除或更改`hibernate.resource.beans.container`属性的`HibernatePropertiesCustomizer`来禁用或调优此行为。 + +### 8.9.使用自定义 EntityManagerFactory + +要完全控制`EntityManagerFactory`的配置,你需要添加一个名为“EntityManagerFactory”的`@Bean`。 Spring 在存在 Bean 该类型的实体管理器的情况下,启动自动配置关闭其实体管理器。 + +### 8.10.使用多个 EntityManagerFactory + +如果需要对多个数据源使用 JPA,则每个数据源可能需要一个`EntityManagerFactory`。 Spring ORM 中的`LocalContainerEntityManagerFactoryBean`允许你为你的需要配置`EntityManagerFactory`。你还可以重用`JpaProperties`来绑定每个`EntityManagerFactory`的设置,如以下示例所示: + +``` +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaVendorAdapter; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; + +@Configuration(proxyBeanMethods = false) +public class MyEntityManagerFactoryConfiguration { + + @Bean + @ConfigurationProperties("app.jpa.first") + public JpaProperties firstJpaProperties() { + return new JpaProperties(); + } + + @Bean + public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(DataSource firstDataSource, + JpaProperties firstJpaProperties) { + EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(firstJpaProperties); + return builder.dataSource(firstDataSource).packages(Order.class).persistenceUnit("firstDs").build(); + } + + private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties jpaProperties) { + JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(jpaProperties); + return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null); + } + + private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) { + // ... map JPA properties as needed + return new HibernateJpaVendorAdapter(); + } + +} + +``` + +上面的示例使用名为`firstDataSource`的`EntityManagerFactory` Bean 创建`EntityManagerFactory`。它扫描位于`Order`相同包中的实体。可以使用`app.first.jpa`名称空间映射额外的 JPA 属性。 + +| |当你自己为`LocalContainerEntityManagerFactoryBean`创建 Bean 时,在创建自动配置的`LocalContainerEntityManagerFactoryBean`期间应用的任何自定义都会丢失。
例如,在 Hibernate 的情况下,在`spring.jpa.hibernate`前缀下的任何属性都不会自动应用到你的`LocalContainerEntityManagerFactoryBean`。
如果你依赖这些属性来配置诸如命名策略或 DDL 模式之类的东西,那么在创建`LocalContainerEntityManagerFactoryBean` Bean 时,你将需要显式地配置这些属性。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你应该为需要 JPA 访问的任何其他数据源提供类似的配置。要完成这幅图,你还需要为每个`EntityManagerFactory`配置一个`JpaTransactionManager`。或者,你也可以使用跨越这两个部分的 JTA 事务管理器。 + +如果使用 Spring 数据,则需要相应地配置`@EnableJpaRepositories`,如以下示例所示: + +``` +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@Configuration(proxyBeanMethods = false) +@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory") +public class OrderConfiguration { + +} + +``` + +``` +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@Configuration(proxyBeanMethods = false) +@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory") +public class CustomerConfiguration { + +} + +``` + +### 8.11.使用传统的 persistence.xml 文件 + +Spring 默认情况下,引导不会搜索或使用`META-INF/persistence.xml`。如果你更喜欢使用传统的`persistence.xml`,则需要定义你自己的`@Bean`类型的`LocalEntityManagerFactoryBean`(ID 为“EntityManagerFactory”)并在此设置持久性单元名称。 + +有关默认设置,请参见[“jpabaseconfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java)。 + +### 8.12.使用 Spring 数据 JPA 和 Mongo 存储库 ### + +Spring Data JPA 和 Spring Data Mongo 都可以自动为你创建`Repository`实现。如果它们都存在于 Classpath 上,那么你可能需要做一些额外的配置来告诉 Spring 引导要创建哪些存储库。最明确的方法是使用标准 Spring 数据`@EnableJpaRepositories`和`@EnableMongoRepositories`注释并提供你的`Repository`接口的位置。 + +还有一些标志(` Spring.data.*.repositories.enabled` 和`spring.data.*.repositories.type`),你可以使用它们在外部配置中打开和关闭自动配置的存储库。这样做是有用的,例如,如果你想关闭 Mongo 存储库,并且仍然使用自动配置的`MongoTemplate`。 + +Spring 对于其他自动配置的数据存储库类型(ElasticSearch、SOLR 和其他),存在相同的障碍和相同的特征。要使用它们,相应地更改注释和标志的名称。 + +### 8.13.自定义 Spring 数据的 Web 支持 + +Spring 数据提供了 Web 支持,该支持简化了 Spring 数据存储库在 Web 应用程序中的使用。 Spring Boot 在`spring.data.web`命名空间中提供了用于定制其配置的属性。请注意,如果使用 Spring 数据 REST,则必须使用`spring.data.rest`名称空间中的属性。 + +### 8.14.公开 Spring 数据存储库作为 REST 端点 ### + +Spring 数据 REST 可以公开实现作为你的 REST 端点,前提是 Spring 已经为应用程序启用了 MVC。 + +Spring 引导公开一组有用的属性(来自`spring.data.rest`命名空间),这些属性定制[“存储库和 restconfiguration”](https://docs.spring.io/spring-data/rest/docs/3.6.2/api/org/springframework/data/rest/core/config/RepositoryRestConfiguration.html)。如果需要提供额外的定制,则应该使用[“RepositoryRestConfigurer”](https://docs.spring.io/spring-data/rest/docs/3.6.2/api/org/springframework/data/rest/webmvc/config/RepositoryRestConfigurer.html) Bean。 + +| |如果你没有在你的自定义`RepositoryRestConfigurer`上指定任何顺序,则它将在 Spring 引导内部使用的命令之后运行。
如果你需要指定一个顺序,请确保它高于 0。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 8.15.配置 JPA ### 使用的组件 + +如果要配置 JPA 使用的组件,那么需要确保在 JPA 之前初始化该组件。当组件被自动配置时, Spring Boot 将为你处理这个问题。例如,当 Flyway 被自动配置时, Hibernate 被配置为依赖于 Flyway,这样 Flyway 就有机会在 Hibernate 尝试使用数据库之前初始化数据库。 + +如果你自己配置一个组件,你可以使用`EntityManagerFactoryDependsOnPostProcessor`子类作为设置必要的依赖关系的一种方便的方式。例如,如果使用 Hibernate Search with ElasticSearch 作为其索引管理器,则任何`EntityManagerFactory`bean 都必须配置为依赖于`elasticsearchClient` Bean,如以下示例所示: + +``` +import javax.persistence.EntityManagerFactory; + +import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryDependsOnPostProcessor; +import org.springframework.stereotype.Component; + +/** + * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that + * {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean. + */ +@Component +public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor + extends EntityManagerFactoryDependsOnPostProcessor { + + public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() { + super("elasticsearchClient"); + } + +} + +``` + +### 8.16.用两个数据源配置 Jooq + +如果你需要对多个数据源使用 Jooq,那么你应该为每个数据源创建自己的`DSLContext`。有关更多详细信息,请参见[JooqAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java)。 + +| |特别地,`JooqExceptionTranslator`和`SpringTransactionProvider`可以被重用,以提供类似于自动配置对单个`DataSource`所做的功能。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 9. 数据库初始化 + +SQL 数据库可以根据堆栈的不同以不同的方式初始化。当然,如果数据库是一个单独的过程,你也可以手动进行此操作。建议使用单一的机制来生成模式。 + +### 9.1.使用 JPA 初始化数据库 + +JPA 具有用于生成 DDL 的功能,并且这些功能可以设置为在启动时针对数据库运行。这是通过两个外部属性控制的: + +* `spring.jpa.generate-ddl`(布尔)会打开和关闭该功能,并且是独立于供应商的。 + +* `spring.jpa.hibernate.ddl-auto`是一个 Hibernate 特性,它以更细粒度的方式控制行为。这一特性将在本指南的后面进行更详细的描述。 + +### 9.2.使用 Hibernate 初始化数据库 + +可以显式地设置`spring.jpa.hibernate.ddl-auto`,标准 Hibernate 属性值为`none`、`validate`、`update`、`create`和`create-drop`。 Spring Boot 根据它是否认为你的数据库是嵌入式的,为你选择一个默认值。如果没有检测到模式管理器,则默认为`create-drop`,或者在所有其他情况下,默认为`none`。通过查看`Connection`类型和 JDBC URL,可以检测到嵌入式数据库。`hsqldb`、`h2`和`derby`是候选项,其他不是候选项。在从内存中切换到“真实”数据库时要小心,不要对新平台中的表和数据的存在做出假设。你必须显式地设置`ddl-auto`,或者使用其他机制之一来初始化数据库。 + +| |你可以通过启用`org.hibernate.SQL`记录器来输出模式创建。
如果你启用[debug mode](features.html#features.logging.console-output),这将自动为你完成。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +此外,如果 Hibernate 从头创建模式(即,如果`ddl-auto`属性设置为`create`或`create-drop`),则在启动时执行 Classpath 根中名为`import.sql`的文件。这对于演示和测试是有用的,如果你很小心的话,但可能不是你希望在生产 Classpath 中使用的东西。这是一个 Hibernate 特性(与 Spring 无关)。 + +### 9.3.使用基本 SQL 脚本初始化数据库 + +Spring 引导可以自动创建你的 JDBC`DataSource`或 R2DBC`ConnectionFactory`的模式(DDL 脚本)并对其进行初始化(DML 脚本)。它从标准的根 Classpath 位置加载 SQL:分别是`schema.sql`和`data.sql`。此外, Spring 引导处理`schema-${platform}.sql`和`data-${platform}.sql`文件(如果存在),其中`platform`是`spring.sql.init.platform`的值。这允许你在必要时切换到特定于数据库的脚本。例如,你可以选择将其设置为数据库的供应商名称(`hsqldb`,`h2`,`oracle`,`mysql`,`postgresql`,等等)。默认情况下,SQL 数据库初始化仅在使用嵌入式内存数据库时执行。要始终初始化 SQL 数据库(无论其类型如何),请将`spring.sql.init.mode`设置为`always`。类似地,要禁用初始化,请将`spring.sql.init.mode`设置为`never`。默认情况下, Spring 启动启用其基于脚本的数据库初始化器的抗故障特性。这意味着,如果脚本导致异常,则应用程序将无法启动。你可以通过设置`spring.sql.init.continue-on-error`来调整该行为。 + +默认情况下,在创建任何 JPA `EntityManagerFactory`bean 之前,将执行基于脚本的`DataSource`初始化。可以使用 schema.sql’为 JPA 管理的实体创建模式,也可以使用`data.sql`填充它。虽然我们不建议使用多个数据源初始化技术,但如果你希望基于脚本的`DataSource`初始化能够构建在 Hibernate 执行的模式创建上,请将`spring.jpa.defer-datasource-initialization`设置为`true`。这将推迟数据源初始化,直到创建和初始化了任何`EntityManagerFactory`bean 之后。然后可以使用 schema.sql’对 Hibernate 执行的任何模式创建进行添加,并且`data.sql`可以用于填充它。 + +如果你使用[高级数据库迁移工具](#howto.data-initialization.migration-tool),比如 Flyway 或 Liquibase,那么你应该单独使用它们来创建和初始化模式。不建议在 Flyway 或 Liquibase 旁边使用基本的`schema.sql`和`data.sql`脚本,并且在未来的版本中将取消支持。 + +### 9.4.初始化 Spring 批处理数据库 + +如果你使用 Spring 批处理,它会预先打包为大多数流行的数据库平台的 SQL 初始化脚本。 Spring 启动可以检测到你的数据库类型,并在启动时执行这些脚本。如果使用嵌入式数据库,默认情况下会发生这种情况。你还可以为任何数据库类型启用它,如以下示例所示: + +属性 + +``` +spring.batch.jdbc.initialize-schema=always +``` + +Yaml + +``` +spring: + batch: + jdbc: + initialize-schema: "always" +``` + +还可以通过将`spring.batch.jdbc.initialize-schema`设置为`never`,显式地关闭初始化。 + +### 9.5.使用更高级的数据库迁移工具 + +Spring Boot 支持两个更高级别的迁移工具:[Flyway](https://flywaydb.org/)和[Liquibase](https://www.liquibase.org/)。 + +#### 9.5.1.在启动时执行 Flyway 数据库迁移 + +若要在启动时自动运行 Flyway 数据库迁移,请将`org.flywaydb:flyway-core`添加到 Classpath 中。 + +通常,迁移是`V__.sql`形式的脚本(带有``的下划线分隔版本,例如“1”或“2\_1”)。默认情况下,它们位于一个名为`classpath:db/migration`的目录中,但是你可以通过设置`spring.flyway.locations`来修改该位置。这是一个以逗号分隔的列表,其中包含一个或多个`classpath:`或`filesystem:`位置。例如,以下配置将在缺省 Classpath 位置和`/opt/migration`目录中搜索脚本: + +属性 + +``` +spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration +``` + +Yaml + +``` +spring: + flyway: + locations: "classpath:db/migration,filesystem:/opt/migration" +``` + +你还可以添加一个特殊的`{vendor}`占位符来使用特定于供应商的脚本。假设如下: + +属性 + +``` +spring.flyway.locations=classpath:db/migration/{vendor} +``` + +Yaml + +``` +spring: + flyway: + locations: "classpath:db/migration/{vendor}" +``` + +前面的配置不使用`db/migration`,而是根据数据库的类型设置要使用的目录(例如`db/migration/mysql`for MySQL)。支持的数据库列表在[“数据库驱动程序”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java)中可用。 + +迁移也可以用 Java 编写。Flyway 将使用实现`JavaMigration`的任何 bean 自动配置。 + +[“Flyway属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/Flyway属性.java)提供了 Flyway 的大部分设置和一小组附加属性,可用于禁用迁移或关闭位置检查。如果需要对配置进行更多控制,可以考虑注册`FlywayConfigurationCustomizer` Bean。 + +Spring 引导调用`Flyway.migrate()`来执行数据库迁移。如果你想要更多的控制,请提供一个`@Bean`,实现[“FlywayMigrationStrategy”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationStrategy.java)。 + +Flyway 支持 SQL 和 Java[callbacks](https://flywaydb.org/documentation/concepts/callbacks)。要使用基于 SQL 的回调,请将回调脚本放置在`classpath:db/migration`目录中。要使用基于 Java 的回调,请创建一个或多个实现`Callback`的 bean。任何这样的 bean 都会自动注册为`Flyway`。它们可以通过使用`@Order`或实现`Ordered`来订购。也可以检测到实现不推荐的`FlywayCallback`接口的 bean,但是它们不能与`Callback`bean 一起使用。 + +默认情况下,Flyway 会自动连接上下文中的`DataSource`,并将其用于迁移。如果你希望使用不同的`DataSource`,则可以创建一个,并将其`@Bean`标记为`@FlywayDataSource`。如果你这样做并且需要两个数据源,请记住创建另一个数据源并将其标记为`@Primary`。或者,你可以通过在外部属性中设置`spring.flyway.[url,user,password]`来使用 Flyway 的本机`DataSource`。设置`spring.flyway.url`或`spring.flyway.user`就足以使 Flyway 使用自己的`DataSource`。如果没有设置这三个属性中的任何一个,则将使用其等效的`spring.datasource`属性的值。 + +你还可以使用 Flyway 为特定场景提供数据。例如,你可以在`src/test/resources`中放置特定于测试的迁移,并且仅在应用程序开始测试时才运行这些迁移。此外,你还可以使用配置文件特定的配置文件来定制`spring.flyway.locations`,以便仅在特定配置文件处于活动状态时才运行某些迁移。例如,在`application-dev.properties`中,你可以指定以下设置: + +Properties + +``` +spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration +``` + +Yaml + +``` +spring: + flyway: + locations: "classpath:/db/migration,classpath:/dev/db/migration" +``` + +在这种设置下,只有当`dev`配置文件处于活动状态时,`dev/db/migration`中的迁移才会运行。 + +#### 9.5.2.在启动时执行 Liquibase 数据库迁移 ### + +若要在启动时自动运行 Liquibase 数据库迁移,请将`org.liquibase:liquibase-core`添加到 Classpath 中。 + +| |当你将`org.liquibase:liquibase-core`添加到 Classpath 中时,默认情况下,在应用程序启动期间和测试运行之前都会运行数据库迁移,
可以通过使用`spring.liquibase.enabled`属性来定制此行为,在`main`和`test`配置中设置不同的值。
不可能使用两种不同的方式来初始化数据库(例如,用于应用程序启动的 Liquibase,用于测试运行的 JPA)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,从`spring.liquibase.change-log`读取主更改日志,但是你可以通过设置`spring.liquibase.change-log`来更改位置。除了 YAML,Liquibase 还支持 JSON、XML 和 SQL 更改日志格式。 + +默认情况下,Liquibase 在上下文中自动连接`DataSource`,并将其用于迁移。如果需要使用不同的`DataSource`,则可以创建一个,并将其`@Bean`标记为`@LiquibaseDataSource`。如果这样做,并且需要两个数据源,请记住创建另一个数据源,并将其标记为`@Primary`。或者,你可以通过在外部属性中设置`spring.liquibase.[driver-class-name,url,user,password]`来使用 Liquibase 的本机`DataSource`。设置`spring.liquibase.url`或`spring.liquibase.user`就足以使 Liquibase 使用自己的`DataSource`。如果没有设置这三个属性中的任何一个,则将使用其等效的`spring.datasource`属性的值。 + +有关上下文、默认模式等可用设置的详细信息,请参见[`LiquibaseProperties’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/liquibase/LiquibaseProperties.java)。 + +### 9.6.依赖于初始化的数据库 + +作为应用程序上下文刷新的一部分,在应用程序启动时执行数据库初始化。为了允许在启动过程中访问已初始化的数据库,将自动检测充当数据库初始化器的 bean 和要求该数据库已初始化的 bean。初始化依赖于已初始化的数据库的 bean 被配置为依赖于初始化它的 bean。如果在启动过程中,你的应用程序试图访问数据库,但尚未对其进行初始化,则可以配置额外的 bean 检测,以初始化数据库并要求对数据库进行初始化。 + +#### 9.6.1.检测数据库初始化器 + +Spring 启动将自动检测初始化 SQL 数据库的以下类型的 bean: + +* `DataSourceScriptDatabaseInitializer` + +* `EntityManagerFactory` + +* `Flyway` + +* `FlywayMigrationInitializer` + +* `R2dbcScriptDatabaseInitializer` + +* `SpringLiquibase` + +如果你使用第三方启动器进行数据库初始化库,则它可能会提供一个检测器,从而也会自动检测到其他类型的 bean。要检测到其他 bean,请在`META-INF/spring-factories`中注册`DatabaseInitializerDetector`的实现。 + +#### 9.6.2.检测依赖于数据库初始化的 Bean ### + +Spring 启动将自动检测依赖于数据库初始化的以下类型的 bean: + +* `AbstractEntityManagerFactoryBean`(除非`spring.jpa.defer-datasource-initialization`设置为`true`) + +* `DSLContext` + +* `EntityManagerFactory`(除非`spring.jpa.defer-datasource-initialization`设置为`true`) + +* `JdbcOperations` + +* `NamedParameterJdbcOperations` + +如果你使用的是第三方启动器数据访问库,那么它可能会提供一个检测器,使得其他类型的 bean 也会被自动检测到。要检测到其他 bean,请在`META-INF/spring-factories`中注册`DependsOnDatabaseInitializationDetector`的实现。或者,用`@DependsOnDatabaseInitialization`注释 Bean 的类或其`@Bean`方法。 + +## 10. 消息传递 + +Spring Boot 提供了许多启动器来支持消息传递。本节回答了在 Spring 引导下使用消息传递所产生的问题。 + +### 10.1.禁用已处理的 JMS 会话 + +如果你的 JMS 代理不支持事务会话,那么你必须完全禁用事务的支持。如果你创建自己的`JmsListenerContainerFactory`,则无需做任何事情,因为默认情况下无法进行交易。如果你想使用`DefaultJmsListenerContainerFactoryConfigurer`重用 Spring 引导的默认值,可以禁用已处理的会话,如下所示: + +``` +import javax.jms.ConnectionFactory; + +import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jms.config.DefaultJmsListenerContainerFactory; + +@Configuration(proxyBeanMethods = false) +public class MyJmsConfiguration { + + @Bean + public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, + DefaultJmsListenerContainerFactoryConfigurer configurer) { + DefaultJmsListenerContainerFactory listenerFactory = new DefaultJmsListenerContainerFactory(); + configurer.configure(listenerFactory, connectionFactory); + listenerFactory.setTransactionManager(null); + listenerFactory.setSessionTransacted(false); + return listenerFactory; + } + +} + +``` + +前面的示例覆盖了缺省工厂,并且应该将其应用到应用程序定义的任何其他工厂(如果有的话)。 + +## 11. 批处理应用程序 + +当人们在 Spring 引导应用程序中使用 Spring 批处理时,经常会出现许多问题。本节讨论这些问题。 + +### 11.1.指定批处理数据源 + +默认情况下,批处理应用程序需要`DataSource`来存储作业详细信息。 Spring 批处理默认情况下期望单个`DataSource`。要让它使用应用程序的主`DataSource`以外的`DataSource`,请声明一个`DataSource` Bean,并用`@BatchDataSource`注释其`@Bean`方法。如果你这样做并且需要两个数据源,请记住标记另一个`@Primary`。要获得更大的控制权,请实现`BatchConfigurer`。有关更多详细信息,请参见[The Javadoc of `@EnableBatchProcessing`](https://docs.spring.io/spring-batch/docs/4.3.5/api/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.html)。 + +有关 Spring 批处理的更多信息,请参见[Testcontainers](https://www.testcontainers.org/)。 + +### 11.2.在启动时运行批处理作业 + +Spring 通过将`@EnableBatchProcessing`添加到你的`@Configuration`类中的一个来启用批处理自动配置。 + +默认情况下,它在启动时在应用程序上下文中执行**全部**`Jobs`(有关详细信息,请参见[“JobLauncherApplicationRunner”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java))。你可以通过指定`spring.batch.job.names`(它接受以逗号分隔的作业名称模式列表),将范围缩小到特定的一个或多个作业。 + +有关更多详细信息,请参见[BatchaAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java)和[@enableBatchProcessing](https://docs.spring.io/spring-batch/docs/4.3.5/api/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.html)。 + +### 11.3.从命令行运行 + +Spring Boot 将以`--`开头的任何命令行参数转换为要添加到`Environment`的属性,请参见[访问命令行属性](features.html#features.external-config.command-line-args)。这不应用于将参数传递给批处理作业。要在命令行上指定批处理参数,请使用常规格式(即不使用`--`),如下例所示: + +``` +$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue +``` + +如果你在命令行上指定`Environment`的属性,它将被作业忽略。考虑以下命令: + +``` +$ java -jar myapp.jar --server.port=7070 someParameter=someValue +``` + +这只为批处理作业提供一个参数:`someParameter=someValue`。 + +### 11.4.存储作业存储库 + +Spring 批处理需要用于`Job`存储库的数据存储。如果使用 Spring 引导,则必须使用实际的数据库。请注意,它可以是内存中的数据库,请参见[配置作业存储库](https://docs.spring.io/spring-batch/docs/4.3.5/reference/html/job.html#configuringJobRepository)。 + +## 12. 执行器 + +Spring 引导包括 Spring 引导致动器。这一节回答了在使用中经常出现的问题。 + +### 12.1.更改执行器端点的 HTTP 端口或地址 ### + +在独立的应用程序中,Actuator HTTP 端口默认与主 HTTP 端口相同。要使应用程序侦听不同的端口,请设置外部属性:`management.server.port`。要侦听完全不同的网络地址(例如,当你有用于管理的内部网络和用于用户应用程序的外部网络时),还可以将`management.server.address`设置为服务器能够绑定的有效 IP 地址。 + +有关更多详细信息,请参见[“管理服务资产”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementServerProperties.java)源代码和“[actuator.html](actuator.html#actuator.monitoring.customizing-management-server-port)”中的“生产就绪特性”部分。 + +### 12.2.自定义“白标”错误页面 + +Spring 启动安装一个“WhiteLabel”错误页面,如果遇到服务器错误(使用 JSON 和其他媒体类型的机器客户端应该会看到一个具有正确错误代码的合理响应),你将在浏览器客户端中看到该页面。 + +| |设置`server.error.whitelabel.enabled=false`关闭默认的错误页。
这样做会恢复你正在使用的 Servlet 容器的默认值。
注意, Spring boot 仍然尝试解析错误视图,因此你可能应该添加自己的错误页,而不是完全禁用它。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +使用自己的模板技术覆盖错误页面,这取决于你使用的模板技术。例如,如果使用 ThymeLeaf,则可以添加`error.html`模板。如果使用 freemarker,则可以添加`error.ftlh`模板。通常,你需要一个名为`View`的`View`解析,或者一个处理`/error`路径的解析。除非你替换了一些默认配置,否则你应该在`ApplicationContext`中找到`BeanNameViewResolver`,因此名为`@Bean`的`error`将是一种方法。有关更多选项,请参见[“错误的 cautoform”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.java)。 + +有关如何在 Servlet 容器中注册处理程序的详细信息,请参见“[@enableBatchProcessing](https://docs.spring.io/spring-batch/docs/4.3.5/api/org/springframework/batch/core/configuration/annotation/EnableBatchProcessing.html)”一节。 + +### 12.3.清除敏感值 + +由`env`和`configprops`端点返回的信息可能有些敏感,因此默认情况下匹配某些模式的键将被清除(即它们的值被`******`代替)。 Spring 对于这样的密钥,引导使用合理的默认值:任何以单词“password”、“secret”、“key”、“token”、“vcap\_services”、“sun.java.command”结尾的密钥都是完全净化的。此外,将单词`credentials`(配置为正则表达式,即`.*credentials.*`)作为键的一部分的任何键也将被完全清除。 + +此外, Spring 引导将使用以下结尾之一的键的类 URI 值的敏感部分净化: + +* `address` + +* `addresses` + +* `uri` + +* `uris` + +* `url` + +* `urls` + +使用`://:@:/`格式标识 URI 的敏感部分。例如,对于属性`myclient.uri=http://user1:[[email protected]](/cdn-cgi/l/email-protection):8081`,得到的净化值是 `http://user1:******@localhost:8081`。 + +`env`和`configprops`端点使用的默认模式可以分别使用`management.endpoint.env.keys-to-sanitize`和`management.endpoint.configprops.keys-to-sanitize`替换。或者,可以使用`management.endpoint.env.additional-keys-to-sanitize`和`management.endpoint.configprops.additional-keys-to-sanitize`来配置其他模式。 + +### 12.4.将健康指标映射到千分尺指标 + +Spring 引导健康指示器返回一个`Status`类型,以指示整个系统的健康状况。如果你想要监视或提醒特定应用程序的健康水平,那么可以使用 Micrometer 将这些状态导出为度量。默认情况下,状态代码“上”、“下”、“Out\_of\_service”和“unknown”在 Spring boot 中使用。要导出这些值,你需要将这些状态转换为一组数字,以便它们可以与千分尺`Gauge`一起使用。 + +下面的示例展示了一种编写这种输出器的方法: + +``` +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; + +import org.springframework.boot.actuate.health.HealthEndpoint; +import org.springframework.boot.actuate.health.Status; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyHealthMetricsExportConfiguration { + + public MyHealthMetricsExportConfiguration(MeterRegistry registry, HealthEndpoint healthEndpoint) { + // This example presumes common tags (such as the app) are applied elsewhere + Gauge.builder("health", healthEndpoint, this::getStatusCode).strongReference(true).register(registry); + } + + private int getStatusCode(HealthEndpoint health) { + Status status = health.health().getStatus(); + if (Status.UP.equals(status)) { + return 3; + } + if (Status.OUT_OF_SERVICE.equals(status)) { + return 2; + } + if (Status.DOWN.equals(status)) { + return 1; + } + return 0; + } + +} + +``` + +## 13. 安全 + +本节讨论了使用 Spring 引导时的安全性问题,包括在 Spring 引导中使用 Spring 安全性所产生的问题。 + +有关 Spring 安全性的更多信息,请参见[Spring Security project page](https://spring.io/projects/spring-security)。 + +### 13.1.关闭 Spring 启动安全配置 ### + +如果在应用程序中使用`Status`或`SecurityFilterChain` Bean 定义,则会在 Spring 启动时关闭默认的 WebApp 安全设置。 + +### 13.2.更改 UserDetailsService 并添加用户帐户 ### + +如果提供`@Bean`、`AuthenticationProvider`或`UserDetailsService`类型的`@Bean`,则不会创建`@Bean`的默认`@Bean`。这意味着你拥有 Spring 安全性的完整功能集(例如[各种身份验证选项](https://docs.spring.io/spring-security/reference/5.6.2/servlet/authentication/index.html))。 + +添加用户帐户的最简单方法是提供自己的`UserDetailsService` Bean。 + +### 13.3.在代理服务器后面运行时启用 HTTPS + +对于任何应用程序来说,确保你的所有主要端点仅在 HTTPS 上可用都是一项重要的工作。如果你使用 Tomcat 作为 Servlet 容器,那么 Spring 启动将自动添加 Tomcat 自己的`RemoteIpValve`,如果它检测到某些环境设置,并且你应该能够依赖`HttpServletRequest`来报告它是否安全(甚至在处理真正的 SSL 终止的代理服务器的下游)。标准行为是由某些请求头的存在或不存在决定的(“x-forward-for”和`x-forwarded-proto`),它们的名称是常规的,因此它应该与大多数前端代理一起工作。可以通过向`application.properties`添加一些条目来打开阀门,如以下示例所示: + +Properties + +``` +server.tomcat.remoteip.remote-ip-header=x-forwarded-for +server.tomcat.remoteip.protocol-header=x-forwarded-proto +``` + +Yaml + +``` +server: + tomcat: + remoteip: + remote-ip-header: "x-forwarded-for" + protocol-header: "x-forwarded-proto" +``` + +(在阀门上存在这两种属性中的任何一种的开关。或者,你可以通过使用`WebServerFactoryCustomizer` Bean 自定义`TomcatServletWebServerFactory`来添加`RemoteIpValve`。) + +要将 Spring 安全性配置为需要针对所有(或某些)请求的安全通道,请考虑添加你自己的`SecurityFilterChain` Bean,该配置添加以下`HttpSecurity`配置: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class MySecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + // Customize the application security ... + http.requiresChannel().anyRequest().requiresSecure(); + return http.build(); + } + +} + +``` + +## 14. 热交换 + +Spring 启动支持热交换。这一部分回答了有关它如何工作的问题。 + +### 14.1.重新加载静态内容 + +热重装有几种选择。推荐的方法是使用`addresses`,因为它提供了额外的开发时功能,例如支持快速应用程序重启和 LiveReload 以及合理的开发时配置(例如模板缓存)。DevTools 通过监视 Classpath 的变化来工作。这意味着静态资源更改必须“构建”以使更改生效。默认情况下,在 Eclipse 中,当你保存更改时,这会自动发生。在 IntelliJ IDEA 中,make 项目命令触发了必要的构建。由于[默认重启排除项](using.html#using.devtools.restart.excluding-resources),对静态资源的更改不会触发应用程序的重新启动。然而,它们确实会触发一次实时重装。 + +或者,在 IDE 中运行(尤其是在调试的情况下)是进行开发的一种好方法(所有现代 IDE 都允许重新加载静态资源,并且通常还允许对 Java 类的更改进行热交换)。 + +最后,可以配置[Maven and Gradle plugins](build-tool-plugins.html#build-tool-plugins)(参见`addResources`属性)以支持从命令行运行,并直接从源重新加载静态文件。如果你使用更高级的工具编写代码,那么你可以将其与外部 CSS/JS 编译器进程一起使用。 + +### 14.2.在不重新启动容器的情况下重新加载模板 + +Spring Boot 支持的大多数模板化技术包括禁用缓存的配置选项(本文将在后面描述)。如果你使用[自动配置](using.html#using.devtools.property-defaults)模块,这些属性在开发时对你来说是[自动配置](using.html#using.devtools.property-defaults)。 + +#### 14.2.1.百香叶模板 + +如果使用 thymeleaf,请将`spring.thymeleaf.cache`设置为`false`。有关其他胸腺叶自定义选项,请参见[胸腺叶自动构型](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/thymeleaf/ThymeleafAutoConfiguration.java)。 + +#### 14.2.2.自由标记模板 + +如果使用 freemarker,请将`false`设置为`false`。有关其他自由标记定制选项,请参见[FreemarkerAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/freemarker/FreeMarkerAutoConfiguration.java)。 + +#### 14.2.3.Groovy 模板 + +如果使用 Groovy 模板,请将`spring.groovy.template.cache`设置为`false`。有关其他 Groovy 定制选项,请参见[GroovytemplateAutoConfiguration](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/groovy/template/GroovyTemplateAutoConfiguration.java)。 + +### 14.3.快速应用程序重启 + +`spring-boot-devtools`模块包括对应用程序自动重启的支持。虽然没有[JRebel](https://www.jrebel.com/products/jrebel)等技术那么快,但它通常比“冷启动”快得多。在研究本文后面讨论的一些更复杂的重新加载选项之前,你可能应该尝试一下。 + +有关更多详细信息,请参见[using.html](using.html#using.devtools)小节。 + +### 14.4.在不重新启动容器的情况下重新加载 Java 类 ### + +许多现代 IDE(Eclipse、IDEA 和其他)支持字节码的热交换。因此,如果你所做的更改不会影响类或方法签名,那么它应该干净地重新加载,而不会产生任何副作用。 + +## 15. 测试 + +Spring 引导包括许多测试实用程序和支持类,以及提供公共测试依赖关系的专用启动器。这一节回答了有关考试的常见问题。 + +### 15.1.使用 Spring 安全性进行测试 + +Spring 安全性为作为特定用户运行测试提供了支持。例如,下面代码片段中的测试将与具有`ADMIN`角色的经过身份验证的用户一起运行。 + +``` +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; + +@WebMvcTest(UserController.class) +class MySecurityTests { + + @Autowired + private MockMvc mvc; + + @Test + @WithMockUser(roles = "ADMIN") + void requestProtectedUrlWithUser() throws Exception { + this.mvc.perform(get("/")); + } + +} + +``` + +Spring 安全性提供了与 Spring MVC 测试的全面集成,并且这也可以在使用`@WebMvcTest`切片和`MockMvc`测试控制器时使用。 + +有关 Spring Security 的测试支持的更多详细信息,请参见 Spring Security 的[参考文献](https://docs.spring.io/spring-security/reference/5.6.2/servlet/test/index.html)。 + +### 15.2.使用 TestContainers 进行集成测试 + +[Testcontainers](https://www.testcontainers.org/)库提供了一种管理在 Docker 容器中运行的服务的方法。它集成了 JUnit,允许你编写一个测试类,可以在运行任何测试之前启动一个容器。TestContainers 对于编写与真正的后端服务(如 MySQL、MongoDB、Cassandra 等)对话的集成测试特别有用。 Spring 启动测试中可以使用 TestContainers,如下所示: + +``` +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +@Testcontainers +class MyIntegrationTests { + + @Container + static Neo4jContainer neo4j = new Neo4jContainer<>("neo4j:4.2"); + + @Test + void myTest() { + // ... + } + +} + +``` + +这将在运行任何测试之前启动运行 NEO4j 的 Docker 容器(如果 Docker 在本地运行)。在大多数情况下,你将需要使用正在运行的容器中的详细信息来配置应用程序,例如容器 IP 或端口。 + +这可以通过允许向 Spring 环境中添加动态属性值的静态`SpringApplication`方法来完成。 + +``` +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.Neo4jContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; + +@SpringBootTest +@Testcontainers +class MyIntegrationTests { + + @Container + static Neo4jContainer neo4j = new Neo4jContainer<>("neo4j:4.2"); + + @Test + void myTest() { + // ... + } + + @DynamicPropertySource + static void neo4jProperties(DynamicPropertyRegistry registry) { + registry.add("spring.neo4j.uri", neo4j::getBoltUrl); + } + +} + +``` + +上述配置允许应用程序中与 NEO4J 相关的 bean 与运行在 TestContainers 管理的 Docker 容器内的 NEO4J 通信。 + +## 16. 建立 + +Spring 引导包括 Maven 和 Gradle 的构建插件。本节回答有关这些插件的常见问题。 + +### 16.1.生成构建信息 + +Maven 插件和 Gradle 插件都允许生成包含项目的坐标、名称和版本的构建信息。插件也可以被配置为通过配置添加额外的属性。当存在这样的文件时, Spring 引导自动配置`BuildProperties` Bean。 + +要用 Maven 生成构建信息,请为`build-info`目标添加一个执行,如以下示例所示: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + 2.6.4 + + + + build-info + + + + + + +``` + +| |有关更多详细信息,请参见[Spring Boot Maven Plugin documentation](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#goals-build-info)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的示例对 Gradle 执行相同的操作: + +``` +springBoot { + buildInfo() +} +``` + +| |有关更多详细信息,请参见[Spring Boot Gradle Plugin documentation](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#integrating-with-actuator-build-info)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 16.2.生成 Git 信息 + +Maven 和 Gradle 都允许生成一个`git.properties`文件,该文件包含有关在构建项目时你的`git`源代码库的状态的信息。 + +对于 Maven 用户,`spring-boot-starter-parent` POM 包括一个预先配置的插件以生成`git.properties`文件。要使用它,请将`git.properties`的以下声明添加到你的 POM 中: + +``` + + + + pl.project13.maven + git-commit-id-plugin + + + +``` + +Gradle 用户可以通过使用[`gradle-git-properties`](https://plugins.gradle.org/plugin/com.gorylenko.gradle-git-properties)插件来实现相同的结果,如以下示例所示: + +``` +plugins { + id "com.gorylenko.gradle-git-properties" version "2.3.2" +} +``` + +Maven 和 Gradle 插件都允许配置`git.properties`中包含的属性。 + +| |`git.properties`中的提交时间预计将匹配以下格式:`yyyy-MM-dd’T’HH:mm:ssZ`。
这是上面列出的两个插件的默认格式。
使用这种格式,当序列化为 JSON 时,可以将时间解析为`Date`及其格式,由 Jackson 的日期序列化配置设置控制。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 16.3.自定义依赖版本 + +`spring-boot-dependencies` POM 管理公共依赖关系的版本。 Spring Maven 和 Gradle 的引导插件允许使用构建属性定制这些托管依赖版本。 + +| |Spring 每个启动版本都是针对这一组特定的第三方依赖关系进行设计和测试的。
重写版本可能会导致兼容性问题。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要用 Maven 重写依赖版本,请参见 Maven 插件文档中的[this section](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#using)。 + +要重写 Gradle 中的依赖关系版本,请参见 Gradle 插件文档中的[this section](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#managing-dependencies-dependency-management-plugin-customizing)。 + +### 16.4.用 Maven 创建可执行文件 jar + +
可用于创建可执行的“fat” jar。如果你使用`spring-boot-starter-parent` POM,你可以声明该插件,并且你的 JAR 被重新打包如下: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + + +``` + +如果不使用父 POM,则仍然可以使用该插件。但是,你必须另外添加``部分,如下所示: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + {spring-boot-version} + + + + repackage + + + + + + +``` + +有关完整的使用详细信息,请参见[插件文档](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#repackage)。 + +### 16.5.使用 Spring 引导应用程序作为依赖项 ### + +与 WAR 文件一样, Spring 引导应用程序不打算用作依赖项。如果你的应用程序包含希望与其他项目共享的类,那么推荐的方法是将该代码移动到单独的模块中。然后,你的应用程序和其他项目可以依赖于单独的模块。 + +如果无法按照上面的建议重新排列代码,则必须配置 Spring boot 的 Maven 和 Gradle 插件,以生成一个单独的工件,该工件适合用作依赖项。可执行归档文件不能作为[executable jar format](executable-jar.html#appendix.executable-jar.nested-jars.jar-structure)中的`BOOT-INF/classes`包应用程序类的依赖项使用。这意味着当可执行文件 jar 用作依赖项时,无法找到它们。 + +要生成两个工件(一个可用作依赖项,另一个可执行),必须指定一个分类器。此分类器应用于可执行归档的名称,将默认归档作为依赖项使用。 + +要在 Maven 中配置`exec`的分类器,可以使用以下配置: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + exec + + + + +``` + +### 16.6.当可执行文件 jar 运行 ### 时提取特定的库 + +jar 可执行文件中的大多数嵌套库不需要解压缩才能运行。然而,某些图书馆可能会有问题。例如,JRuby 包含其自己的嵌套 jar 支持,它假定`jruby-complete.jar`始终可以直接作为文件使用。 + +为了处理任何有问题的库,你可以标记特定的嵌套 JAR 应该在可执行文件 jar 首次运行时自动解压缩。这样的嵌套 JAR 写在由`java.io.tmpdir`系统属性标识的临时目录之下。 + +| |应该注意确保配置了你的操作系统,以便在应用程序仍在运行时,它不会删除已解压缩到临时目录的 JAR。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +例如,为了表明应该使用 Maven 插件标记 JRuby 以进行解包,你可以添加以下配置: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.jruby + jruby-complete + + + + + + +``` + +### 16.7.创建带有排除项的不可执行程序 jar + +jar 通常,如果将可执行文件和不可执行文件作为两个单独的构建产品,则可执行版本具有库 jar 中不需要的附加配置文件。例如,`application.yml`配置文件可能被从非可执行文件 jar 中排除。 + +在 Maven 中,可执行文件 jar 必须是主要工件,并且可以为库添加一个分类文件 jar,如下所示: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + + maven-jar-plugin + + + lib + package + + jar + + + lib + + application.yml + + + + + + + +``` + +### 16.8.用 Maven 启动的 Spring 启动应用程序的远程调试 + +要将远程调试器附加到用 Maven 启动的 Spring 启动应用程序,可以使用`jvmArguments`属性的[maven plugin](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/)。 + +有关更多详细信息,请参见[this example](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/#run-example-debug)。 + +### 16.9.在不使用 Spring-boot-antlib#### 的情况下,从 Ant 构建一个可执行的归档文件 + +要使用 Ant 进行构建,你需要获取依赖项、进行编译,然后创建 jar 或 WAR 归档。要使其可执行,你可以使用`spring-boot-antlib`模块,也可以遵循以下说明: + +1. 如果正在构建 jar,则将应用程序的类和资源打包到嵌套的`BOOT-INF/classes`目录中。如果正在构建 WAR,则像往常一样将应用程序的类打包到嵌套的`WEB-INF/classes`目录中。 + +2. 在嵌套的`BOOT-INF/lib`目录中为 jar 或`WEB-INF/lib`目录中为 WAR 添加运行时依赖项。记住**不是**来压缩归档中的条目。 + +3. 在嵌套的`BOOT-INF/lib`目录中添加`provided`(嵌入式容器)依赖项,用于 jar 或用于 WAR 的`WEB-INF/lib-provided`。记住**不是**来压缩归档中的条目。 + +4. 在归档的根目录中添加`spring-boot-loader`类(这样`Main-Class`就可用了)。 + +5. 在清单中使用适当的启动器(例如 jar 文件的`JarLauncher`)作为`Main-Class`属性,并指定它需要的其他属性作为清单条目——主要是通过设置`Start-Class`属性。 + +下面的示例展示了如何使用 Ant 构建可执行归档文件: + +``` + + + + + + + + + + + + + + + + + + + + + +``` + +## 17. 传统部署 + +Spring 启动支持传统的部署以及更现代的部署形式。本节回答有关传统部署的常见问题。 + +### 17.1.创建一个可部署的 WAR 文件 + +| |由于 Spring WebFlux 并不严格依赖于 Servlet API,并且应用程序在默认情况下部署在嵌入式的 Reactor Netty 服务器上,因此 WebFlux 应用程序不支持 WAR 部署。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +生成可部署 WAR 文件的第一步是提供`SpringBootServletInitializer`子类并覆盖其`configure`方法。这样做利用了 Spring Framework 的 Servlet 3.0 支持,并允许你在 Servlet 容器启动应用程序时对其进行配置。通常,你应该更新应用程序的主类以扩展`SpringBootServletInitializer`,如下例所示: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication +public class MyApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(MyApplication.class); + } + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +下一步是更新你的构建配置,以便你的项目生成一个 WAR 文件,而不是一个 jar 文件。如果你使用 Maven 和`spring-boot-starter-parent`(它为你配置了 Maven 的 WAR 插件),那么你所需要做的就是修改`pom.xml`以将包装更改为 WAR,如下所示: + +``` +war +``` + +如果使用 Gradle,则需要修改`build.gradle`以将 WAR 插件应用到项目中,如下所示: + +``` +apply plugin: 'war' +``` + +该过程中的最后一步是确保嵌入式 Servlet 容器不会干扰部署 WAR 文件的 Servlet 容器。要做到这一点,你需要将嵌入的 Servlet 容器依赖标记为已提供的。 + +如果使用 Maven,则以下示例将 Servlet 容器( Tomcat,在本例中)标记为被提供的: + +``` + + + + org.springframework.boot + spring-boot-starter-tomcat + provided + + + +``` + +如果使用 Gradle,则以下示例将 Servlet 容器( Tomcat,在本例中)标记为被提供的: + +``` +dependencies { + // ... + providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat' + // ... +} +``` + +| |`providedRuntime`优于 Gradle 的`compileOnly`配置。
在其他限制中,`compileOnly`依赖项不在测试 Classpath 上,因此任何基于 Web 的集成测试都会失败。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果使用[Spring Boot build tools](build-tool-plugins.html#build-tool-plugins),将嵌入的 Servlet 容器依赖项标记为提供的,则生成一个可执行的 WAR 文件,该文件包含打包在`lib-provided`目录中的所提供的依赖项。这意味着,除了可以部署到 Servlet 容器之外,还可以通过在命令行上使用`java -jar`来运行应用程序。 + +### 17.2.将现有的应用程序转换为 Spring 引导 ### + +要将现有的非 Web Spring 应用程序转换为 Spring 引导应用程序,请替换创建`ApplicationContext`的代码,并将其替换为对`SpringApplication`或`SpringApplicationBuilder`的调用。 Spring MVC Web 应用程序通常易于首先创建可部署的 WAR 应用程序,然后随后将其迁移到可执行的 WAR 或 jar。参见[Getting Started Guide on Converting a jar to a war](https://spring.io/guides/gs/convert-jar-to-war/)。 + +要通过扩展`SpringBootServletInitializer`(例如,在一个名为`Application`的类中)并添加 Spring boot`@SpringBootApplication`注释来创建可部署的 WAR,请使用类似于以下示例中所示的代码: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication +public class MyApplication extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + // Customize the application or call application.sources(...) to add sources + // Since our example is itself a @Configuration class (via @SpringBootApplication) + // we actually do not need to override this method. + return application; + } + +} + +``` + +请记住,无论你在`sources`中放入什么,都只是一个 Spring `ApplicationContext`。通常情况下,任何已经起作用的东西都应该在这里起作用。可能有一些 bean 可以稍后删除,然后让 Spring Boot 为它们提供自己的默认值,但是在你需要这样做之前,应该有可能使某些工作正常进行。 + +静态资源可以在 Classpath 根中移动到`/public`(或`/static`或`/resources`或`/META-INF/resources`)。这同样适用于`messages.properties`( Spring boot 在 Classpath 的根中自动检测)。 + +Spring `DispatcherServlet`和 Spring 安全性的普通用法应该不需要进一步的更改。如果你的应用程序中有其他特性(例如,使用其他 servlet 或过滤器),那么你可能需要通过替换`web.xml`中的那些元素,向`Application`上下文添加一些配置,如下所示: + +* `@Bean`或`ServletRegistrationBean`类型的`@Bean`将 Bean 安装到容器中,就好像它是``和``中的`web.xml`一样。 + +* 类型`Filter`或`FilterRegistrationBean`的`ADMIN`的行为类似(如``和``)。 + +* XML 文件中的`ApplicationContext`可以通过``中的`Application`添加。或者,注释配置已经被大量使用的情况可以用几行重新创建`@Bean`定义。 + +一旦 WAR 文件开始工作,你就可以通过在`Application`中添加`main`方法使其可执行,如以下示例所示: + +``` +public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); +} + +``` + +| |如果你打算以 WAR 或可执行应用程序的形式启动你的应用程序,你需要在一个方法中共享构建器的自定义。这对`SpringBootServletInitializer`回调都是可用的而在`main`方法中的一个类类似于:

``
Import org.SpringFramework.Boot.Banner;
Import org.SpringFramework.Boot.Boot.216.SpringBuilder;<1210"/>Import org.SpringtFramework.gt=”[SpringtBuilder]。支持.SpringtExtExtends;“/”/“公共应用程序 =”/“SpringtServframework=”/“初始化应用程序 =”@styletIntegtApplicalizr=main{
CustomizerBuilder(New SpringApplicationBuilder())).run;


Private Static SpringationBuilder CustomizerBuilder{<
Return Builder.sources.sources(myApplication.class).bannermode(banner.mode.mode.gt=“1226”/>gt r=“1227”/><1228"gt r="/>“1230”<>“>”>“>”r=“qt=”qt| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +应用程序可以分为多个类别: + +* Servlet 3.0+no`web.xml`的应用程序。 + +* 带有
的应用程序。 + +* 具有上下文层次结构的应用程序。 + +* 没有上下文层次结构的应用程序。 + +所有这些都应该易于翻译,但每一种可能需要略有不同的技术。 + +Servlet 3.0+ 应用程序如果已经使用了 Spring Servlet 3.0+ 初始化器支持类,则可以非常容易地进行转换。通常,来自现有`WebApplicationInitializer`的所有代码都可以移动到`SpringBootServletInitializer`中。如果你现有的应用程序有多个`AbstractDispatcherServletInitializer`(例如,如果它使用`AbstractDispatcherServletInitializer`),那么你可以将所有的上下文源合并到一个`SpringApplication`中。你可能遇到的主要复杂情况是,如果合并不起作用,并且需要维护上下文层次结构。有关示例,请参见[关于建立层次结构的条目](#howto.application.context-hierarchy)。包含特定于 Web 的特性的现有父上下文通常需要分解,以便所有`ServletContextAware`组件都在子上下文中。 + +Spring 尚未应用的应用程序可以转换为 Spring 引导应用程序,并且前面提到的指导可能会有所帮助。然而,你可能还会遇到问题。在这种情况下,我们建议[asking questions on Stack Overflow with a tag of `spring-boot`](https://stackoverflow.com/questions/tagged/spring-boot)。 + +### 17.3.向 WebLogic 部署一场战争 + +要将 Spring 引导应用程序部署到 WebLogic,你必须确保你的 Servlet 初始化器**直接**实现了`WebApplicationInitializer`(即使你从已经实现它的基类进行了扩展)。 + +WebLogic 的典型初始化器应该类似于以下示例: + +``` +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.web.WebApplicationInitializer; + +@SpringBootApplication +public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer { + +} + +``` + +如果你使用注销,你还需要告诉 WebLogic 更喜欢打包的版本,而不是服务器预装的版本。可以通过添加具有以下内容的`WEB-INF/weblogic.xml`文件来实现此目的: + +``` + + + + + org.slf4j + + + +``` diff --git a/docs/spring-boot/io.md b/docs/spring-boot/io.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c412490aa8c40c57ae91dc8069c100d2da1365c2 100644 --- a/docs/spring-boot/io.md +++ b/docs/spring-boot/io.md @@ -0,0 +1,870 @@ +# IO + +大多数应用程序将需要在某个时刻处理输入和输出问题。 Spring Boot 提供了一系列技术的实用工具和集成,以在需要 IO 功能时提供帮助。本节介绍了标准的 IO 特性,如缓存和验证,以及更高级的主题,如调度和分布式事务。我们还将覆盖呼叫远程 REST 或 SOAP 服务和发送电子邮件。 + +## 1. 缓存 + +Spring 框架为透明地向应用程序添加缓存提供了支持。在其核心部分,抽象将缓存应用于方法,从而减少了基于缓存中可用信息的执行次数。缓存逻辑是透明地应用的,不会对调用者造成任何干扰。 Spring 只要使用`@EnableCaching`注释启用缓存支持,启动就会自动配置缓存基础设施。 + +| |查看 Spring 框架引用的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/integration.html#cache)以获取更多详细信息。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +简而言之,要将缓存添加到服务的操作中,请将相关的注释添加到其方法中,如以下示例所示: + +``` +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; + +@Component +public class MyMathService { + + @Cacheable("piDecimals") + public int computePiDecimal(int precision) { + ... + } + +} + +``` + +这个示例演示了在可能代价高昂的操作上使用缓存的方法。在调用`computePiDecimal`之前,抽象在`piDecimals`缓存中查找与`i`参数匹配的条目。如果找到一个条目,缓存中的内容将立即返回给调用者,并且不调用该方法。否则,将调用该方法,并在返回值之前更新缓存。 + +| |你也可以透明地使用标准的 JSR-107 注释(例如`@CacheResult`)。但是,我们强烈建议你不要混合和匹配 Spring 缓存和 JCache 注释。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果不添加任何特定的缓存库, Spring 引导自动配置一个[simple provider](#io.caching.provider.simple),它在内存中使用并发映射。当需要缓存时(例如前面示例中的`piDecimals`),此提供程序将为你创建它。简单的提供者并不是真正推荐用于生产使用的,但是它对于开始使用和确保你了解这些特性是非常好的。当你决定使用缓存提供程序时,请务必阅读其文档,以了解如何配置应用程序使用的缓存。几乎所有的提供程序都要求你显式地配置应用程序中使用的每个缓存。一些提供了一种方法来定制由`spring.cache.cache-names`属性定义的默认缓存。 + +| |还可以透明地从缓存中获取[update](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/integration.html#cache-annotations-put)或[evict](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/integration.html#cache-annotations-evict)数据。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.1.支持的缓存提供程序 + +缓存抽象不提供实际的存储,而是依赖于由`org.springframework.cache.Cache`和`org.springframework.cache.CacheManager`接口实现的抽象。 + +如果你还没有定义类型`CacheManager`的 Bean 或名为`cacheResolver`的`CacheResolver`(参见[“CachingConfigurer”](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/cache/annotation/CachingConfigurer.html)), Spring 引导将尝试检测以下提供程序(按指定的顺序): + +1. [Generic](#io.caching.provider.generic) + +2. [JCache(JSR-107)](#io.caching.provider.jcache)(Ehcache3、黑泽尔卡斯特、Infinispan 等) + +3. [EhCache 2.x](#io.caching.provider.ehcache2) + +4. [Hazelcast](#io.caching.provider.hazelcast) + +5. [Infinispan](#io.caching.provider.infinispan) + +6. [Couchbase](#io.caching.provider.couchbase) + +7. [Redis](#io.caching.provider.redis) + +8. [Caffeine](#io.caching.provider.caffeine) + +9. [Simple](#io.caching.provider.simple) + +| |通过设置`spring.cache.type`属性,也可以*力*特定的缓存提供程序。
如果在某些环境(例如测试)中需要[完全禁用缓存](#io.caching.provider.none),请使用此属性。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |使用`spring-boot-starter-cache`“starter”快速添加基本的缓存依赖关系。
starter 带来`spring-context-support`。
如果手动添加依赖关系,则必须包含`spring-context-support`才能使用 jcache、ehcache2.x 或咖啡因支持。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果`CacheManager`是通过 Spring 引导自动配置的,那么可以通过公开实现`CacheManagerCustomizer`接口的 Bean 来在它完全初始化之前进一步优化其配置。下面的示例设置了一个标志,表示`null`值不应向下传递到底层映射: + +``` +import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyCacheManagerConfiguration { + + @Bean + public CacheManagerCustomizer cacheManagerCustomizer() { + return (cacheManager) -> cacheManager.setAllowNullValues(false); + } + +} + +``` + +| |在前面的示例中,需要自动配置`ConcurrentMapCacheManager`。,如果不是这样(要么你提供了自己的配置,要么自动配置了其他缓存提供程序),则根本不会调用自定义程序,
你可以根据需要配置任意多的自定义程序,你还可以使用`@Order`或`Ordered`来订购它们。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.1.泛型 + +如果上下文定义*至少*one`org.springframework.cache.Cache` Bean,则使用泛型缓存。创建了包装该类型的所有 bean 的`CacheManager`。 + +#### 1.1.2.JCache(JSR-107) + +[JCache](https://jcp.org/en/jsr/detail?id=107)是通过在 Classpath 上存在`javax.cache.spi.CachingProvider`(即在 Classpath 上存在符合 JSR-107 的缓存库)来引导的,而`JCacheCacheManager`是由`spring-boot-starter-cache`“starter”提供的。各种兼容的库是可用的,并且 Spring Boot 为 Ehcache3、HazelCast 和 Infinispan 提供了依赖管理。还可以添加任何其他兼容的库。 + +可能会出现多个提供程序,在这种情况下,必须显式地指定提供程序。 Spring 即使 JSR-107 标准没有强制使用一种标准化的方式来定义配置文件的位置, Spring Boot 也会尽最大的努力来适应使用实现细节来设置缓存,如下面的示例所示: + +属性 + +``` +# Only necessary if more than one provider is present +spring.cache.jcache.provider=com.example.MyCachingProvider +spring.cache.jcache.config=classpath:example.xml +``` + +Yaml + +``` +# Only necessary if more than one provider is present +spring: + cache: + jcache: + provider: "com.example.MyCachingProvider" + config: "classpath:example.xml" +``` + +| |Spring 当缓存库同时提供本机实现和 JSR-107 支持时,引导更倾向于 JSR-107 支持,这样,如果切换到不同的 JSR-107 实现,就可以使用相同的功能。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |Spring 引导具有[对 Hazelcast 的一般支持](#io.hazelcast)。
如果单个`HazelcastInstance`是可用的,那么它也会自动被用于`CacheManager`,除非指定了`spring.cache.jcache.config`属性。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有两种方法可以定制底层`javax.cache.cacheManager`: + +* 通过设置`spring.cache.cache-names`属性,可以在启动时创建缓存。如果定义了自定义`javax.cache.configuration.Configuration` Bean,则使用它来自定义它们。 + +* `org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer`bean 是用`CacheManager`的引用调用的,用于完全定制。 + +| |如果定义了一个标准的`javax.cache.CacheManager` Bean,则它会自动包装在抽象所期望的`org.springframework.cache.CacheManager`实现中。
不会对其应用进一步的自定义。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.3.Ehcache2.x + +如果在 Classpath 的根位置可以找到名为`ehcache.xml`的文件,则使用[EhCache](https://www.ehcache.org/)2.x。如果找到了 Ehcache2.x,则使用`spring-boot-starter-cache`“starter”提供的`EhCacheCacheManager`引导缓存管理器。还可以提供一个替代配置文件,如以下示例所示: + +属性 + +``` +spring.cache.ehcache.config=classpath:config/another-config.xml +``` + +Yaml + +``` +spring: + cache: + ehcache: + config: "classpath:config/another-config.xml" +``` + +#### 1.1.4.黑泽尔卡斯特 + +Spring 引导有[对 Hazelcast 的一般支持](#io.hazelcast)。如果`HazelcastInstance`已被自动配置,则它将自动包装在`CacheManager`中。 + +#### 1.1.5.无限极 + +[Infinispan](https://infinispan.org/)没有默认的配置文件位置,因此必须显式地指定它。否则,将使用默认的引导程序。 + +属性 + +``` +spring.cache.infinispan.config=infinispan.xml +``` + +Yaml + +``` +spring: + cache: + infinispan: + config: "infinispan.xml" +``` + +通过设置`spring.cache.cache-names`属性,可以在启动时创建缓存。如果定义了自定义`ConfigurationBuilder` Bean,则使用它来定制缓存。 + +| |Spring boot 中对 Infinispan 的支持仅限于嵌入式模式,非常基本。
如果你想要更多选项,应该使用官方的 Infinispan Spring boot starter。
查看[Infinispan 的文档](https://github.com/infinispan/infinispan-spring-boot)了解更多详细信息。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.6.Couchbase + +如果 Spring 数据 Couchbase 是可用的,并且 Couchbase 是[configured](data.html#data.nosql.couchbase),则将自动配置`CouchbaseCacheManager`。通过设置`spring.cache.cache-names`属性,可以在启动时创建额外的缓存,并且可以使用`spring.cache.couchbase.*`属性配置缓存默认值。例如,下面的配置使用一个 10 分钟的条目*过期*创建`cache1`和`cache2`缓存: + +属性 + +``` +spring.cache.cache-names=cache1,cache2 +spring.cache.couchbase.expiration=10m +``` + +Yaml + +``` +spring: + cache: + cache-names: "cache1,cache2" + couchbase: + expiration: "10m" +``` + +如果需要对配置进行更多控制,可以考虑注册`CouchbaseCacheManagerBuilderCustomizer` Bean。下面的示例显示了一个定制程序,它为`cache1`和`cache2`配置一个特定的条目过期: + +``` +import java.time.Duration; + +import org.springframework.boot.autoconfigure.cache.CouchbaseCacheManagerBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.couchbase.cache.CouchbaseCacheConfiguration; + +@Configuration(proxyBeanMethods = false) +public class MyCouchbaseCacheManagerConfiguration { + + @Bean + public CouchbaseCacheManagerBuilderCustomizer myCouchbaseCacheManagerBuilderCustomizer() { + return (builder) -> builder + .withCacheConfiguration("cache1", CouchbaseCacheConfiguration + .defaultCacheConfig().entryExpiry(Duration.ofSeconds(10))) + .withCacheConfiguration("cache2", CouchbaseCacheConfiguration + .defaultCacheConfig().entryExpiry(Duration.ofMinutes(1))); + + } + +} + +``` + +#### 1.1.7.雷迪斯 + +如果[Redis](https://redis.io/)可用并已配置,则自动配置`RedisCacheManager`。通过设置`spring.cache.cache-names`属性,可以在启动时创建额外的缓存,并且可以使用`spring.cache.redis.*`属性配置缓存默认值。例如,下面的配置使用 10 分钟的*活下去的时间*创建`cache1`和`cache2`缓存: + +属性 + +``` +spring.cache.cache-names=cache1,cache2 +spring.cache.redis.time-to-live=10m +``` + +Yaml + +``` +spring: + cache: + cache-names: "cache1,cache2" + redis: + time-to-live: "10m" +``` + +| |默认情况下,会添加一个密钥前缀,这样,如果两个独立的缓存使用相同的密钥,则 Redis 没有重叠的密钥,并且不能返回无效的值。
如果你创建自己的`RedisCacheManager`,我们强烈建议启用此设置。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |你可以通过添加自己的`RedisCacheConfiguration``@Bean`来完全控制默认的配置。
如果你需要自定义默认的序列化策略,这将非常有用。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果需要对配置进行更多控制,可以考虑注册`RedisCacheManagerBuilderCustomizer` Bean。下面的示例显示了一个定制程序,它为`cache1`和`cache2`配置了一个特定的生存时间: + +``` +import java.time.Duration; + +import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; + +@Configuration(proxyBeanMethods = false) +public class MyRedisCacheManagerConfiguration { + + @Bean + public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() { + return (builder) -> builder + .withCacheConfiguration("cache1", RedisCacheConfiguration + .defaultCacheConfig().entryTtl(Duration.ofSeconds(10))) + .withCacheConfiguration("cache2", RedisCacheConfiguration + .defaultCacheConfig().entryTtl(Duration.ofMinutes(1))); + + } + +} + +``` + +#### 1.1.8.咖啡因 + +[Caffeine](https://github.com/ben-manes/caffeine)是对番石榴缓存的 Java8 重写,取代了对番石榴的支持。如果存在咖啡因,则自动配置`CaffeineCacheManager`(由`spring-boot-starter-cache`“starter”提供)。通过设置`spring.cache.cache-names`属性,可以在启动时创建缓存,并且可以通过以下方式之一(按指定的顺序)进行定制: + +1. 由`spring.cache.caffeine.spec`定义的缓存规范 + +2. 定义了`com.github.benmanes.caffeine.cache.CaffeineSpec` Bean + +3. 定义了`com.github.benmanes.caffeine.cache.Caffeine` Bean + +例如,下面的配置创建了`cache1`和`cache2`缓存,缓存的最大大小为 500,*活下去的时间*为 10 分钟 + +属性 + +``` +spring.cache.cache-names=cache1,cache2 +spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s +``` + +Yaml + +``` +spring: + cache: + cache-names: "cache1,cache2" + caffeine: + spec: "maximumSize=500,expireAfterAccess=600s" +``` + +如果定义了`com.github.benmanes.caffeine.cache.CacheLoader` Bean,则它将自动关联到`CaffeineCacheManager`。由于`CacheLoader`将与由缓存管理器管理的*全部*缓存相关联,因此必须将其定义为`CacheLoader`。自动配置忽略任何其他通用类型。 + +#### 1.1.9.简单 + +如果找不到其他提供程序,则配置一个使用`ConcurrentHashMap`作为缓存存储的简单实现。如果应用程序中不存在缓存库,这是默认的。默认情况下,缓存是根据需要创建的,但是你可以通过设置`cache-names`属性来限制可用缓存的列表。例如,如果只想要`cache1`和`cache2`缓存,请将`cache-names`属性设置如下: + +属性 + +``` +spring.cache.cache-names=cache1,cache2 +``` + +Yaml + +``` +spring: + cache: + cache-names: "cache1,cache2" +``` + +如果你这样做,而你的应用程序使用了一个未列出的缓存,那么当需要缓存时,它会在运行时失败,而不是在启动时。如果使用未声明的缓存,这类似于“真正的”缓存提供者的行为。 + +#### 1.1.10.无 + +当`@EnableCaching`出现在你的配置中时,还需要一个合适的缓存配置。如果需要在某些环境中完全禁用缓存,请强制缓存类型`none`使用无操作实现,如以下示例所示: + +属性 + +``` +spring.cache.type=none +``` + +Yaml + +``` +spring: + cache: + type: "none" +``` + +## 2. Hazelcast + +如果[Hazelcast](https://hazelcast.com/)在 Classpath 上,并且找到了合适的配置, Spring 引导自动配置一个`HazelcastInstance`,你可以将其注入到应用程序中。 + +Spring 通过检查以下配置选项,启动首先尝试创建客户端: + +* 存在`com.hazelcast.client.config.ClientConfig` Bean。 + +* 由`spring.hazelcast.config`属性定义的配置文件。 + +* 存在`hazelcast.client.config`系统属性。 + +* 在 Classpath 的工作目录或根目录中的`hazelcast-client.xml`。 + +* 在 Classpath 的工作目录或根目录中的`hazelcast-client.yaml`。 + +| |Spring Boot 同时支持 Hazelcast4 和 Hazelcast3。
如果降级为 Hazelcast3,则应将`hazelcast-client`添加到 Classpath 中以配置客户端。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 如果无法创建客户端,则引导尝试配置嵌入式服务器。如果你定义了`com.hazelcast.config.Config` Bean, Spring boot 将使用它。如果你的配置定义了一个实例名, Spring 引导将尝试定位一个现有的实例,而不是创建一个新的实例。 + +你还可以指定通过配置使用的 HazelCast 配置文件,如以下示例所示: + +属性 + +``` +spring.hazelcast.config=classpath:config/my-hazelcast.xml +``` + +Yaml + +``` +spring: + hazelcast: + config: "classpath:config/my-hazelcast.xml" +``` + +否则, Spring 引导将尝试从默认位置查找 HazelCast 配置:工作目录中或 Classpath 根目录中的`hazelcast.xml`,或相同位置中的`.yaml`对应位置。我们还检查`hazelcast.config`系统属性是否已设置。有关更多详细信息,请参见[Hazelcast 文档](https://docs.hazelcast.org/docs/latest/manual/html-single/)。 + +| |Spring 引导还具有[对 HazelCast 的显式缓存支持](#io.caching.provider.hazelcast)。
如果启用了缓存,`HazelcastInstance`将自动包装在`CacheManager`实现中。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 3. 石英调度器 + +Spring Boot 为使用[石英调度器](https://www.quartz-scheduler.org/)提供了几种便利,包括`spring-boot-starter-quartz`“starter”。如果 Quartz 可用,则自动配置`Scheduler`(通过`SchedulerFactoryBean`抽象)。 + +以下类型的 bean 将被自动拾取并与`Scheduler`关联: + +* `JobDetail`:定义特定的作业。`JobDetail’实例可以用`JobBuilder`API 构建。 + +* `Calendar`. + +* `Trigger`:定义触发特定作业的时间。 + +默认情况下,使用内存`JobStore`。但是,如果`DataSource` Bean 在你的应用程序中可用,并且`spring.quartz.job-store-type`属性被相应地配置,则可以配置基于 JDBC 的存储,如以下示例所示: + +属性 + +``` +spring.quartz.job-store-type=jdbc +``` + +Yaml + +``` +spring: + quartz: + job-store-type: "jdbc" +``` + +当使用 JDBC 存储时,可以在启动时初始化模式,如以下示例所示: + +属性 + +``` +spring.quartz.jdbc.initialize-schema=always +``` + +Yaml + +``` +spring: + quartz: + jdbc: + initialize-schema: "always" +``` + +| |默认情况下,通过使用 Quartz 库提供的标准脚本来检测和初始化数据库。
这些脚本删除现有的表,在每次重新启动时删除所有触发器。
还可以通过设置`spring.quartz.jdbc.schema`属性来提供自定义脚本。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要让 Quartz 使用应用程序的主`DataSource`以外的`DataSource`,请声明一个`DataSource` Bean,并用`@QuartzDataSource`注释其`@Bean`方法。这样做可以确保`SchedulerFactoryBean`和模式初始化都使用特定于石英的`DataSource`。类似地,要使 Quartz 使用一个`TransactionManager`而不是应用程序的主`TransactionManager`声明一个`TransactionManager` Bean,用`@QuartzTransactionManager`注释其`@Bean`方法。 + +默认情况下,通过配置创建的作业不会覆盖已注册的、已从持久作业存储区读取的作业。要启用覆盖现有的作业定义,请设置`spring.quartz.overwrite-existing-jobs`属性。 + +Quartz 调度器配置可以使用`spring.quartz`属性和`SchedulerFactoryBeanCustomizer`bean 进行自定义,这允许程序化的`SchedulerFactoryBean`自定义。可以使用`spring.quartz.properties.*`定制高级石英配置属性。 + +| |特别地,`Executor` Bean 不与调度程序相关联,因为 Quartz 提供了一种通过`spring.quartz.properties`配置调度程序的方法。
如果需要定制任务执行器,请考虑实现`SchedulerFactoryBeanCustomizer`。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +作业可以定义 setter 来注入数据映射属性。普通的豆子也可以以类似的方式注入,如以下示例所示: + +``` +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +import org.springframework.scheduling.quartz.QuartzJobBean; + +public class MySampleJob extends QuartzJobBean { + + // fields ... + + private MyService myService; + + private String name; + + // Inject "MyService" bean + public void setMyService(MyService myService) { + this.myService = myService; + } + + // Inject the "name" job data property + public void setName(String name) { + this.name = name; + } + + @Override + protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + this.myService.someMethod(context.getFireTime(), this.name); + } + +} + +``` + +## 4. 发送电子邮件 + +Spring 框架提供了用于通过使用`JavaMailSender`接口发送电子邮件的抽象,并且 Spring 启动为其提供了自动配置以及一个启动模块。 + +| |有关如何使用`JavaMailSender`的详细说明,请参见[参考文献](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/integration.html#mail)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果`spring.mail.host`和相关的库(由`spring-boot-starter-mail`定义)是可用的,则如果不存在,则创建一个默认的`JavaMailSender`。发送方可以通过`spring.mail`名称空间中的配置项进行进一步定制。有关更多详细信息,请参见[“Mail属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/Mail属性.java)。 + +特别是,某些默认的超时值是无限的,你可能希望对其进行更改,以避免线程被无响应的邮件服务器阻塞,如以下示例所示: + +属性 + +``` +spring.mail.properties[mail.smtp.connectiontimeout]=5000 +spring.mail.properties[mail.smtp.timeout]=3000 +spring.mail.properties[mail.smtp.writetimeout]=5000 +``` + +Yaml + +``` +spring: + mail: + properties: + "[mail.smtp.connectiontimeout]": 5000 + "[mail.smtp.timeout]": 3000 + "[mail.smtp.writetimeout]": 5000 +``` + +也可以使用 JNDI 中现有的`Session`配置`JavaMailSender`: + +Properties + +``` +spring.mail.jndi-name=mail/Session +``` + +Yaml + +``` +spring: + mail: + jndi-name: "mail/Session" +``` + +当设置`jndi-name`时,它优先于所有其他与会话相关的设置。 + +## 5. 验证 + +Bean 验证 1.1 支持的方法验证功能是自动启用的,只要在 Classpath 上有一个 JSR-303 实现(例如 Hibernate 验证器)。这使得 Bean 方法在其参数和/或返回值上使用`javax.validation`约束进行注释。具有这种注释方法的目标类需要在类型级别使用`@Validated`注释进行注释,以便在内联约束注释中搜索它们的方法。 + +例如,下面的服务将触发第一个参数的验证,以确保其大小在 8 到 10 之间: + +``` +import javax.validation.constraints.Size; + +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +@Service +@Validated +public class MyBean { + + public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code, Author author) { + return ... + } + +} + +``` + +在解析约束消息中的`{parameters}`时,将使用应用程序的`MessageSource`。这允许你对 Bean 验证消息使用[your application’s `messages.properties` files](features.html#features.internationalization)。一旦解决了参数问题,就使用 Bean 验证的默认内插器完成消息插值。 + +## 6. 呼叫 REST 服务 + +如果你的应用程序调用远程 REST 服务, Spring 启动使用`RestTemplate`或`WebClient`使这一点非常方便。 + +### 6.1.RESTTemplate + +如果需要从应用程序调用远程 REST 服务,可以使用 Spring 框架的[`RestTemplate`](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/web/client/RestTemplate.html)类。由于`RestTemplate`实例在使用之前通常需要进行自定义, Spring Boot 不提供任何单独的自动配置`RestTemplate` Bean。但是,它会自动配置`RestTemplateBuilder`,在需要时可以使用它来创建`RestTemplate`实例。自动配置的`RestTemplateBuilder`确保将合理的`HttpMessageConverters`应用于`RestTemplate`实例。 + +下面的代码展示了一个典型的示例: + +``` +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class MyService { + + private final RestTemplate restTemplate; + + public MyService(RestTemplateBuilder restTemplateBuilder) { + this.restTemplate = restTemplateBuilder.build(); + } + + public Details someRestCall(String name) { + return this.restTemplate.getForObject("/{name}/details", Details.class, name); + } + +} + +``` + +| |`RestTemplateBuilder`包括许多有用的方法,可以用来快速配置`RestTemplate`。
例如,要添加基本的 auth 支持,可以使用`builder.basicAuthentication("user", "password").build()`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 6.1.1.resttemplate 定制 + +对于`RestTemplate`定制,有三种主要的方法,这取决于你希望定制应用的范围。 + +要使任何定制的范围尽可能窄,请插入自动配置的`RestTemplateBuilder`,然后根据需要调用其方法。每个方法调用返回一个新的`RestTemplateBuilder`实例,因此自定义仅影响构建器的这种使用。 + +要进行应用程序范围的可加性定制,请使用`RestTemplateCustomizer` Bean。所有这样的 bean 都会自动注册到自动配置的`RestTemplateBuilder`中,并应用到用它构建的任何模板中。 + +下面的示例显示了一个自定义程序,它为除`192.168.0.5`以外的所有主机配置代理的使用: + +``` +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultProxyRoutePlanner; +import org.apache.http.protocol.HttpContext; + +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +public class MyRestTemplateCustomizer implements RestTemplateCustomizer { + + @Override + public void customize(RestTemplate restTemplate) { + HttpRoutePlanner routePlanner = new CustomRoutePlanner(new HttpHost("proxy.example.com")); + HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(routePlanner).build(); + restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient)); + } + + static class CustomRoutePlanner extends DefaultProxyRoutePlanner { + + CustomRoutePlanner(HttpHost proxy) { + super(proxy); + } + + @Override + public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context) throws HttpException { + if (target.getHostName().equals("192.168.0.5")) { + return null; + } + return super.determineProxy(target, request, context); + } + + } + +} + +``` + +最后,你可以定义自己的`RestTemplateBuilder` Bean。这样做将取代自动配置的构建器。如果你希望将任何`RestTemplateCustomizer`bean 应用到你的自定义构建器,正如自动配置所做的那样,请使用`RestTemplateBuilderConfigurer`对其进行配置。下面的示例公开了一个`RestTemplateBuilder`,它与 Spring boot 的自动配置所做的匹配,除了还指定了自定义连接和读取超时之外: + +``` +import java.time.Duration; + +import org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyRestTemplateBuilderConfiguration { + + @Bean + public RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer configurer) { + return configurer.configure(new RestTemplateBuilder()).setConnectTimeout(Duration.ofSeconds(5)) + .setReadTimeout(Duration.ofSeconds(2)); + } + +} + +``` + +最极端(也很少使用)的选项是在不使用配置器的情况下创建自己的`RestTemplateBuilder` Bean。除了替换自动配置的生成器外,这还可以防止使用任何`RestTemplateCustomizer`bean。 + +### 6.2.WebClient + +如果你的 Classpath 上有 Spring WebFlux,你还可以选择使用`WebClient`来调用远程 REST 服务。与`RestTemplate`相比,该客户机具有更多的功能,并且完全具有反应性。你可以在专用的[section in the Spring Framework docs](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#webflux-client)中了解有关`WebClient`的更多信息。 + +Spring 引导为你创建并预配置一个`WebClient.Builder`。强烈建议将其注入到组件中,并使用它来创建`WebClient`实例。 Spring 引导正在配置该构建器来共享 HTTP 资源,以与服务器相同的方式反映编解码器设置(参见[WebFlux HTTP 编解码器自动配置](web.html#web.reactive.webflux.httpcodecs)),等等。 + +下面的代码展示了一个典型的示例: + +``` +import org.neo4j.cypherdsl.core.Relationship.Details; +import reactor.core.publisher.Mono; + +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; + +@Service +public class MyService { + + private final WebClient webClient; + + public MyService(WebClient.Builder webClientBuilder) { + this.webClient = webClientBuilder.baseUrl("https://example.org").build(); + } + + public Mono
someRestCall(String name) { + return this.webClient.get().uri("/{name}/details", name).retrieve().bodyToMono(Details.class); + } + +} + +``` + +#### 6.2.1.WebClient 运行时 + +Spring 根据应用程序上可用的库 Classpath,启动将自动检测要使用哪个`ClientHttpConnector`来驱动`WebClient`。目前,反应堆网络和 Jetty RS 客户端是受支持的。 + +默认情况下,`spring-boot-starter-webflux`starter 依赖于`io.projectreactor.netty:reactor-netty`,这带来了服务器和客户端实现。如果你选择使用 Jetty 作为反应性服务器,那么你应该在 Jetty 反应性 HTTP 客户库上添加一个依赖项,`org.eclipse.jetty:jetty-reactive-httpclient`。使用同样的技术对服务器和客户端都有它的优点,因为它会自动地在客户端和服务器之间共享 HTTP 资源。 + +开发人员可以通过提供自定义`ReactorResourceFactory`或`JettyResourceFactory` Bean 来覆盖 Jetty 和反应器网络的资源配置-这将同时应用于客户机和服务器。 + +如果你希望为客户机重写该选择,那么你可以定义自己的`ClientHttpConnector` Bean 并对客户机配置拥有完全的控制权。 + +你可以了解更多关于[`WebClient` configuration options in the Spring Framework reference documentation](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#webflux-client-builder)的信息。 + +#### 6.2.2.WebClient 自定义 + +对于`WebClient`定制,有三种主要的方法,这取决于你希望定制应用的范围。 + +要使任何自定义的范围尽可能窄,请插入自动配置的`WebClient.Builder`,然后根据需要调用其方法。`Webclient.Builder’实例是有状态的:构建器上的任何更改都反映在随后与其一起创建的所有客户机中。如果你想用同一个构建器创建多个客户机,还可以考虑用`WebClient.Builder other = builder.clone();`克隆构建器。 + +要对所有`WebClient.Builder`实例进行应用程序范围的加性定制,你可以声明`WebClientCustomizer`bean,并在注入点本地更改`WebClient.Builder`。 + +最后,你可以返回到原始 API 并使用`WebClient.create()`。在这种情况下,不应用自动配置或`WebClientCustomizer`。 + +## 7. Web 服务 + +Spring 启动提供了 Web 服务自动配置,因此你所必须做的就是定义你的`Endpoints`。 + +[Spring Web Services features](https://docs.spring.io/spring-ws/docs/3.1.2/reference/html/)模块可以轻松访问`spring-boot-starter-webservices`。 + +`SimpleWsdl11Definition`和`SimpleXsdSchema`bean 可以分别为你的 WSDL 和 XSD 自动创建。要做到这一点,请配置它们的位置,如以下示例所示: + +Properties + +``` +spring.webservices.wsdl-locations=classpath:/wsdl +``` + +Yaml + +``` +spring: + webservices: + wsdl-locations: "classpath:/wsdl" +``` + +### 7.1.使用 WebServiceTemplate 调用 Web 服务 + +如果需要从应用程序调用远程 Web 服务,可以使用[“WebServiceTemplate”](https://docs.spring.io/spring-ws/docs/3.1.2/reference/html/#client-web-service-template)类。由于`WebServiceTemplate`实例在使用之前通常需要进行自定义, Spring boot 不提供任何单独的自动配置`WebServiceTemplate` Bean。但是,它可以自动配置`WebServiceTemplateBuilder`,在需要时可以使用它来创建`WebServiceTemplate`实例。 + +下面的代码展示了一个典型的示例: + +``` +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.stereotype.Service; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.soap.client.core.SoapActionCallback; + +@Service +public class MyService { + + private final WebServiceTemplate webServiceTemplate; + + public MyService(WebServiceTemplateBuilder webServiceTemplateBuilder) { + this.webServiceTemplate = webServiceTemplateBuilder.build(); + } + + public SomeResponse someWsCall(SomeRequest detailsReq) { + return (SomeResponse) this.webServiceTemplate.marshalSendAndReceive(detailsReq, + new SoapActionCallback("https://ws.example.com/action")); + } + +} + +``` + +默认情况下,`WebServiceTemplateBuilder`使用 Classpath 上可用的 HTTP 客户库来检测合适的基于 HTTP 的`WebServiceMessageSender`。你还可以自定义读取和连接超时,如下所示: + +``` +import java.time.Duration; + +import org.springframework.boot.webservices.client.HttpWebServiceMessageSenderBuilder; +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.WebServiceMessageSender; + +@Configuration(proxyBeanMethods = false) +public class MyWebServiceTemplateConfiguration { + + @Bean + public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) { + WebServiceMessageSender sender = new HttpWebServiceMessageSenderBuilder() + .setConnectTimeout(Duration.ofSeconds(5)) + .setReadTimeout(Duration.ofSeconds(2)) + .build(); + return builder.messageSenders(sender).build(); + } + +} + +``` + +## 8. 使用 JTA 的分布式事务 + +Spring 通过使用[Atomikos](https://www.atomikos.com/)嵌入式事务管理器,启动支持跨多个 XA 资源的分布式 JTA 事务。当部署到合适的 Java EE 应用程序服务器时,也支持 JTA 事务。 + +当检测到 JTA 环境时,将使用 Spring 的`JtaTransactionManager`来管理事务。升级了自动配置的 JMS、数据源和 JPA bean,以支持 XA 事务。你可以使用标准 Spring 习惯用法,例如`@Transactional`,来参与分布式事务。如果你处于 JTA 环境中,并且仍然希望使用本地事务,那么可以将`spring.jta.enabled`属性设置为`false`以禁用 JTA 自动配置。 + +### 8.1.使用 Atomikos 事务管理器 + +[Atomikos](https://www.atomikos.com/)是一种流行的开源事务管理器,它可以嵌入到你的 Spring 引导应用程序中。你可以使用`spring-boot-starter-jta-atomikos`启动器来获取适当的 Atomikos 库。 Spring 引导自动配置 Atomikos,并确保将适当的`depends-on`设置应用到你的 Spring bean,以进行正确的启动和关闭排序。 + +默认情况下,Atomikos 事务日志被写入应用程序主目录(应用程序 jar 文件所在的目录)中的`transaction-logs`目录。可以通过在`application.properties`文件中设置`spring.jta.log-dir`属性来自定义此目录的位置。以`spring.jta.atomikos.properties`开头的属性也可用于自定义 Atomikos`UserTransactionServiceImp`。有关完整的详细信息,请参见[`AtomikosProperties`Javadoc](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/jta/atomikos/AtomikosProperties.html)。 + +| |为了确保多个事务管理器能够安全地协调相同的资源管理器,每个 Atomikos 实例都必须配置一个唯一的 ID,
默认情况下,这个 ID 是运行 Atomikos 的机器的 IP 地址,
以确保生产中的唯一性,你应该为应用程序的每个实例配置具有不同值的`spring.jta.transaction-manager-id`属性。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 8.2.使用 Java EE 托管事务管理器 + +如果将 Spring 引导应用程序打包为`war`或`ear`文件,并将其部署到 Java EE 应用服务器,则可以使用应用服务器的内置事务管理器。 Spring 引导试图通过查看常见的 JNDI 位置(`java:comp/usertransaction`,`java:comp/TransactionManager`,等等)来自动配置事务管理器。如果使用应用程序服务器提供的事务服务,通常还需要确保所有资源都由服务器管理,并通过 JNDI 公开。 Spring Boot 试图通过在 JNDI 路径(`java:/jmsxa` 或`java:/XAConnectionFactory`)上查找`ConnectionFactory`来自动配置 JMS,并且你可以使用[`spring.datasource.jndi-name` property](data.html#data.sql.datasource.jndi)来配置你的`DataSource`。 + +### 8.3.混合 XA 和非 XA JMS 连接 + +在使用 JTA 时,主 JMS`ConnectionFactory` Bean 是可感知 XA 的,并参与分布式事务。你可以在不需要使用任何`@Qualifier`的情况下注入到你的 Bean: + +``` +public MyBean(ConnectionFactory connectionFactory) { + // ... +} + +``` + +在某些情况下,你可能希望通过使用非 XA`ConnectionFactory`来处理某些 JMS 消息。例如,你的 JMS 处理逻辑可能需要比 XA 超时更长的时间。 + +如果要使用非 XA`ConnectionFactory`,则可以使用`nonXaJmsConnectionFactory` Bean: + +``` +public MyBean(@Qualifier("nonXaJmsConnectionFactory") ConnectionFactory connectionFactory) { + // ... +} + +``` + +为了保持一致性,`jmsConnectionFactory` Bean 还通过使用 Bean 别名`xaJmsConnectionFactory`来提供: + +``` +public MyBean(@Qualifier("xaJmsConnectionFactory") ConnectionFactory connectionFactory) { + // ... +} + +``` + +### 8.4.支持另一种嵌入式事务管理器 ### + +[xaconnectionFactoryWrapper](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jms/XAConnectionFactoryWrapper.java)和[XadataSourceWrapper](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/XADataSourceWrapper.java)接口可用于支持替代的嵌入式事务管理器。接口负责包装`XAConnectionFactory`和`XADataSource`bean,并将它们公开为常规的`ConnectionFactory`和`DataSource`bean,这些 bean 透明地登记在分布式事务中。数据源和 JMS 自动配置使用 JTA 变体,只要你有`JtaTransactionManager` Bean 和在`ApplicationContext`中注册的适当的 XA 包装 bean。 + +[AtomikosxaconnectionFactoryWrapper](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jta/atomikos/AtomikosXAConnectionFactoryWrapper.java)和[Atomikosxadatasourcewrapper](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jta/atomikos/AtomikosXADataSourceWrapper.java)为如何编写 XA 包装器提供了很好的示例。 + +## 9. 接下来读什么? + +现在你应该对 Spring boot 的[core features](features.html#features)以及 Spring boot 通过自动配置提供支持的各种技术有了很好的了解。 + +接下来的几节将详细介绍如何将应用程序部署到云平台上。你可以在下一节中阅读有关[构建容器图像](container-images.html#container-images)的内容,也可以跳到[可投入生产的功能](actuator.html#actuator)的部分。 diff --git a/docs/spring-boot/legal.md b/docs/spring-boot/legal.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d593427c2797fc2b83a8292f845f63bd55d2b81 100644 --- a/docs/spring-boot/legal.md +++ b/docs/spring-boot/legal.md @@ -0,0 +1,5 @@ +# 法律 + +版权所有 2012-2022 + +本文件的副本可供你自己使用并分发给他人,但前提是你不对此类副本收取任何费用,并且还需每一份副本均包含本版权声明,无论是以印刷形式还是以电子方式分发。 diff --git a/docs/spring-boot/messaging.md b/docs/spring-boot/messaging.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b2354870e959f2dc7a8dc26a51d07e8582bbd17d 100644 --- a/docs/spring-boot/messaging.md +++ b/docs/spring-boot/messaging.md @@ -0,0 +1,931 @@ +# 消息传递 + +Spring 框架为与消息传递系统集成提供了广泛的支持,从使用`JmsTemplate`的 JMS API 的简化使用到异步接收消息的完整基础设施。 Spring AMQP 为高级消息队列协议提供了类似的功能集。 Spring Boot 还提供了`RabbitTemplate`和 RabbitMQ 的自动配置选项。 Spring WebSocket 原生地包括对 Stomp 消息传递的支持,并且 Spring 引导通过启动器和少量自动配置对此提供支持。 Spring Boot 还支持 Apache Kafka。 + +## 1. JMS + +`javax.jms.ConnectionFactory`接口提供了一种标准方法,用于创建用于与 JMS 代理交互的`javax.jms.Connection`。尽管 Spring 需要`ConnectionFactory`来处理 JMS,但你通常不需要自己直接使用它,而是可以依赖于更高级别的消息传递抽象。(有关详细信息,请参见 Spring Framework 参考文档的[相关部分](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/integration.html#jms)。) Spring Boot 还自动配置发送和接收消息所需的基础设施。 + +### 1.1.ActiveMQ 支持 + +当[ActiveMQ](https://activemq.apache.org/)在 Classpath 上可用时, Spring 引导也可以配置`ConnectionFactory`。如果存在代理,则会自动启动并配置嵌入式代理(前提是通过配置未指定代理 URL,并且在配置中未禁用嵌入式代理)。 + +| |如果使用`spring-boot-starter-activemq`,则提供了连接或嵌入 ActiveMQ 实例所需的依赖关系, Spring 与 JMS 集成的基础设施也是如此。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +ActiveMQ 配置由`spring.activemq.*`中的外部配置属性控制。 + +默认情况下,ActiveMQ 被自动配置为使用[VM transport](https://activemq.apache.org/vm-transport-reference.html),它启动嵌入在同一 JVM 实例中的代理。 + +可以通过配置`spring.activemq.in-memory`属性禁用嵌入式代理,如以下示例所示: + +属性 + +``` +spring.activemq.in-memory=false +``` + +Yaml + +``` +spring: + activemq: + in-memory: false +``` + +如果你配置代理 URL,那么嵌入式代理也将被禁用,如以下示例所示: + +属性 + +``` +spring.activemq.broker-url=tcp://192.168.1.210:9876 +spring.activemq.user=admin +spring.activemq.password=secret +``` + +Yaml + +``` +spring: + activemq: + broker-url: "tcp://192.168.1.210:9876" + user: "admin" + password: "secret" +``` + +如果你想完全控制嵌入式代理,请参阅[ActiveMQ 文档](https://activemq.apache.org/how-do-i-embed-a-broker-inside-a-connection.html)以获取更多信息。 + +默认情况下,`CachingConnectionFactory`将本机`ConnectionFactory`包装成合理的设置,你可以通过`spring.jms.*`中的外部配置属性来控制这些设置: + +属性 + +``` +spring.jms.cache.session-cache-size=5 +``` + +Yaml + +``` +spring: + jms: + cache: + session-cache-size: 5 +``` + +如果你更愿意使用本机池,那么可以通过向`org.messaginghub:pooled-jms`添加一个依赖项并相应地配置`JmsPoolConnectionFactory`来实现,如下面的示例所示: + +属性 + +``` +spring.activemq.pool.enabled=true +spring.activemq.pool.max-connections=50 +``` + +Yaml + +``` +spring: + activemq: + pool: + enabled: true + max-connections: 50 +``` + +| |有关更多支持的选项,请参见[“ActiveMQ属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/activemq/ActiveMQ属性.java)。
你还可以注册任意数量的实现`ActiveMQConnectionFactoryCustomizer`的 bean,以获得更高级的自定义。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +默认情况下,ActiveMQ 会创建一个尚不存在的目标,以便根据其提供的名称对目标进行解析。 + +### 1.2.ActiveMQ Artemis 支持 + +Spring 当在 Classpath 上检测到[ActiveMQ Artemis](https://activemq.apache.org/components/artemis/)可用时,启动可以自动配置`ConnectionFactory`。如果存在代理,则会自动启动和配置嵌入式代理(除非已显式设置了 Mode 属性)。支持的模式是`embedded`(明确表示需要嵌入式代理,并且如果代理在 Classpath 上不可用,则应该发生错误)和`native`(使用`netty`传输协议连接到代理)。当对后者进行配置时, Spring 引导将配置一个`ConnectionFactory`,该代理连接到使用默认设置在本地计算机上运行的代理。 + +| |如果使用`spring-boot-starter-artemis`,则提供了连接到现有 ActiveMQ Artemis 实例的必要依赖项,以及 Spring 与 JMS 集成的基础设施。
将`org.apache.activemq:artemis-jms-server`添加到应用程序中,可以使用嵌入式模式。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +ActiveMQ Artemis 配置由`spring.artemis.*`中的外部配置属性控制。例如,你可以在`application.properties`中声明以下部分: + +属性 + +``` +spring.artemis.mode=native +spring.artemis.broker-url=tcp://192.168.1.210:9876 +spring.artemis.user=admin +spring.artemis.password=secret +``` + +Yaml + +``` +spring: + artemis: + mode: native + broker-url: "tcp://192.168.1.210:9876" + user: "admin" + password: "secret" +``` + +嵌入代理时,你可以选择是否要启用持久性,并列出应该使其可用的目标。它们可以指定为逗号分隔的列表,以使用默认选项创建它们,或者你可以分别为高级队列和主题配置定义类型`org.apache.activemq.artemis.jms.server.config.JMSQueueConfiguration`或`org.apache.activemq.artemis.jms.server.config.TopicConfiguration`的 Bean(s)。 + +默认情况下,`CachingConnectionFactory`将本机`ConnectionFactory`包装成合理的设置,你可以通过`spring.jms.*`中的外部配置属性来控制这些设置: + +属性 + +``` +spring.jms.cache.session-cache-size=5 +``` + +Yaml + +``` +spring: + jms: + cache: + session-cache-size: 5 +``` + +如果你更愿意使用本机池,那么可以通过向`org.messaginghub:pooled-jms`添加一个依赖项并相应地配置`JmsPoolConnectionFactory`来实现,如下面的示例所示: + +属性 + +``` +spring.artemis.pool.enabled=true +spring.artemis.pool.max-connections=50 +``` + +Yaml + +``` +spring: + artemis: + pool: + enabled: true + max-connections: 50 +``` + +有关更多支持的选项,请参见[` 青蒿素特性’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/artemis/Artemis属性.java)。 + +不涉及 JNDI 查找,并且使用 Artemis 配置中的`name`属性或通过配置提供的名称,根据它们的名称解析目标。 + +### 1.3.使用 JNDI ConnectionFactory + +如果你在应用程序服务器中运行你的应用程序, Spring 引导将尝试通过使用 JNDI 来定位 JMS`ConnectionFactory`。默认情况下,将检查`java:/JmsXA`和`java:/XAConnectionFactory`位置。如果需要指定替代位置,可以使用`spring.jms.jndi-name`属性,如下例所示: + +属性 + +``` +spring.jms.jndi-name=java:/MyConnectionFactory +``` + +Yaml + +``` +spring: + jms: + jndi-name: "java:/MyConnectionFactory" +``` + +### 1.4.发送消息 + +Spring 的`JmsTemplate`是自动配置的,你可以直接将其自动连接到自己的 bean 中,如以下示例所示: + +``` +import org.springframework.jms.core.JmsTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final JmsTemplate jmsTemplate; + + public MyBean(JmsTemplate jmsTemplate) { + this.jmsTemplate = jmsTemplate; + } + + // ... + + public void someMethod() { + this.jmsTemplate.convertAndSend("hello"); + } + +} + +``` + +| |[jmsmessagingtemplate’](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/jms/core/JmsMessagingTemplate.html)可以以类似的方式注入。
如果定义了一个`DestinationResolver`或一个`MessageConverter` Bean,则自动将其关联到自动配置的`JmsTemplate`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.5.接收消息 + +当存在 JMS 基础设施时,任何 Bean 都可以用`@JmsListener`进行注释,以创建侦听器端点。如果没有`JmsListenerContainerFactory`被定义,一个默认的是自动配置的。如果定义了`DestinationResolver`、`MessageConverter`或`javax.jms.ExceptionListener`bean,则它们将自动与默认工厂关联。 + +默认情况下,默认工厂是事务性的。如果在存在`JtaTransactionManager`的基础设施中运行,则默认情况下它与侦听器容器关联。如果没有,则启用`sessionTransacted`标志。在后一种场景中,可以通过在侦听器方法(或其委托)上添加`@Transactional`,将本地数据存储事务与传入消息的处理相关联。这可以确保在本地事务完成后确认传入消息。这还包括发送在同一 JMS 会话上执行的响应消息。 + +以下组件在`someQueue`目标上创建一个侦听器端点: + +``` +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @JmsListener(destination = "someQueue") + public void processMessage(String content) { + // ... + } + +} + +``` + +| |有关更多详细信息,请参见[the Javadoc of `@EnableJms`](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/jms/annotation/EnableJms.html)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你需要创建更多的`JmsListenerContainerFactory`实例,或者如果你想要重写默认值, Spring boot 提供一个`DefaultJmsListenerContainerFactoryConfigurer`,你可以使用它初始化`DefaultJmsListenerContainerFactory`,并使用与自动配置的设置相同的设置。 + +例如,下面的示例公开了另一个使用特定`MessageConverter`的工厂: + +``` +import javax.jms.ConnectionFactory; + +import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jms.config.DefaultJmsListenerContainerFactory; + +@Configuration(proxyBeanMethods = false) +public class MyJmsConfiguration { + + @Bean + public DefaultJmsListenerContainerFactory myFactory(DefaultJmsListenerContainerFactoryConfigurer configurer) { + DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); + ConnectionFactory connectionFactory = getCustomConnectionFactory(); + configurer.configure(factory, connectionFactory); + factory.setMessageConverter(new MyMessageConverter()); + return factory; + } + + private ConnectionFactory getCustomConnectionFactory() { + return ... + } + +} + +``` + +然后,你可以在任何`@JmsListener`-注释的方法中使用工厂,如下所示: + +``` +import org.springframework.jms.annotation.JmsListener; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @JmsListener(destination = "someQueue", containerFactory = "myFactory") + public void processMessage(String content) { + // ... + } + +} + +``` + +## 2. AMQP + +高级消息队列协议是一种面向消息中间件的平台中立、线路级协议。 Spring AMQP 项目将核心 Spring 概念应用于基于 AMQP 的消息传递解决方案的开发。 Spring Boot 为通过 RabbitMQ 使用 AMQP 提供了几种便利,包括`spring-boot-starter-amqp`“starter”。 + +### 2.1.RabbitMQ 支持 + +[RabbitMQ](https://www.rabbitmq.com/)是一种基于 AMQP 协议的轻量级、可靠、可扩展和可移植的消息代理。 Spring 使用`RabbitMQ`通过 AMQP 协议进行通信。 + +RabbitMQ 配置由`spring.rabbitmq.*`中的外部配置属性控制。例如,你可以在`application.properties`中声明以下部分: + +属性 + +``` +spring.rabbitmq.host=localhost +spring.rabbitmq.port=5672 +spring.rabbitmq.username=admin +spring.rabbitmq.password=secret +``` + +Yaml + +``` +spring: + rabbitmq: + host: "localhost" + port: 5672 + username: "admin" + password: "secret" +``` + +或者,你可以使用`addresses`属性配置相同的连接: + +属性 + +``` +spring.rabbitmq.addresses=amqp://admin:[email protected] +``` + +Yaml + +``` +spring: + rabbitmq: + addresses: "amqp://admin:[email protected]" +``` + +| |当以这种方式指定地址时,`host`和`port`属性将被忽略。
如果地址使用`amqps`协议,则将自动启用 SSL 支持。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有关更多支持的基于属性的配置选项,请参见[`Rabbit属性’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/Rabbit属性.java)。要配置 Spring AMQP 使用的 RabbitMQ`ConnectionFactory`的较低级别的详细信息,请定义`ConnectionFactoryCustomizer` Bean。 + +如果上下文中存在`ConnectionNameStrategy` Bean,则将自动使用它来命名由自动配置的`CachingConnectionFactory`创建的连接。 + +| |有关更多详细信息,请参见[理解 RabbitMQ 使用的协议 AMQP](https://spring.io/blog/2010/06/14/understanding-amqp-the-protocol-used-by-rabbitmq/)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.2.发送消息 + +Spring 的`AmqpTemplate`和`AmqpAdmin`是自动配置的,你可以将它们直接自动连接到自己的 bean 中,如以下示例所示: + +``` +import org.springframework.amqp.core.AmqpAdmin; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final AmqpAdmin amqpAdmin; + + private final AmqpTemplate amqpTemplate; + + public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) { + this.amqpAdmin = amqpAdmin; + this.amqpTemplate = amqpTemplate; + } + + // ... + + public void someMethod() { + this.amqpAdmin.getQueueInfo("someQueue"); + } + + public void someOtherMethod() { + this.amqpTemplate.convertAndSend("hello"); + } + +} + +``` + +| |[RabbitMessagingTemplate](https://docs.spring.io/spring-amqp/docs/2.4.2/api/org/springframework/amqp/rabbit/core/RabbitMessagingTemplate.html)可以以类似的方式注入。
如果定义了`MessageConverter` Bean,则自动将其关联到自动配置的`AmqpTemplate`。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果有必要,任何被定义为 Bean 的`org.springframework.amqp.core.Queue`都会自动用于在 RabbitMQ 实例上声明相应的队列。 + +要重试操作,你可以在`AmqpTemplate`上启用重试(例如,在代理连接丢失的情况下): + +属性 + +``` +spring.rabbitmq.template.retry.enabled=true +spring.rabbitmq.template.retry.initial-interval=2s +``` + +Yaml + +``` +spring: + rabbitmq: + template: + retry: + enabled: true + initial-interval: "2s" +``` + +默认情况下,重试是禁用的。你还可以通过声明一个`RabbitRetryTemplateCustomizer` Bean 以编程方式定制`RetryTemplate`。 + +如果你需要创建更多的`RabbitTemplate`实例,或者如果你想要重写默认设置, Spring boot 提供了一个`RabbitTemplateConfigurer` Bean,你可以使用它初始化`RabbitTemplate`,并使用与自动配置所使用的工厂相同的设置。 + +### 2.3.接收消息 + +当 Rabbit 基础结构存在时,任何 Bean 都可以用`@RabbitListener`进行注释,以创建侦听器端点。如果没有`RabbitListenerContainerFactory`已被定义,一个默认的`SimpleRabbitListenerContainerFactory`将被自动配置,并且你可以使用`spring.rabbitmq.listener.type`属性切换到一个直接容器。如果定义了`MessageConverter`或`MessageRecoverer` Bean,则会自动与默认工厂关联。 + +以下示例组件在`someQueue`队列上创建一个侦听器端点: + +``` +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @RabbitListener(queues = "someQueue") + public void processMessage(String content) { + // ... + } + +} + +``` + +| |有关更多详细信息,请参见[the Javadoc of `@EnableRabbit`](https://docs.spring.io/spring-amqp/docs/2.4.2/api/org/springframework/amqp/rabbit/annotation/EnableRabbit.html)。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +如果你需要创建更多的`RabbitListenerContainerFactory`实例,或者你想要重写默认值, Spring Boot 提供了一个`SimpleRabbitListenerContainerFactoryConfigurer`和一个`DirectRabbitListenerContainerFactoryConfigurer`,你可以使用它们初始化一个`SimpleRabbitListenerContainerFactory`和一个`DirectRabbitListenerContainerFactory`,其设置与自动配置所使用的工厂相同。 + +| |选择哪种容器类型并不重要。
这两个 bean 是通过自动配置公开的。| +|---|-------------------------------------------------------------------------------------------------------------| + +例如,下面的配置类公开了另一个使用特定`MessageConverter`的工厂: + +``` +import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyRabbitConfiguration { + + @Bean + public SimpleRabbitListenerContainerFactory myFactory(SimpleRabbitListenerContainerFactoryConfigurer configurer) { + SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); + ConnectionFactory connectionFactory = getCustomConnectionFactory(); + configurer.configure(factory, connectionFactory); + factory.setMessageConverter(new MyMessageConverter()); + return factory; + } + + private ConnectionFactory getCustomConnectionFactory() { + return ... + } + +} + +``` + +然后,你可以在任何`@RabbitListener`-注释的方法中使用工厂,如下所示: + +``` +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @RabbitListener(queues = "someQueue", containerFactory = "myFactory") + public void processMessage(String content) { + // ... + } + +} + +``` + +你可以启用重试来处理侦听器抛出异常的情况。默认情况下,使用`RejectAndDontRequeueRecoverer`,但你可以定义自己的`MessageRecoverer`。当重复尝试用完时,消息将被拒绝,如果代理配置为这样做,消息将被丢弃或路由到死信交换。默认情况下,重试是禁用的。你还可以通过声明一个`RabbitRetryTemplateCustomizer` Bean 以编程方式定制`RetryTemplate`。 + +| |默认情况下,如果禁用了重试,并且侦听器抛出了异常,交付将无限期地重试。
你可以通过两种方式修改此行为:将`defaultRequeueRejected`属性设置为`false`所以尝试进行零次重新交付或抛出`AmqpRejectAndDontRequeueException`以表示消息应被拒绝。
后者是在启用重试并达到最大交付尝试次数时使用的机制。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 3. Apache Kafka 支持 + +[Apache Kafka](https://kafka.apache.org/)通过提供`spring-kafka`项目的自动配置而得到支持。 + +Kafka 配置由`spring.kafka.*`中的外部配置属性控制。例如,你可以在`application.properties`中声明以下部分: + +属性 + +``` +spring.kafka.bootstrap-servers=localhost:9092 +spring.kafka.consumer.group-id=myGroup +``` + +Yaml + +``` +spring: + kafka: + bootstrap-servers: "localhost:9092" + consumer: + group-id: "myGroup" +``` + +| |要在启动时创建一个主题,请添加类型`NewTopic`的 Bean。
如果该主题已经存在,则忽略 Bean。| +|---|------------------------------------------------------------------------------------------------------------------| + +有关更多支持的选项,请参见[Kafkaproperties’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/Kafka属性.java)。 + +### 3.1.发送消息 + +Spring 的`KafkaTemplate`是自动配置的,你可以直接在自己的 bean 中自动连接它,如以下示例所示: + +``` +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + private final KafkaTemplate kafkaTemplate; + + public MyBean(KafkaTemplate kafkaTemplate) { + this.kafkaTemplate = kafkaTemplate; + } + + // ... + + public void someMethod() { + this.kafkaTemplate.send("someTopic", "Hello"); + } + +} + +``` + +| |如果定义了属性`spring.kafka.producer.transaction-id-prefix`,则自动配置一个`KafkaTransactionManager`。
同样,如果定义了一个`RecordMessageConverter` Bean,则自动将其关联到自动配置的`KafkaTemplate`。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.2.接收消息 + +当存在 Apache Kafka 基础设施时,任何 Bean 都可以用`@KafkaListener`进行注释,以创建侦听器端点。如果没有`KafkaListenerContainerFactory`被定义,默认的一个是自动配置与`spring.kafka.listener.*`中定义的键。 + +以下组件在`someTopic`主题上创建一个侦听器端点: + +``` +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +@Component +public class MyBean { + + @KafkaListener(topics = "someTopic") + public void processMessage(String content) { + // ... + } + +} + +``` + +如果定义了`KafkaTransactionManager` Bean,则自动将其关联到容器工厂。类似地,如果定义了 a`RecordFilterStrategy`、`CommonErrorHandler`、`AfterRollbackProcessor`或`ConsumerAwareRebalanceListener` Bean,则自动将其与默认工厂关联。 + +根据侦听器类型,与默认工厂关联的是`RecordMessageConverter`或`BatchMessageConverter` Bean。如果批处理侦听器只存在一个`RecordMessageConverter` Bean,则将其包装在`BatchMessageConverter`中。 + +| |自定义`ChainedKafkaTransactionManager`必须标记`@Primary`,因为它通常引用自动配置的`KafkaTransactionManager` Bean。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.3.卡夫卡溪流 + +Spring 对于 Apache,Kafka 提供了一个工厂 Bean 来创建`StreamsBuilder`对象并管理其流的生命周期。 Spring 启动自动配置所需的`KafkaStreamsConfiguration` Bean,只要`kafka-streams`在 Classpath 上并且 Kafka 流是由`@EnableKafkaStreams`注释启用的。 + +启用 Kafka 流意味着必须设置应用程序 ID 和引导程序服务器。前者可以使用`spring.kafka.streams.application-id`进行配置,如果没有设置,则默认为`spring.application.name`。后者可以全局设置,也可以只针对流专门重写。 + +使用专用属性可以获得几个附加属性;可以使用`spring.kafka.streams.properties`名称空间设置其他任意 Kafka 属性。有关更多信息,请参见[features.html](features.html#messaging.kafka.additional-properties)。 + +要使用工厂 Bean,将`StreamsBuilder`连接到你的`@Bean`中,如以下示例所示: + +``` +import org.apache.kafka.common.serialization.Serdes; +import org.apache.kafka.streams.KeyValue; +import org.apache.kafka.streams.StreamsBuilder; +import org.apache.kafka.streams.kstream.KStream; +import org.apache.kafka.streams.kstream.Produced; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafkaStreams; +import org.springframework.kafka.support.serializer.JsonSerde; + +@Configuration(proxyBeanMethods = false) +@EnableKafkaStreams +public class MyKafkaStreamsConfiguration { + + @Bean + public KStream kStream(StreamsBuilder streamsBuilder) { + KStream stream = streamsBuilder.stream("ks1In"); + stream.map(this::uppercaseValue).to("ks1Out", Produced.with(Serdes.Integer(), new JsonSerde<>())); + return stream; + } + + private KeyValue uppercaseValue(Integer key, String value) { + return new KeyValue<>(key, value.toUpperCase()); + } + +} + +``` + +默认情况下,由它创建的`StreamBuilder`对象管理的流是自动启动的。你可以使用`spring.kafka.streams.auto-startup`属性自定义此行为。 + +### 3.4.其他卡夫卡物业 + +Auto Configuration 支持的属性显示在附录的[“集成属性”](application-properties.html#appendix.application-properties.integration)部分中。请注意,在大多数情况下,这些属性(连字符或 CamelCase)直接映射到 Apache Kafka 点线属性。有关详细信息,请参见 Apache Kafka 文档。 + +这些属性中的前几个应用于所有组件(生产者、消费者、管理员和流),但如果你希望使用不同的值,则可以在组件级别指定这些属性。Apache Kafka 将属性的重要性指定为高、中或低。 Spring 引导自动配置支持所有高重要性属性、一些选定的中低属性以及不具有默认值的任何属性。 + +只有 Kafka 支持的属性的一个子集可以直接通过`Kafka属性`类获得。如果你希望为生产者或消费者配置不受直接支持的附加属性,请使用以下属性: + +属性 + +``` +spring.kafka.properties[prop.one]=first +spring.kafka.admin.properties[prop.two]=second +spring.kafka.consumer.properties[prop.three]=third +spring.kafka.producer.properties[prop.four]=fourth +spring.kafka.streams.properties[prop.five]=fifth +``` + +Yaml + +``` +spring: + kafka: + properties: + "[prop.one]": "first" + admin: + properties: + "[prop.two]": "second" + consumer: + properties: + "[prop.three]": "third" + producer: + properties: + "[prop.four]": "fourth" + streams: + properties: + "[prop.five]": "fifth" +``` + +这将普通的`prop.one`Kafka 属性设置为`first`(适用于生产者、消费者和管理员),`prop.two`管理属性设置为`second`,`prop.three`消费者属性设置为`third`,`prop.four`生产者属性设置为`fourth`,`prop.five`流属性设置为`fifth`。 + +还可以将 Spring kafka`JsonDeserializer`配置如下: + +属性 + +``` +spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer +spring.kafka.consumer.properties[spring.json.value.default.type]=com.example.Invoice +spring.kafka.consumer.properties[spring.json.trusted.packages]=com.example.main,com.example.another +``` + +Yaml + +``` +spring: + kafka: + consumer: + value-deserializer: "org.springframework.kafka.support.serializer.JsonDeserializer" + properties: + "[spring.json.value.default.type]": "com.example.Invoice" + "[spring.json.trusted.packages]": "com.example.main,com.example.another" +``` + +类似地,你可以禁用`JsonSerializer`在标题中发送类型信息的默认行为: + +Properties + +``` +spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer +spring.kafka.producer.properties[spring.json.add.type.headers]=false +``` + +Yaml + +``` +spring: + kafka: + producer: + value-serializer: "org.springframework.kafka.support.serializer.JsonSerializer" + properties: + "[spring.json.add.type.headers]": false +``` + +| |以这种方式设置的属性将覆盖 Spring 引导明确支持的任何配置项。| +|---|------------------------------------------------------------------------------------------------| + +### 3.5.嵌入式 Kafka 测试 + +Spring 对于 Apache Kafka 提供了一种方便的方式来测试带有嵌入式 Apache Kafka 代理的项目。要使用此功能,请使用`spring-kafka-test`模块中的`@EmbeddedKafka`对测试类进行注释。有关更多信息,请参见 Apache Kafka[参考手册](https://docs.spring.io/spring-kafka/docs/2.8.3/reference/html/#embedded-kafka-annotation)的 Spring。 + +要使 Spring 引导自动配置与前面提到的嵌入式 Apache Kafka 代理一起工作,你需要将嵌入式代理地址(由`EmbeddedKafkaBroker`填充)的系统属性重新映射到 Apache Kafka 的 Spring 引导配置属性中。有几种方法可以做到这一点: + +* 提供一个系统属性,将嵌入式代理地址映射到测试类中的`spring.kafka.bootstrap-servers`: + +``` +static { + System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY, "spring.kafka.bootstrap-servers"); +} + +``` + +* 在`@EmbeddedKafka`注释上配置属性名: + +``` +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.kafka.test.context.EmbeddedKafka; + +@SpringBootTest +@EmbeddedKafka(topics = "someTopic", bootstrapServersProperty = "spring.kafka.bootstrap-servers") +class MyTest { + + // ... + +} + +``` + +* 在配置属性中使用占位符: + +Properties + +``` +spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers} +``` + +Yaml + +``` +spring: + kafka: + bootstrap-servers: "${spring.embedded.kafka.brokers}" +``` + +## 4. RSocket + +[RSocket](https://rsocket.io)是一种用于字节流传输的二进制协议。它支持通过在单个连接上传递异步消息的对称交互模型。 + +Spring 框架的`spring-messaging`模块在客户端和服务器端为 RSocket 请求者和响应者提供支持。有关更多详细信息,请参见 Spring 框架引用的[RSocket section](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#rsocket-spring),包括 RSocket 协议的概述。 + +### 4.1.RSocket 策略自动配置 + +Spring 引导自动配置`RSocketStrategies` Bean,该配置提供了编码和解码 RSocket 有效负载所需的所有基础设施。默认情况下,自动配置将尝试配置以下内容(按顺序): + +1. [CBOR](https://cbor.io/)带 Jackson 的编解码器 + +2. 具有 Jackson 的 JSON 编解码器 + +`spring-boot-starter-rsocket`启动器提供了这两个依赖项。请参阅[Jackson 支助科](features.html#features.json.jackson)以了解有关自定义可能性的更多信息。 + +开发人员可以通过创建实现`RSocketStrategiesCustomizer`接口的 bean 来定制`RSocketStrategies`组件。请注意,它们的`@Order`很重要,因为它决定了编解码器的顺序。 + +### 4.2.RSocket 服务器自动配置 + +Spring 启动提供了 RSocket 服务器的自动配置。所需的依赖关系由`spring-boot-starter-rsocket`提供。 + +Spring 启动允许在 WebSocket 上从 WebFlux 服务器公开 RSocket,或者在独立的 RSocket 服务器上公开 RSocket。这取决于应用程序的类型及其配置。 + +对于 WebFlux 应用程序(类型为`WebApplicationType.REACTIVE`),只有在以下属性匹配的情况下,才会将 RSocket 服务器插入到 Web 服务器中: + +Properties + +``` +spring.rsocket.server.mapping-path=/rsocket +spring.rsocket.server.transport=websocket +``` + +Yaml + +``` +spring: + rsocket: + server: + mapping-path: "/rsocket" + transport: "websocket" +``` + +| |将 RSocket 插入到 Web 服务器中只受 Reactor Netty 的支持,因为 RSocket 本身是用该库构建的。| +|---|----------------------------------------------------------------------------------------------------------------------| + +或者,RSocket TCP 或 WebSocket 服务器作为独立的嵌入式服务器启动。除了依赖关系需求外,唯一需要的配置是为该服务器定义一个端口: + +Properties + +``` +spring.rsocket.server.port=9898 +``` + +Yaml + +``` +spring: + rsocket: + server: + port: 9898 +``` + +### 4.3. Spring 消息 RSocket 支持 + +Spring 启动将自动为 RSocket 配置 Spring 消息传递基础设施。 + +这意味着 Spring 引导将创建一个`RSocketMessageHandler` Bean,它将处理对应用程序的 RSocket 请求。 + +### 4.4.使用 RSocketRequester 调用 RSocket 服务 + +一旦在服务器和客户端之间建立了`RSocket`通道,任何一方都可以向另一方发送或接收请求。 + +作为服务器,你可以在 RSocket`@Controller`的任何处理程序方法上插入`RSocketRequester`实例。作为客户机,你需要首先配置和建立一个 RSocket 连接。 Spring 对于这种情况,引导自动配置带有预期编解码器的`RSocketRequester.Builder`,并应用任何`RSocketConnectorConfigurer` Bean。 + +`RSocketRequester.Builder`实例是一个原型 Bean,这意味着每个注入点都将为你提供一个新实例。这是有目的的,因为这个构建器是有状态的,你不应该使用相同的实例创建具有不同设置的请求者。 + +下面的代码展示了一个典型的示例: + +``` +import reactor.core.publisher.Mono; + +import org.springframework.messaging.rsocket.RSocketRequester; +import org.springframework.stereotype.Service; + +@Service +public class MyService { + + private final RSocketRequester rsocketRequester; + + public MyService(RSocketRequester.Builder rsocketRequesterBuilder) { + this.rsocketRequester = rsocketRequesterBuilder.tcp("example.org", 9898); + } + + public Mono someRSocketCall(String name) { + return this.rsocketRequester.route("user").data(name).retrieveMono(User.class); + } + +} + +``` + +## 5. Spring 整合 + +Spring Boot 为使用[Spring Integration](https://spring.io/projects/spring-integration)提供了几种便利,包括`spring-boot-starter-integration`“starter”。 Spring 集成在消息传递和其他传输(例如 HTTP、TCP 和其他传输)上提供了抽象。如果 Spring 集成在你的 Classpath 上可用,则通过`@EnableIntegration`注释对其进行初始化。 + +Spring 集成轮询逻辑依赖于[on the auto-configured `TaskScheduler`](features.html#features.task-execution-and-scheduling)。默认的`PollerMetadata`(每秒轮询无界的消息数量)可以使用`spring.integration.poller.*`配置属性进行定制。 + +Spring 引导还配置了由存在额外的 Spring 集成模块触发的一些特征。如果`spring-integration-jmx`也在 Classpath 上,则消息处理统计信息将在 JMX 上发布。如果`spring-integration-jdbc`可用,则可以在启动时创建默认的数据库模式,如下所示: + +Properties + +``` +spring.integration.jdbc.initialize-schema=always +``` + +Yaml + +``` +spring: + integration: + jdbc: + initialize-schema: "always" +``` + +如果`spring-integration-rsocket`可用,开发人员可以使用`"spring.rsocket.server.*"`属性配置 RSocket 服务器,并让它使用`IntegrationRSocketEndpoint`或`RSocketOutboundGateway`组件来处理传入的 RSocket 消息。该基础结构可以处理 Spring 集成 RSocket 通道适配器和`@MessageMapping`处理程序(已配置`"spring.integration.rsocket.server.message-mapping-enabled"`)。 + +Spring 启动还可以使用配置属性自动配置`ClientRSocketConnector`: + +Properties + +``` +# Connecting to a RSocket server over TCP +spring.integration.rsocket.client.host=example.org +spring.integration.rsocket.client.port=9898 +``` + +Yaml + +``` +# Connecting to a RSocket server over TCP +spring: + integration: + rsocket: + client: + host: "example.org" + port: 9898 +``` + +Properties + +``` +# Connecting to a RSocket Server over WebSocket +spring.integration.rsocket.client.uri=ws://example.org +``` + +Yaml + +``` +# Connecting to a RSocket Server over WebSocket +spring: + integration: + rsocket: + client: + uri: "ws://example.org" +``` + +有关更多详细信息,请参见[“IntegrationAutoConfiguration”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java)和[“整合属性”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationProperties.java)类。 + +## 6. 接下来读什么? + +下一节介绍如何在应用程序中启用[IO capabilities](io.html#io)。你可以在本节中阅读有关[caching](io.html#io.caching)、[mail](io.html#io.email)、[validation](io.html#io.validation)、[rest clients](io.html#io.rest-client)等内容。 diff --git a/docs/spring-boot/upgrading.md b/docs/spring-boot/upgrading.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d615d42f24a282218562fe7228fb407963e81c32 100644 --- a/docs/spring-boot/upgrading.md +++ b/docs/spring-boot/upgrading.md @@ -0,0 +1,38 @@ +# 升级 Spring 引导 + +在项目[wiki](https://github.com/spring-projects/spring-boot/wiki)上提供了如何从 Spring 启动的早期版本升级的说明。按照[release notes](https://github.com/spring-projects/spring-boot/wiki#release-notes)部分中的链接查找要升级到的版本。 + +升级说明总是发布说明中的第一项。如果你落后于多个版本,请确保你还查看了跳过的版本的发布说明。 + +## 1. 从 1.x 升级 + +如果你正在从 Spring 启动的`1.x`版本进行升级,请检查提供详细升级说明的[维基项目中的“迁移指南”](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide)。还可以在[“release notes”](https://github.com/spring-projects/spring-boot/wiki)中查看每个版本的“新的和值得注意的”特性列表。 + +## 2. 升级到新的功能版本 + +当升级到新的功能版本时,一些属性可能已被重命名或删除。 Spring Boot 提供了一种在启动时分析应用程序环境和打印诊断的方法,但也为你提供了在运行时临时迁移属性的方法。要启用该功能,请在项目中添加以下依赖项: + +``` + + org.springframework.boot + spring-boot-properties-migrator + runtime + +``` + +| |较晚添加到环境中的属性(例如使用`@PropertySource`时)将不会被考虑在内。| +|---|------------------------------------------------------------------------------------------------------------------------| + +| |完成迁移后,请确保从项目的依赖项中删除此模块。| +|---|-------------------------------------------------------------------------------------------------------| + +## 3. 升级 Spring 引导 cli + +要升级现有的 CLI 安装,请使用适当的 PackageManager 命令(例如,)。如果你手动安装了 CLI,请按照[标准说明](getting-started.html#getting-started.installing.cli.manual-installation),记住更新你的`PATH`环境变量,以删除任何旧的引用。 + +## 4. 接下来读什么? + +一旦你决定升级你的应用程序,你可以在文档的其余部分找到关于特定功能的详细信息。 + +Spring Boot 的文档是针对该版本的,因此你在这里找到的任何信息都将包含该版本中最新的更改。 + diff --git a/docs/spring-boot/using.md b/docs/spring-boot/using.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6e6693081cc677149b0d6baa5d8c5e0917a284bd 100644 --- a/docs/spring-boot/using.md +++ b/docs/spring-boot/using.md @@ -0,0 +1,851 @@ +# 用 Spring boot 开发 + +本节将更详细地介绍如何使用 Spring 引导。它涵盖了诸如构建系统、自动配置以及如何运行应用程序等主题。我们还介绍了一些引导最佳实践。虽然 Spring boot 并没有什么特别之处(它只是另一个你可以使用的库),但有一些建议,如果遵循这些建议,将使你的开发过程变得更容易一些。 + +如果你是从 Spring boot 开始的,那么在深入了解本节之前,你可能应该阅读*[Getting Started](getting-started.html#getting-started)*指南。 + +## 1. 建立系统 + +强烈建议你选择一个支持[* 扶养管理 *](#using.build-systems.dependency-management)并且可以使用发布到“ Maven Central”存储库的工件的构建系统。我们建议你选择 Maven 或 Gradle。让 Spring 引导与其他构建系统(例如 Ant)一起工作是可能的,但是它们没有得到特别好的支持。 + +### 1.1.依赖管理 + +Spring 启动的每个版本都提供了它所支持的依赖关系的精心策划的列表。在实践中,你不需要在构建配置中为这些依赖项中的任何一个提供版本,因为 Spring boot 为你管理这些依赖项。当你升级 Spring 引导本身时,这些依赖关系也会以一致的方式进行升级。 + +| |你仍然可以指定一个版本,并在需要时重写 Spring Boot 的建议。| +|---|------------------------------------------------------------------------------------------------| + +这个精心策划的列表包含你可以在 Spring 启动时使用的所有 Spring 模块,以及第三方库的完善列表。这份清单是一份标准的材料清单(` Spring-boot-dependencies`),可用于[Maven](#using.build-systems.maven)和[Gradle](#using.build-systems.gradle)。 + +| |Spring 启动的每个版本都与 Spring 框架的基本版本相关联。
我们**高度**建议你不要指定其版本。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.2. Maven + +要了解如何在 Maven 中使用 Spring boot,请参阅 Spring boot 的 Maven 插件的文档: + +* 参考文献([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/htmlsingle/)和[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/pdf/spring-boot-maven-plugin-reference.pdf)) + +* [API](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/api/) + +### 1.3. Gradle + +要了解如何在 Gradle 中使用 Spring boot,请参阅 Spring boot 的 Gradle 插件的文档: + +* 参考文献([HTML](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/)和[PDF](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/pdf/spring-boot-gradle-plugin-reference.pdf)) + +* [API](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/api/) + +### 1.4. Ant + +可以使用 Apache Ant +Ivy 构建 Spring 引导项目。`spring-boot-antlib`“antlib”模块也可用于帮助 Ant 创建可执行 JAR。 + +要声明依赖关系,典型的`ivy.xml`文件类似于以下示例: + +``` + + + + + + + + + + +``` + +一个典型的`build.xml`看起来像以下示例: + +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +| |如果你不想使用`spring-boot-antlib`模块,请参阅*[howto.html](howto.html#howto.build.build-an-executable-archive-with-ant-without-using-spring-boot-antlib)*“how-to”。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 1.5.初学者 + +启动器是一组方便的依赖关系描述符,你可以将它们包含在应用程序中。你可以获得所需的所有 Spring 和相关技术的一站式服务,而无需搜索示例代码和复制粘贴依赖描述符的负载。例如,如果你希望开始使用 Spring 和 JPA 进行数据库访问,那么在你的项目中包含`spring-boot-starter-data-jpa`依赖项。 + +启动器包含许多依赖项,你需要这些依赖项才能使项目快速启动和运行,并具有一组一致的、受支持的托管传递依赖项。 + +名字里的含义是什么? + +所有**官员**启动器都遵循类似的命名模式;`spring-boot-starter-*`,其中`*`是一种特定类型的应用程序。此命名结构旨在帮助你在需要查找启动器时使用。 Maven 在许多 IDE 中的集成允许你按名称搜索依赖项。例如,在安装了适当的 Eclipse 或 Spring Tools 插件后,你可以在 POM 编辑器中按`ctrl-space`并输入“ Spring-boot-starter”以获得完整的列表。 + +正如在“[创建自己的启动器](features.html#features.developing-auto-configuration.custom-starter)”部分中所解释的,第三方启动器不应该以`spring-boot`开头,因为它是为官方 Spring 引导工件保留的。相反,第三方启动程序通常以项目的名称开始。例如,一个名为`thirdpartyproject`的第三方启动项目通常被命名为`thirdpartyproject-spring-boot-starter`。 + +Spring Boot 在`org.springframework.boot`组下提供了以下应用程序启动器: + +| Name |说明| +|-------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| []()`spring-boot-starter` |核心启动器,包括自动配置支持、日志记录和 YAML| +| []()`spring-boot-starter-activemq` |使用 Apache ActiveMQ 进行 JMS 消息传递的启动器| +| []()`spring-boot-starter-amqp` |使用 Spring AMQP 和 Rabbit MQ 的启动器| +| []()`spring-boot-starter-aop` |Spring AOP 和 AspectJ 用于面向方面编程的启动器| +| []()`spring-boot-starter-artemis` |使用 Apache Artemis 进行 JMS 消息传递的启动器| +| []()`spring-boot-starter-batch` |用于使用 Spring 批的启动器| +| []()`spring-boot-starter-cache` |使用 Spring Framework 的缓存支持的入门工具| +| []()`spring-boot-starter-data-cassandra` |用于使用 Cassandra 分布式数据库和 Spring 数据的启动器 Cassandra| +|[]()`spring-boot-starter-data-cassandra-reactive`|用于使用 Cassandra 分布式数据库的启动器和 Spring 数据 Cassandra 反应| +| []()`spring-boot-starter-data-couchbase` |用于使用 CouchBase 面向文档的数据库和 Spring DataCouchBase 的启动器| +|[]()`spring-boot-starter-data-couchbase-reactive`|Spring 用于使用 CouchBase 文档的面向数据库和 CouchBase 数据库的启动器| +| []()`spring-boot-starter-data-elasticsearch` |用于使用 ElasticSearch 搜索和分析引擎的启动器和 Spring 数据 ElasticSearch| +| []()`spring-boot-starter-data-jdbc` |用于使用 Spring 数据 JDBC 的启动器| +| []()`spring-boot-starter-data-jpa` |用于使用 Spring 数据 JPA 和 Hibernate 的启动器| +| []()`spring-boot-starter-data-ldap` |用于使用 Spring 数据 LDAP 的启动器| +| []()`spring-boot-starter-data-mongodb` |用于使用 MongoDB 面向文档的数据库和 Spring 数据 MongoDB 的启动器| +| []()`spring-boot-starter-data-mongodb-reactive` |用于使用 MongoDB 面向文档的数据库的启动器和 Spring 数据 MongoDB Active| +| []()`spring-boot-starter-data-neo4j` |用于使用 NEO 的启动器 4j 图数据库和 Spring 数据 NEO4j| +| []()`spring-boot-starter-data-r2dbc` |用于使用 Spring 数据 R2DBC 的启动器| +| []()`spring-boot-starter-data-redis` |用 Spring 数据 Redis 和生菜客户机使用 Redis 键值数据存储的启动器| +| []()`spring-boot-starter-data-redis-reactive` |用于使用 Redis 键值数据存储的启动器 Spring 数据 Redis 是被动的和生菜客户机| +| []()`spring-boot-starter-data-rest` |用于使用 Spring 数据 REST 在 REST 上公开 Spring 数据存储库的启动器| +| []()`spring-boot-starter-freemarker` |使用 Freemarker 视图构建 MVC Web 应用程序的启动器| +| []()`spring-boot-starter-groovy-templates` |使用 Groovy Templates 视图构建 MVC Web 应用程序的入门工具| +| []()`spring-boot-starter-hateoas` |用 Spring MVC 和 Spring Hateoas 构建基于超媒体的 RESTful Web 应用程序的启动器| +| []()`spring-boot-starter-integration` |用于使用 Spring 集成的启动器| +| []()`spring-boot-starter-jdbc` |使用 JDBC 和 HikarICP 连接池的启动器| +| []()`spring-boot-starter-jersey` |使用 JAX-RS 和 Jersey 构建 RESTful Web 应用程序的入门工具。[`spring-boot-starter-web`](#spring-boot-starter-web)的一种替代方法| +| []()`spring-boot-starter-jooq` |使用 Jooq 使用 JDBC 访问 SQL 数据库的启动器。[`spring-boot-starter-data-jpa`](#spring-boot-starter-data-jpa)或[`spring-boot-starter-jdbc`](#spring-boot-starter-jdbc)的替代方案| +| []()`spring-boot-starter-json` |读写 JSON 的启动器| +| []()`spring-boot-starter-jta-atomikos` |使用 Atomikos 的 JTA 事务启动器| +| []()`spring-boot-starter-mail` |使用 Java Mail 的启动器和 Spring Framework 的电子邮件发送支持| +| []()`spring-boot-starter-mustache` |使用 Mustache 视图构建 Web 应用程序的入门工具| +| []()`spring-boot-starter-oauth2-client` |使用 Spring Security 的 OAuth2/OpenID Connect 客户端功能的启动器| +|[]()`spring-boot-starter-oauth2-resource-server` |使用 Spring Security 的 OAuth2 资源服务器特性的启动器| +| []()`spring-boot-starter-quartz` |使用 Quartz 调度器的启动器| +| []()`spring-boot-starter-rsocket` |用于构建 RSocket 客户机和服务器的启动器| +| []()`spring-boot-starter-security` |使用 Spring 安全性的启动器| +| []()`spring-boot-starter-test` |用包括 JUnit Jupiter、Hamcrest 和 Mockito 在内的库测试 Spring 引导应用程序的启动器| +| []()`spring-boot-starter-thymeleaf` |使用 ThymeLeaf 视图构建 MVC Web 应用程序的启动器| +| []()`spring-boot-starter-validation` |用 Hibernate 验证器使用 Java Bean 验证的启动器| +| []()`spring-boot-starter-web` |用于使用 Spring MVC 构建 Web(包括 RESTful)应用程序的启动器。使用 Tomcat 作为默认的嵌入式容器| +| []()`spring-boot-starter-web-services` |用于使用 Spring Web 服务的启动器| +| []()`spring-boot-starter-webflux` |使用 Spring 框架的反应性 Web 支持构建 WebFlux 应用程序的启动器| +| []()`spring-boot-starter-websocket` |用于使用 Spring 框架的 WebSocket 支持构建 WebSocket 应用程序的启动器| + +除了应用程序启动器之外,还可以使用以下启动器添加*[生产准备就绪](actuator.html#actuator)*特性: + +| Name |说明| +|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +|[]()`spring-boot-starter-actuator`|使用 Spring Boot 的执行器的启动器,该执行器提供了生产就绪的功能,以帮助你监视和管理你的应用程序| + +最后, Spring 引导还包括以下启动器,如果你想要排除或交换特定的技术方面,可以使用这些启动器: + +| Name |说明| +|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| []()`spring-boot-starter-jetty` |用于使用 Jetty 作为嵌入式 Servlet 容器的启动器。[`spring-boot-starter-tomcat`](#spring-boot-starter-tomcat)的一种替代方法| +| []()`spring-boot-starter-log4j2` |使用 log4j2 进行日志记录的启动器。[`spring-boot-starter-logging`](#spring-boot-starter-logging)的一种替代方法| +| []()`spring-boot-starter-logging` |使用日志记录的启动器。默认日志启动器| +|[]()`spring-boot-starter-reactor-netty`|使用反应器网络作为嵌入式反应性 HTTP 服务器的启动器。| +| []()`spring-boot-starter-tomcat` |用于使用 Tomcat 作为嵌入式 Servlet 容器的启动器。由[`spring-boot-starter-web`](#spring-boot-starter-web)使用的默认 Servlet 容器启动器| +| []()`spring-boot-starter-undertow` |用于使用 Undertow 作为嵌入式 Servlet 容器的启动器。[`spring-boot-starter-tomcat`](#spring-boot-starter-tomcat)的一种替代方法| + +要了解如何交换技术方面,请参阅[交换 Web 服务器](howto.html#howto.webserver.use-another)和[logging system](howto.html#howto.logging.log4j)的操作文档。 + +| |有关其他社区贡献的启动器的列表,请参见 Github 上`spring-boot-starters`模块中的[README file](https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-starters/README.adoc)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 2. 构建你的代码 + +Spring 启动不需要任何特定的代码布局来工作。然而,有一些最佳实践是有帮助的。 + +### 2.1.使用“默认”包 + +当一个类不包含`package`声明时,它被认为是在“缺省包”中。“默认包”的使用通常是不鼓励的,应该避免。对于使用`@ComponentScan`、`@Configuration属性Scan`、`@EntityScan`或`@SpringBootApplication`注释的 Spring 引导应用程序来说,这可能会导致特殊的问题,因为每个 jar 中的每个类都被读取。 + +| |我们建议你遵循 Java 推荐的包命名约定,并使用一个反向域名(例如,`com.example.project`)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------| + +### 2.2.定位主应用程序类 + +我们通常建议你将主应用程序类定位在一个根包中,而不是其他类。[@SpringBootApplication](#using.using-the-springbootapplication-annotation)通常放在主类上,它隐式地为某些项定义了一个基本的“搜索包”。例如,如果你正在编写 JPA 应用程序,则使用`@SpringBootApplication`注释类的包来搜索`@Entity`项。使用根包还允许组件扫描仅应用于你的项目。 + +| |如果不想使用`@SpringBootApplication`,则它导入的`@EnableAutoConfiguration`和`@ComponentScan`注释定义了该行为,因此你也可以使用这些注释。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +下面的清单显示了一个典型的布局: + +``` +com + +- example + +- myapplication + +- MyApplication.java + | + +- customer + | +- Customer.java + | +- CustomerController.java + | +- CustomerService.java + | +- CustomerRepository.java + | + +- order + +- Order.java + +- OrderController.java + +- OrderService.java + +- OrderRepository.java +``` + +`MyApplication.java`文件将声明`main`方法,以及基本的`@SpringBootApplication`,如下所示: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +## 3. 配置类 + +Spring 引导有利于基于 Java 的配置。尽管在 XML 源中可以使用`SpringApplication`,但我们通常建议你的主要源是一个单独的`@Configuration`类。通常,定义`main`方法的类是作为主要`@Configuration`方法的很好的候选者。 + +| |Spring 互联网上已经发布了许多使用 XML 配置的配置示例。
如果可能的话,总是尝试使用等效的基于 Java 的配置。
搜索`Enable*`注释可以是一个很好的起点。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 3.1.导入额外的配置类 ### + +你不需要将所有的`@Configuration`都放入一个类中。`@Import`注释可用于导入其他配置类。或者,你可以使用`@ComponentScan`来自动拾取所有 Spring 组件,包括`@Configuration`类。 + +### 3.2.导入 XML 配置 + +如果你绝对必须使用基于 XML 的配置,我们建议你仍然从`@Configuration`类开始。然后可以使用`@ImportResource`注释来加载 XML 配置文件。 + +## 4. 自动配置 + +Spring 引导自动配置尝试基于你已添加的 jar 依赖项自动配置你的 Spring 应用程序。例如,如果`HSQLDB`在你的 Classpath 上,并且你还没有手动配置任何数据库连接 bean,那么 Spring 引导自动配置内存中的数据库。 + +你需要通过向你的`@Configuration`类中的一个添加`@EnableAutoConfiguration`或`@SpringBootApplication`注释来 OPT 到自动配置中。 + +| |你应该只添加一个`@SpringBootApplication`或`@EnableAutoConfiguration`注释。
我们通常建议你只在主`@Configuration`类中添加一个或另一个。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 4.1.逐步取代自动配置 + +自动配置是非侵入性的。在任何时候,你都可以开始定义自己的配置,以替换自动配置的特定部分。例如,如果你添加了自己的`DataSource` Bean,那么默认的嵌入式数据库支持就退后了。 + +如果需要找出当前正在应用的自动配置,以及为什么,请使用`--debug`开关启动应用程序。这样做可以为选择的核心记录器启用调试日志,并将一个条件报告给控制台。 + +### 4.2.禁用特定的自动配置类 + +如果你发现你不想要的特定自动配置类正在被应用,那么你可以使用`@SpringBootApplication`的 exclude 属性来禁用它们,如下面的示例所示: + +``` +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class MyApplication { + +} + +``` + +如果类不在 Classpath 上,则可以使用注释的`excludeName`属性并指定完全限定的名称。如果你更喜欢使用`@EnableAutoConfiguration`而不是`@SpringBootApplication`,也可以使用`exclude`和`excludeName`。最后,还可以使用`spring.autoconfigure.exclude`属性控制要排除的自动配置类列表。 + +| |你可以在注释级别和通过使用属性来定义排除。| +|---|---------------------------------------------------------------------------------| + +| |尽管自动配置类是`public`,但该类中唯一被认为是公共 API 的方面是可用于禁用自动配置的类的名称,
这些类的实际内容,例如嵌套配置类或 Bean 方法仅供内部使用,我们不建议直接使用这些方法。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 5. Spring bean 和依赖注入 + +你可以自由地使用任何标准的 Spring 框架技术来定义你的 bean 及其注入的依赖项。我们通常建议使用构造函数注入连接依赖项,并使用`@ComponentScan`查找 bean。 + +如果按照上面的建议构造代码(将应用程序类定位在一个顶级包中),则可以添加`@ComponentScan`而不带任何参数,或者使用`@SpringBootApplication`注释,其中隐式地包含它。你的所有应用程序组件(“@component”、`@Service`、`@Repository`、`@Controller`等)都会自动注册为 Spring bean。 + +下面的示例显示了一个`@Service` Bean,它使用构造函数注入来获得所需的`RiskAssessor` Bean: + +``` +import org.springframework.stereotype.Service; + +@Service +public class MyAccountService implements AccountService { + + private final RiskAssessor riskAssessor; + + public MyAccountService(RiskAssessor riskAssessor) { + this.riskAssessor = riskAssessor; + } + + // ... + +} + +``` + +如果 Bean 具有多个构造函数,则需要将希望 Spring 使用的构造函数标记为`@Autowired`: + +``` +import java.io.PrintStream; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MyAccountService implements AccountService { + + private final RiskAssessor riskAssessor; + + private final PrintStream out; + + @Autowired + public MyAccountService(RiskAssessor riskAssessor) { + this.riskAssessor = riskAssessor; + this.out = System.out; + } + + public MyAccountService(RiskAssessor riskAssessor, PrintStream out) { + this.riskAssessor = riskAssessor; + this.out = out; + } + + // ... + +} + +``` + +| |请注意,如何使用构造函数注入使`riskAssessor`字段标记为`final`,这表明它不能随后进行更改。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------| + +## 6. 使用 @SpringBootApplication 注释 + +Spring 许多引导开发人员喜欢他们的应用程序使用自动配置、组件扫描,并能够在他们的“应用程序类”上定义额外的配置。可以使用一个`@SpringBootApplication`注释来启用这三个特性,即: + +* `@EnableAutoConfiguration`:启用[Spring Boot’s auto-configuration mechanism](#using.auto-configuration) + +* `@ComponentScan`:在应用程序所在的包上启用`@Component`扫描(参见[最佳实践](#using.structuring-your-code)) + +* `@SpringBootConfiguration`:允许在上下文中注册额外的 bean 或导入额外的配置类。 Spring 的标准`@Configuration`的一种替代方法,它在集成测试中帮助[配置检测](features.html#features.testing.spring-boot-applications.detecting-configuration)。 + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +| |`@SpringBootApplication`还提供别名来定制`@EnableAutoConfiguration`和`@ComponentScan`的属性。| +|---|------------------------------------------------------------------------------------------------------------------------------| + +| |这些功能都不是强制性的,你可以选择用它启用的任何功能替换这一条注释。,例如,
,你可能不希望在应用程序中使用组件扫描或配置属性扫描:

``
导入 org.springframework.boot.springapplication;
导入 org.springframework.boof.autform.autofconfigure.enableautofconfiguration;
import.springtr.contexform.context.org.org.context.import;<<><<<<<>>>配置 =“156”Bootframmetation"/><}
}

``

在本例中,`MyApplication`就像任何其他 Spring 启动应用程序一样,除了`@Component`-注释的类和`@Configuration属性`-注释的类未被检测到,并且显式地导入了用户定义的 bean(参见`@Import`)。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 7. 运行你的应用程序 + +将应用程序打包为 jar 并使用嵌入式 HTTP 服务器的最大优势之一是,你可以像运行其他应用程序一样运行你的应用程序。该示例应用于调试 Spring 引导应用程序。你不需要任何特殊的 IDE 插件或扩展。 + +| |本节仅涵盖基于 jar 的打包。
如果你选择将应用程序打包为 WAR 文件,请参阅你的服务器和 IDE 文档。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------| + +### 7.1.从 IDE 中运行 + +你可以将 IDE 中的 Spring 引导应用程序作为 Java 应用程序运行。然而,你首先需要导入你的项目。导入步骤根据你的 IDE 和构建系统而有所不同。大多数 IDE 可以直接导入 Maven 项目。例如,Eclipse 用户可以从`File`菜单中选择`Import…​``Existing Maven Projects`。 + +如果不能直接将项目导入 IDE,则可以使用构建插件生成 IDE 元数据。 Maven 包括用于[Eclipse](https://maven.apache.org/plugins/maven-eclipse-plugin/)和[IDEA](https://maven.apache.org/plugins/maven-idea-plugin/)的插件。 Gradle 为[various IDEs](https://docs.gradle.org/current/userguide/userguide.html)提供插件。 + +| |如果你不小心运行了一个 Web 应用程序两次,你会看到一个“端口已在使用”错误。
Spring 工具用户可以使用`Relaunch`按钮,而不是`Run`按钮,以确保任何现有的实例都已关闭。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 7.2.以打包应用程序的形式运行 + +如果使用 Spring boot Maven 或 Gradle 插件创建可执行文件 jar,则可以使用`java -jar`运行应用程序,如以下示例所示: + +``` +$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar +``` + +还可以在启用远程调试支持的情况下运行打包的应用程序。这样做可以将一个调试器附加到打包的应用程序中,如以下示例所示: + +``` +$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \ + -jar target/myapplication-0.0.1-SNAPSHOT.jar +``` + +### 7.3.使用 Maven 插件 + +Spring 引导 Maven 插件包括一个`run`目标,该目标可用于快速编译和运行你的应用程序。应用程序以分块的形式运行,就像它们在你的 IDE 中一样。下面的示例显示了用于运行 Spring 引导应用程序的典型 Maven 命令: + +``` +$ mvn spring-boot:run +``` + +你可能还希望使用`MAVEN_OPTS`操作系统环境变量,如以下示例所示: + +``` +$ export MAVEN_OPTS=-Xmx1024m +``` + +### 7.4.使用 Gradle 插件 + +Spring boot Gradle 插件还包括一个`bootRun`任务,该任务可用于以扩展形式运行你的应用程序。每当你应用`org.springframework.boot`和`java`插件时,都会添加`bootRun`任务,如下例所示: + +``` +$ gradle bootRun +``` + +你可能还希望使用`JAVA_OPTS`操作系统环境变量,如以下示例所示: + +``` +$ export JAVA_OPTS=-Xmx1024m +``` + +### 7.5.热交换 + +Spring 由于引导应用程序是普通的 Java 应用程序,所以 JVM 热交换应该是开箱即用的。JVM 热交换在一定程度上受到它可以替换的字节码的限制。对于更完整的解决方案,可以使用[JRebel](https://www.jrebel.com/products/jrebel)。 + +`spring-boot-devtools`模块还包括对快速应用程序重启的支持。有关详细信息,请参见[热点交换“操作”](howto.html#howto.hotswapping)。 + +## 8. 开发工具 + +Spring 启动包括一组额外的工具,这些工具可以使应用程序的开发体验稍微更愉快一些。`spring-boot-devtools`模块可以包含在任何项目中,以提供额外的开发时功能。要包含 DevTools 支持,请将模块依赖项添加到构建中,如以下 Maven 和 Gradle 的清单所示: + +Maven + +``` + + + org.springframework.boot + spring-boot-devtools + true + + +``` + +Gradle + +``` +dependencies { + developmentOnly("org.springframework.boot:spring-boot-devtools") +} +``` + +| |DevTools 可能会导致类加载问题,特别是在多模块项目中。[诊断类加载问题](#using.devtools.diagnosing-classloading-issues)解释了如何诊断和解决它们。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在运行完全打包的应用程序时,开发人员工具会自动禁用。
如果你的应用程序是从`java -jar`启动的,或者是从一个特殊的类加载器启动的,然后它被认为是一个“生产应用程序”,
你可以通过使用`spring.devtools.restart.enabled`系统属性来控制这种行为,
来启用 DevTools,而不考虑启动应用程序所使用的类加载器,设置`-Dspring.devtools.restart.enabled=true`系统属性。
在运行 DevTools 存在安全风险的生产环境中,不能这样做。
要禁用 DevTools,排除依赖关系或设置`-Dspring.devtools.restart.enabled=false`系统属性。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在 Maven 中将依赖标记为可选的,或在 Gradle 中使用`developmentOnly`配置(如上图所示),可以防止将 DevTools 传递地应用于使用你的项目的其他模块。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |重新打包的归档文件默认不包含 DevTools。
如果你想使用[某些远程 DevTools 功能](#using.devtools.remote-applications),则需要包含它。
在使用 Maven 插件时,将`excludeDevtools`属性设置为`false`。
在使用 Gradle 插件时,[configure the task’s classpath to include the `developmentOnly` configuration](https://docs.spring.io/spring-boot/docs/2.6.4/gradle-plugin/reference/htmlsingle/#packaging-executable-configuring-including-development-only-dependencies)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 8.1.诊断类加载问题 + +如[重新启动 VS 重新加载](#using.devtools.restart.restart-vs-reload)小节中所述,重新启动功能是通过使用两个类装入器来实现的。对于大多数应用程序来说,这种方法效果很好。然而,它有时会导致类加载问题,特别是在多模块项目中。 + +要诊断类加载问题是否确实是由 DevTools 及其两个类加载器引起的,[尝试禁用重新启动](#using.devtools.restart.disable)。如果这解决了你的问题,[自定义重新启动类装入器](#using.devtools.restart.customizing-the-classload)将包含你的整个项目。 + +### 8.2.属性默认值 + +Spring 引导支持的几个库使用缓存来提高性能。例如,[模板引擎](web.html#web.servlet.spring-mvc.template-engines)缓存已编译的模板,以避免重复解析模板文件。另外, Spring MVC 可以在提供静态资源时将 HTTP 缓存头添加到响应中。 + +虽然缓存在生产中非常有益,但在开发过程中可能会适得其反,从而使你无法看到刚刚在应用程序中所做的更改。出于这个原因, Spring-boot-devtools 默认禁用缓存选项。 + +缓存选项通常由`application.properties`文件中的设置来配置。例如,ThymeLeaf 提供`spring.thymeleaf.cache`属性。不需要手动设置这些属性,`spring-boot-devtools`模块会自动应用合理的开发时配置。 + +由于在开发 Spring MVC 和 Spring WebFlux 应用程序时需要有关 Web 请求的更多信息,因此 Developer Tools 建议你为`DEBUG`日志记录组启用`web`日志记录。这将为你提供有关传入请求的信息、处理该请求的处理程序、响应结果以及其他详细信息。如果希望记录所有请求详细信息(包括可能敏感的信息),可以打开`spring.mvc.log-request-details`或`spring.codec.log-request-details`配置属性。 + +| |如果不希望应用属性默认值,可以在`application.properties`中将`spring.devtools.add-properties`设置为`false`。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------| + +| |有关 DevTools 应用的属性的完整列表,请参见[DevToolsPropertyDefaultSpostProcessor](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java)。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +### 8.3.自动重启 + +每当 Classpath 上的文件发生更改时,使用`spring-boot-devtools`的应用程序会自动重新启动。在 IDE 中工作时,这可能是一个有用的特性,因为它为代码更改提供了非常快的反馈循环。默认情况下, Classpath 上指向某个目录的任何条目都会被监视以进行更改。请注意,某些资源,例如静态资产和视图模板,[不需要重新启动应用程序](#using.devtools.restart.excluding-resources)。 + +触发重新启动 + +由于 DevTools 监视 Classpath 资源,触发重启的唯一方法是更新 Classpath。使 Classpath 更新的方式取决于你使用的 IDE: + +* 在 Eclipse 中,保存修改后的文件会导致 Classpath 被更新并触发重新启动。 + +* 在 IntelliJ IDEA 中,构建项目(“Build++Build 项目”)具有相同的效果。 + +* 如果使用构建插件,对 Maven 运行`mvn compile`或对 Gradle 运行`gradle build`将触发重新启动。 + +| |如果使用 Maven 或 Gradle 使用构建插件重新启动,则必须将`forking`设置为`enabled`。
如果禁用分叉,将不会创建 DevTools 使用的隔离应用程序类装入器,并且重新启动将无法正常运行。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |与 LiveReload 一起使用时,自动重启非常有效。[参见 LiveReload 部分](#using.devtools.livereload)有关详细信息。
如果使用 JRebel,自动重启将被禁用,以利于动态类重新加载。
其他 DevTools 功能(例如 LiveReload 和 Property overrides)仍然可以使用。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |DevTools 依赖应用程序上下文的关闭钩子在重新启动时关闭它。
如果你禁用了关闭钩子,它将无法正常工作。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |DevTools 需要自定义`ApplicationContext`所使用的`ResourceLoader`。
如果你的应用程序已经提供了一个,它将被包装。
不支持`getResource`方法上的`getResource`直接覆盖。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |使用 AspectJ 编织时不支持自动重新启动。| +|---|--------------------------------------------------------------| + +重新启动 VS 重新加载 + +Spring Boot 提供的重启技术通过使用两个类装入器来工作。不会更改的类(例如,来自第三方 JAR 的类)被加载到*基座*类装入器中。你正在积极开发的类被加载到*重新启动*类装入器中。当重新启动应用程序时,将丢弃*重新启动*类装入器,并创建一个新的类装入器。这种方法意味着应用程序的重新启动通常比“冷启动”快得多,因为*基座*类装入器已经可用并填充了。 + +如果你发现应用程序的重新启动速度不够快,或者遇到类加载问题,那么可以考虑重新加载 ZeroTurnaround 中的[JRebel](https://jrebel.com/software/jrebel/)等技术。这些工作是在加载时重写类,以使它们更易于重新加载。 + +#### 8.3.1.状态评估中的测井变化 + +默认情况下,每次应用程序重新启动时,都会记录一份显示条件评估增量的报告。当你进行添加或删除 bean 和设置配置属性等更改时,该报告将显示对应用程序自动配置的更改。 + +要禁用报告的日志记录,请设置以下属性: + +属性 + +``` +spring.devtools.restart.log-condition-evaluation-delta=false +``` + +Yaml + +``` +spring: + devtools: + restart: + log-condition-evaluation-delta: false +``` + +#### 8.3.2.不包括资源 + +某些资源在被更改时不一定需要触发重新启动。例如,胸腺叶模板可以就地编辑。默认情况下,在`/META-INF/maven`、`/META-INF/resources`、`/resources`、`/static`、`/public`或`/templates`中更改资源不会触发重启,但会触发[live reload](#using.devtools.livereload)。如果希望自定义这些排除,可以使用`spring.devtools.restart.exclude`属性。例如,要仅排除`/static`和`/public`,你需要设置以下属性: + +属性 + +``` +spring.devtools.restart.exclude=static/**,public/** +``` + +Yaml + +``` +spring: + devtools: + restart: + exclude: "static/**,public/**" +``` + +| |如果你希望保留这些默认值和*添加*附加排除项,请使用`spring.devtools.restart.additional-exclude`属性。| +|---|------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.3.观看其他路径 + +当你对不在 Classpath 上的文件进行更改时,你可能希望你的应用程序被重新启动或重新加载。要做到这一点,请使用`spring.devtools.restart.additional-paths`属性来配置其他路径,以监视更改。你可以使用`spring.devtools.restart.exclude`属性[前面描述的](#using.devtools.restart.excluding-resources)来控制附加路径下方的更改是否触发完全重新启动或[live reload](#using.devtools.livereload)。 + +#### 8.3.4.禁用重新启动 + +如果不想使用 Restart 特性,可以使用`spring.devtools.restart.enabled`属性禁用它。在大多数情况下,你可以在`application.properties`中设置此属性(这样做仍然会初始化 Restart 类装入器,但它不会监视文件更改)。 + +如果需要*完全*禁用重启支持(例如,因为它不能与特定的库一起工作),则需要在调用`spring.devtools.restart.enabled``System`之前将`false`属性设置为`false`,如以下示例所示: + +``` +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class MyApplication { + + public static void main(String[] args) { + System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(MyApplication.class, args); + } + +} + +``` + +#### 8.3.5.使用触发器文件 + +如果你使用的 IDE 不断编译更改的文件,那么你可能更喜欢只在特定的时间触发重启。要做到这一点,你可以使用“触发器文件”,这是一种特殊的文件,当你想要实际触发重新启动检查时,必须对其进行修改。 + +| |对该文件的任何更新都会触发检查,但只有在 DevTools 检测到有事情要做时,才会真正重新启动。| +|---|------------------------------------------------------------------------------------------------------------------------------| + +要使用触发器文件,请将`spring.devtools.restart.trigger-file`属性设置为触发器文件的名称(不包括任何路径)。触发器文件必须出现在你的 Classpath 上的某个地方。 + +例如,如果你的项目具有以下结构: + +``` +src ++- main + +- resources + +- .reloadtrigger +``` + +那么你的`trigger-file`属性将是: + +属性 + +``` +spring.devtools.restart.trigger-file=.reloadtrigger +``` + +Yaml + +``` +spring: + devtools: + restart: + trigger-file: ".reloadtrigger" +``` + +现在只有更新`src/main/resources/.reloadtrigger`时才会重新启动。 + +| |你可能希望将`spring.devtools.restart.trigger-file`设置为[global setting](#using.devtools.globalsettings),以便所有项目都以相同的方式运行。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +有些 IDE 有一些特性,可以使你不必手动更新触发器文件。[Spring Tools for Eclipse](https://spring.io/tools)和[Intellij Idea(终极版)](https://www.jetbrains.com/idea/)都有这样的支持。使用 Spring 工具,你可以使用控制台视图中的“Reload”按钮(只要你的`trigger-file`被命名为`.reloadtrigger`)。对于 IntelliJ IDEA,你可以遵循[文件中的说明](https://www.jetbrains.com/help/idea/spring-boot.html#application-update-policies)。 + +#### 8.3.6.自定义重新启动类装入器 + +如前面[重新启动 VS 重新加载](#using.devtools.restart.restart-vs-reload)小节中所述,重新启动功能是通过使用两个类装入器来实现的。如果这会导致问题,你可能需要定制由哪个类装入器加载的内容。 + +默认情况下,你的 IDE 中的任何打开的项目都是用“restart”类加载器加载的,而任何常规的`.jar`文件都是用“base”类加载器加载的。如果使用`mvn spring-boot:run`或`gradle bootRun`,情况也是如此:包含`@SpringBootApplication`的项目是用“重新启动”类装入器加载的,其他所有内容都是用“基本”类装入器加载的。 + +通过创建`META-INF/spring-devtools.properties`文件,你可以指示 Spring boot 使用不同的类装入器加载项目的部分内容。`spring-devtools.properties`文件可以包含以`restart.exclude`和`restart.include`为前缀的属性。`include`元素是应该被拉到“重新启动”类装入器中的项,而`exclude`元素是应该被下推到“基本”类装入器中的项。该属性的值是应用于 Classpath 的正则表达式模式,如以下示例所示: + +属性 + +``` +restart.exclude.companycommonlibs=/mycorp-common-[\\w\\d-\\.]+\\.jar +restart.include.projectcommon=/mycorp-myproj-[\\w\\d-\\.]+\\.jar +``` + +Yaml + +``` +restart: + exclude: + companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar" + include: + projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar" +``` + +| |所有属性键必须是唯一的。
只要一个属性以`restart.include.`或`restart.exclude.`开头,就被认为是唯一的。| +|---|----------------------------------------------------------------------------------------------------------------------------------| + +| |Classpath 中的所有`META-INF/spring-devtools.properties`都已加载。
你可以将文件打包到你的项目中,或者打包到项目使用的库中。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.3.7.已知限制 + +对于使用标准`ObjectInputStream`进行反序列化的对象,重启功能不能很好地工作。如果需要反序列化数据,则可能需要结合使用 Spring 的`ConfigurableObjectInputStream`和`Thread.currentThread().getContextClassLoader()`。 + +遗憾的是,一些第三方库在反序列化时没有考虑上下文类装入器。如果你发现了这样的问题,你需要向原始作者请求修复。 + +### 8.4.LiveReload + +`spring-boot-devtools`模块包括一个嵌入式 LiveReload 服务器,该服务器可用于在更改资源时触发浏览器刷新。LiveReload 浏览器扩展从[livereload.com](http://livereload.com/extensions/)免费提供给 Chrome、Firefox 和 Safari。 + +如果不想在应用程序运行时启动 LiveReload 服务器,可以将`spring.devtools.livereload.enabled`属性设置为`false`。 + +| |一次只能运行一个 LiveReload 服务器。
在启动应用程序之前,请确保没有其他 LiveReload 服务器在运行。
如果你从 IDE 启动多个应用程序,则只有第一个应用程序具有 LiveReload 支持。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |要在文件更改时触发 LiveReload,必须启用[自动重启](#using.devtools.restart)。| +|---|--------------------------------------------------------------------------------------------------------| + +### 8.5.全局设置 + +你可以通过向`$HOME/.config/spring-boot`目录中添加以下任意一个文件来配置全局 devtools 设置: + +1. `spring-boot-devtools.properties` + +2. `spring-boot-devtools.yaml` + +3. `spring-boot-devtools.yml` + +添加到这些文件中的任何属性都适用于机器上使用 DevTools 的*全部* Spring 引导应用程序。例如,要将 Restart 配置为始终使用[trigger file](#using.devtools.restart.triggerfile),你可以将以下属性添加到`spring-boot-devtools`文件中: + +Properties + +``` +spring.devtools.restart.trigger-file=.reloadtrigger +``` + +Yaml + +``` +spring: + devtools: + restart: + trigger-file: ".reloadtrigger" +``` + +默认情况下,`$HOME`是用户的主目录。要自定义此位置,请设置`SPRING_DEVTOOLS_HOME`环境变量或`spring.devtools.home`系统属性。 + +| |如果在`$HOME/.config/spring-boot`中未找到 DevTools 配置文件,在`$HOME`目录的根目录中搜索是否存在`.spring-boot-devtools.properties`文件。
这允许你与不支持`$HOME/.config/spring-boot`位置的旧版本 Spring 启动上的应用程序共享 DevTools 全局配置。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |DevTools Properties/YAML 文件不支持配置文件。

在`.spring-boot-devtools.properties`中激活的任何配置文件都不会影响[特定于配置文件的配置文件](features.html#features.external-config.files.profile-specific)的加载。
配置文件特定的文件名(`spring-boot-devtools-.properties`)和`spring.config.activate.on-profile`文件在 YAML 和 Properties 文件中都不受支持。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.5.1.配置文件系统监视器 + +[Filesystemwatcher](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/filewatch/FileSystemWatcher.java)的工作原理是轮询具有一定时间间隔的类更改,然后等待预定义的静默期,以确保没有更多更改。由于 Spring 引导完全依赖于 IDE 来编译和复制文件到 Spring 引导可以读取它们的位置,所以当 DevTools 重新启动应用程序时,你可能会发现有时某些更改没有得到反映。如果你经常观察到这样的问题,请尝试将`spring.devtools.restart.poll-interval`和`spring.devtools.restart.quiet-period`参数增加到适合你的开发环境的值: + +Properties + +``` +spring.devtools.restart.poll-interval=2s +spring.devtools.restart.quiet-period=1s +``` + +Yaml + +``` +spring: + devtools: + restart: + poll-interval: "2s" + quiet-period: "1s" +``` + +Classpath 监视的目录现在每 2 秒对更改进行一次轮询,并保持 1 秒的静默期,以确保没有额外的类更改。 + +### 8.6.远程应用程序 + +Spring 引导开发人员工具不限于本地开发。在远程运行应用程序时,你还可以使用几个功能。远程支持是 OPT 的,因为启用它可能会带来安全风险。只有在运行在受信任的网络上或使用 SSL 进行安全保护时,才应启用它。如果这两个选项都不对你可用,那么你不应该使用 DevTools 的远程支持。你永远不应该在生产部署中启用支持。 + +要启用它,你需要确保`devtools`包含在重新打包的归档文件中,如以下清单所示: + +``` + + + + org.springframework.boot + spring-boot-maven-plugin + + false + + + + +``` + +然后需要设置`spring.devtools.remote.secret`属性。像任何重要的密码或秘密一样,该值应该是唯一的和强大的,以便它不能被猜测或暴力强迫。 + +远程 DevTools 支持由两部分提供:接受连接的服务器端端点和在 IDE 中运行的客户机应用程序。设置`spring.devtools.remote.secret`属性后,服务器组件将自动启用。客户端组件必须手动启动。 + +| |Spring WebFlux 应用程序不支持远程 DevTools。| +|---|-----------------------------------------------------------------| + +#### 8.6.1.运行远程客户端应用程序 + +远程客户机应用程序被设计为在你的 IDE 中运行。你需要以与所连接的远程项目相同的 Classpath 运行`org.springframework.boot.devtools.RemoteSpringApplication`。应用程序唯一需要的参数是它所连接的远程 URL。 + +例如,如果你正在使用 Eclipse 或 Spring 工具,并且你有一个名为`my-app`的项目已部署到 Cloud Foundry,那么你将执行以下操作: + +* 从`Run`菜单中选择`Run Configurations…​`。 + +* 创建一个新的`Java Application`“启动配置”。 + +* 浏览`my-app`项目。 + +* 使用`org.springframework.boot.devtools.RemoteSpringApplication`作为主类。 + +* 将`https://myapp.cfapps.io`添加到`Program arguments`(或你的远程 URL 是什么)。 + +正在运行的远程客户端可能类似于以下清单: + +``` + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \ + \\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / / + =========|_|==============|___/===================================/_/_/_/ + :: Spring Boot Remote :: 2.6.4 + +2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Starting RemoteSpringApplication on pwmbp with PID 14938 (/Users/pwebb/projects/spring-boot/code/spring-boot-project/spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code) +2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]2a17b7b6: startup date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy +2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'. +2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729 +2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication : Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105) +``` + +| |因为远程客户端使用的是与实际应用相同的 Classpath,所以它可以直接读取应用程序属性。
这就是`spring.devtools.remote.secret`属性的读取方式,并将其传递给服务器以进行身份验证。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |始终建议使用`https://`作为连接协议,以便对流量进行加密,并且不能拦截密码。| +|---|--------------------------------------------------------------------------------------------------------------------------------------| + +| |如果需要使用代理来访问远程应用程序,请配置`spring.devtools.remote.proxy.host`和`spring.devtools.remote.proxy.port`属性。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 8.6.2.远程更新 + +远程客户端以与[local restart](#using.devtools.restart)相同的方式监视你的应用程序 Classpath 的更改。任何更新的资源都会被推送到远程应用程序,并(* 如果需要 *)触发重新启动。如果你迭代使用本地没有的云服务的功能,这将很有帮助。通常,远程更新和重启要比完整的重建和部署周期快得多。 + +在较慢的开发环境中,可能会出现静默期不够的情况,并且类中的更改可能会被分解为批处理。上载第一批类更改后,服务器将重新启动。由于服务器正在重新启动,下一个批处理不能发送到应用程序。 + +这通常表现为`RemoteSpringApplication`日志中有关于未能上载某些类的警告,以及随后的重试。但这也可能导致应用程序代码不一致,以及在上传第一批更改后无法重新启动。如果你经常观察到这样的问题,请尝试将`spring.devtools.restart.poll-interval`和`spring.devtools.restart.quiet-period`参数增加到适合你的开发环境的值。有关这些属性的配置,请参见[配置文件系统监视器](#using.devtools.globalsettings.configuring-file-system-watcher)小节。 + +| |只有在远程客户端运行时才会监视文件。
如果在启动远程客户端之前更改了文件,则不会将其推送到远程服务器。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 9. 包装你的应用程序以进行生产 + +可执行 JAR 可用于生产部署。由于它们是自包含的,因此也非常适合基于云的部署。 + +对于额外的“生产就绪”特性,例如健康状态、审计和度量休息或 JMX 端点,可以考虑添加`spring-boot-actuator`。详见*[actuator.html](actuator.html#actuator)*。 + +## 10. 接下来读什么? + +你现在应该了解如何使用 Spring 引导和一些你应该遵循的最佳实践。你现在可以继续深入了解特定的*[Spring Boot features](features.html#features)*,或者你可以跳过并阅读 Spring 引导的“[生产准备就绪](actuator.html#actuator)”方面。 diff --git a/docs/spring-boot/web.md b/docs/spring-boot/web.md index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7a5d12cb48f7f75a6232ae2ef82055126995b799 100644 --- a/docs/spring-boot/web.md +++ b/docs/spring-boot/web.md @@ -0,0 +1,1887 @@ +# 万维网 + +Spring 启动非常适合于 Web 应用程序开发。你可以使用嵌入式 Tomcat、 Jetty、 Undertow 或 Netty 创建一个自包含的 HTTP 服务器。大多数 Web 应用程序使用`spring-boot-starter-web`模块来快速启动和运行。你还可以选择使用`spring-boot-starter-webflux`模块构建反应性 Web 应用程序。 + +如果你还没有开发 Spring 启动 Web 应用程序,那么可以按照*[Getting started](getting-started.html#getting-started.first-application)*部分中的“Hello World!”示例进行操作。 + +## 1. Servlet Web 应用程序 + +如果你想构建基于 Servlet 的 Web 应用程序,可以利用 Spring Boot 的自动配置来实现 Spring MVC 或 Jersey。 + +### 1.1.“ Spring Web MVC 框架” + +[Spring Web MVC framework](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc)(通常被称为“ Spring MVC”)是一个丰富的“模型视图控制器”Web 框架。 Spring MVC 允许你创建特殊的`@Controller`或`@RestController`bean 来处理传入的 HTTP 请求。通过使用`@RequestMapping`注释,将控制器中的方法映射到 HTTP。 + +下面的代码显示了一个提供 JSON 数据的典型`@RestController`: + +``` +import java.util.List; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/users") +public class MyRestController { + + private final UserRepository userRepository; + + private final CustomerRepository customerRepository; + + public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) { + this.userRepository = userRepository; + this.customerRepository = customerRepository; + } + + @GetMapping("/{user}") + public User getUser(@PathVariable Long userId) { + return this.userRepository.findById(userId).get(); + } + + @GetMapping("/{user}/customers") + public List getUserCustomers(@PathVariable Long userId) { + return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get(); + } + + @DeleteMapping("/{user}") + public void deleteUser(@PathVariable Long userId) { + this.userRepository.deleteById(userId); + } + +} + +``` + +“WebMVC.FN”是一种功能变体,它将路由配置与请求的实际处理分离开来,如下例所示: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.function.RequestPredicate; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.springframework.web.servlet.function.RequestPredicates.accept; +import static org.springframework.web.servlet.function.RouterFunctions.route; + +@Configuration(proxyBeanMethods = false) +public class MyRoutingConfiguration { + + private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON); + + @Bean + public RouterFunction routerFunction(MyUserHandler userHandler) { + return route() + .GET("/{user}", ACCEPT_JSON, userHandler::getUser) + .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers) + .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser) + .build(); + } + +} + +``` + +``` +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.function.ServerRequest; +import org.springframework.web.servlet.function.ServerResponse; + +@Component +public class MyUserHandler { + + public ServerResponse getUser(ServerRequest request) { + ... + return ServerResponse.ok().build(); + } + + public ServerResponse getUserCustomers(ServerRequest request) { + ... + return ServerResponse.ok().build(); + } + + public ServerResponse deleteUser(ServerRequest request) { + ... + return ServerResponse.ok().build(); + } + +} + +``` + +Spring MVC 是核心 Spring 框架的一部分,详细信息可在[参考文献](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc)中获得。在[spring.io/guides](https://spring.io/guides)上也有几个涵盖 Spring MVC 的指南。 + +| |你可以定义任意多的`RouterFunction`bean 来模块化路由器的定义。如果需要应用优先级,可以订购
bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.1. Spring MVC 自动配置 + +Spring Boot 为 Spring MVC 提供了自动配置,该配置在大多数应用程序中都能很好地工作。 + +自动配置在 Spring 的默认值之上添加了以下功能: + +* 包含`ContentNegotiatingViewResolver`和`BeanNameViewResolver`bean。 + +* 对服务静态资源的支持,包括对 WebJAR 的支持(覆盖[在本文的后面部分](features.html#web.servlet.spring-mvc.static-content))。 + +* 自动注册`Converter`、`GenericConverter`和`Formatter`bean。 + +* 支持`HttpMessageConverters`(覆盖[在本文的后面部分](features.html#web.servlet.spring-mvc.message-converters))。 + +* 自动注册`MessageCodesResolver`(覆盖[在本文的后面部分](features.html#web.servlet.spring-mvc.message-codes))。 + +* 静态`index.html`支持。 + +* 自动使用`ConfigurableWebBindingInitializer` Bean(覆盖[在本文的后面部分](features.html#web.servlet.spring-mvc.binding-initializer))。 + +如果你希望保留那些 Spring 引导 MVC 自定义并使更多[MVC 定制](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc)(拦截器、格式化程序、视图控制器和其他功能),则可以添加你自己的`@Configuration`类型的`WebMvcConfigurer`但**没有**`@EnableWebMvc`类。 + +如果希望提供`RequestMappingHandlerMapping`、`RequestMappingHandlerAdapter`或`ExceptionHandlerExceptionResolver`的自定义实例,并且仍然保留 Spring 引导 MVC 自定义,则可以声明类型`WebMvcRegistrations`的 Bean 并使用它来提供这些组件的自定义实例。 + +如果你想完全控制 Spring MVC,可以添加你自己的`@Configuration`,并使用`@EnableWebMvc`进行注释,或者也可以添加你自己的`@Configuration`-注释`DelegatingWebMvcConfiguration`,如`@EnableWebMvc`的 javadoc 中所描述的。 + +| |Spring MVC 使用不同的`ConversionService`来转换来自`application.properties`或`application.yaml`文件的值,
表示`Period`,`Duration`和`DataSize`转换器不可用,并且`@DurationUnit`和`@DataSizeUnit`注释将被忽略。

如果你想定制 Spring MVC 使用的`ConversionService`,你可以提供带有`WebMvcConfigurer` Bean 的`addFormatters`方法。
从该方法你可以注册任何你喜欢的转换器,或者你可以将其委托给`ApplicationConversionService`上可用的静态方法。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.2.HtpMessageConverters + +Spring MVC 使用`HttpMessageConverter`接口来转换 HTTP 请求和响应。合理的默认值是开箱即用的。例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用 JacksonXML 扩展(如果可用的话),或者通过使用 JAXB(如果 JacksonXML 扩展不可用的话)。默认情况下,字符串编码为`UTF-8`。 + +如果需要添加或自定义转换器,可以使用 Spring boot 的`HttpMessageConverters`类,如以下清单所示: + +``` +import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; + +@Configuration(proxyBeanMethods = false) +public class MyHttpMessageConvertersConfiguration { + + @Bean + public HttpMessageConverters customConverters() { + HttpMessageConverter additional = new AdditionalHttpMessageConverter(); + HttpMessageConverter another = new AnotherHttpMessageConverter(); + return new HttpMessageConverters(additional, another); + } + +} + +``` + +上下文中存在的任何`HttpMessageConverter` Bean 都被添加到转换器列表中。你也可以用同样的方法覆盖默认的转换器。 + +#### 1.1.3.自定义 JSON 序列化器和反序列化器 + +如果使用 Jackson 序列化和反序列化 JSON 数据,则可能需要编写自己的`JsonSerializer`和`JsonDeserializer`类。自定义序列化器通常是[通过模块在 Jackson 中注册](https://github.com/FasterXML/jackson-docs/wiki/JacksonHowToCustomSerializers),但是 Spring Boot 提供了一种替代的`@JsonComponent`注释,使得直接注册 Spring bean 更加容易。 + +你可以直接在`JsonSerializer`、`JsonDeserializer`或`KeyDeserializer`实现上使用`@JsonComponent`注释。你也可以在包含序列化器/反序列化器作为内部类的类上使用它,如以下示例所示: + +``` +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.springframework.boot.jackson.JsonComponent; + +@JsonComponent +public class MyJsonComponent { + + public static class Serializer extends JsonSerializer { + + @Override + public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException { + jgen.writeStringField("name", value.getName()); + jgen.writeNumberField("age", value.getAge()); + } + + } + + public static class Deserializer extends JsonDeserializer { + + @Override + public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + ObjectCodec codec = jsonParser.getCodec(); + JsonNode tree = codec.readTree(jsonParser); + String name = tree.get("name").textValue(); + int age = tree.get("age").intValue(); + return new MyObject(name, age); + } + + } + +} + +``` + +在`ApplicationContext`中的所有`@JsonComponent`bean 都会自动注册到 Jackson 中。由于`@JsonComponent`是用`@Component`进行元注释的,因此通常的组件扫描规则适用。 + +Spring Boot 还提供了[jsonobjectserializer’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectSerializer.java)和[jsonobjectdeserializer’](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jackson/JsonObjectDeserializer.java)基类,它们在序列化对象时为标准 Jackson 版本提供了有用的替代方案。有关详细信息,请参见 Javadoc 中的[jsonobjectserializer’](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/jackson/JsonObjectSerializer.html)和[jsonobjectdeserializer’](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/jackson/JsonObjectDeserializer.html)。 + +上面的示例可以重写为使用`JsonObjectSerializer`/`jsonobjectdeserializer’,如下所示: + +``` +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.springframework.boot.jackson.JsonComponent; +import org.springframework.boot.jackson.JsonObjectDeserializer; +import org.springframework.boot.jackson.JsonObjectSerializer; + +@JsonComponent +public class MyJsonComponent { + + public static class Serializer extends JsonObjectSerializer { + + @Override + protected void serializeObject(MyObject value, JsonGenerator jgen, SerializerProvider provider) + throws IOException { + jgen.writeStringField("name", value.getName()); + jgen.writeNumberField("age", value.getAge()); + } + + } + + public static class Deserializer extends JsonObjectDeserializer { + + @Override + protected MyObject deserializeObject(JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, + JsonNode tree) throws IOException { + String name = nullSafeValue(tree.get("name"), String.class); + int age = nullSafeValue(tree.get("age"), Integer.class); + return new MyObject(name, age); + } + + } + +} + +``` + +#### 1.1.4.MessageCodesResolver + +Spring MVC 具有用于生成用于从绑定错误呈现错误消息的错误代码的策略:。如果你设置`spring.mvc.message-codes-resolver-format`属性`PREFIX_ERROR_CODE`或`POSTFIX_ERROR_CODE`, Spring boot 将为你创建一个属性(请参见[`DefaultMessageCodesResolver.format’](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/validation/DefaultMessageCodesResolver.Format.html)中的枚举)。 + +#### 1.1.5.静态内容 + +默认情况下, Spring 引导从 Classpath 中的一个名为`/static`(或`/public`或`/resources`或`/META-INF/resources`)的目录或从`ServletContext`的根目录提供静态内容。它使用 Spring MVC 中的`ResourceHttpRequestHandler`,这样你就可以通过添加自己的`WebMvcConfigurer`并重写`addResourceHandlers`方法来修改该行为。 + +在独立的 Web 应用程序中,来自容器的默认 Servlet 也被启用,并充当后备,如果 Spring 决定不处理它,则从`ServletContext`的根目录中提供内容。在大多数情况下,这种情况不会发生(除非你修改了默认的 MVC 配置),因为 Spring 始终可以通过`DispatcherServlet`处理请求。 + +默认情况下,资源映射在`/**`上,但你可以使用`spring.mvc.static-path-pattern`属性对其进行调优。例如,将所有资源重新定位到`/resources/**`可以通过以下方式实现: + +属性 + +``` +spring.mvc.static-path-pattern=/resources/** +``` + +Yaml + +``` +spring: + mvc: + static-path-pattern: "/resources/**" +``` + +你还可以通过使用`spring.web.resources.static-locations`属性(用目录位置列表替换默认值)来定制静态资源位置。根 Servlet 上下文路径`"/"`也会自动添加为位置。 + +除了前面提到的“标准”静态资源位置之外,[Webjars content](https://www.webjars.org/)还有一个特殊情况。路径在`/webjars/**`中的任何资源,如果以 WebJARS 格式打包,都将从 jar 文件中得到服务。 + +| |如果你的应用程序被打包为 jar,请不要使用`src/main/webapp`目录。
虽然这个目录是一个通用的标准,但是它在 WAR 打包中是**只是**的,如果你生成 jar,大多数构建工具都会默默地忽略它。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +Spring 启动还支持由 Spring MVC 提供的高级资源处理功能,允许使用诸如破坏缓存的静态资源或使用用于 WebJAR 的与版本无关的 URL 的情况。 + +要为 WebJAR 使用与版本无关的 URL,请添加`webjars-locator-core`依赖项。然后申报你的 Webjar。以 jQuery 为例,添加`"/webjars/jquery/jquery.min.js"`会导致`"/webjars/jquery/x.y.z/jquery.min.js"`,其中`x.y.z`是 WebJAR 版本。 + +| |如果使用 JBoss,则需要声明`webjars-locator-jboss-vfs`依赖项,而不是`webjars-locator-core`。
否则,所有 WebJAR 解析为`404`。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +为了使用缓存破坏,以下配置为所有静态资源配置了一个缓存破坏解决方案,有效地在 URL 中添加了内容散列,例如``: + +属性 + +``` +spring.web.resources.chain.strategy.content.enabled=true +spring.web.resources.chain.strategy.content.paths=/** +``` + +Yaml + +``` +spring: + web: + resources: + chain: + strategy: + content: + enabled: true + paths: "/**" +``` + +| |资源的链接在运行时在模板中被重写,由于`ResourceUrlEncodingFilter`是为 ThymeLeaf 和 FreeMarker 自动配置的。
在使用 JSP 时,你应该手动声明此过滤器。
其他模板引擎目前不能自动支持,但可以与自定义模板宏/助手一起使用[` 资源提供者’](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/web/servlet/resource/ResourceUrlProvider.html)。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +例如,当使用 JavaScript 模块加载程序动态加载资源时,不能选择重命名文件。这就是为什么其他策略也得到了支持,并且可以结合在一起。“固定”策略在不更改文件名的情况下在 URL 中添加静态版本字符串,如下例所示: + +属性 + +``` +spring.web.resources.chain.strategy.content.enabled=true +spring.web.resources.chain.strategy.content.paths=/** +spring.web.resources.chain.strategy.fixed.enabled=true +spring.web.resources.chain.strategy.fixed.paths=/js/lib/ +spring.web.resources.chain.strategy.fixed.version=v12 +``` + +Yaml + +``` +spring: + web: + resources: + chain: + strategy: + content: + enabled: true + paths: "/**" + fixed: + enabled: true + paths: "/js/lib/" + version: "v12" +``` + +在这种配置下,位于`"/js/lib/"`下的 JavaScript 模块使用固定的版本控制策略(`“/v12/js/lib/mymodule.js”`),而其他资源仍然使用 Content One(``)。 + +有关更多支持的选项,请参见[“Web属性.resources”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/Web属性.java)。 + +| |这个特性已经在专用的[blog post](https://spring.io/blog/2014/07/24/spring-framework-4-1-handling-static-web-resources)和 Spring 框架的[参考文献](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-config-static-resources)中进行了详细的描述。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.6.欢迎页面 + +Spring 启动支持静态和模板化欢迎页面。它首先在配置的静态内容位置中查找`index.html`文件。如果没有找到一个,那么它将查找`index`模板。如果找到其中之一,它将自动用作应用程序的欢迎页面。 + +#### 1.1.7.路径匹配和内容协商 + +Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,控制器方法上的`@GetMapping`注释)进行匹配,将传入的 HTTP 请求映射到处理程序。 + +Spring Boot 默认情况下选择禁用后缀模式匹配,这意味着像`"GET /projects/spring-boot.json"`这样的请求将不会匹配到`@GetMapping("/projects/spring-boot")`映射。这被认为是[best practice for Spring MVC applications](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-ann-requestmapping-suffix-pattern-match)。这个特性在过去主要用于 HTTP 客户机,因为 HTTP 客户机没有发送正确的“接受”请求头;我们需要确保向客户机发送正确的内容类型。如今,内容协商更可靠了。 + +还有其他方法来处理不能始终发送正确的“接受”请求头的 HTTP 客户机。与使用后缀匹配不同,我们可以使用一个查询参数来确保像`"GET /projects/spring-boot?format=json"`这样的请求将被映射到`@GetMapping("/projects/spring-boot")`: + +属性 + +``` +spring.mvc.contentnegotiation.favor-parameter=true +``` + +Yaml + +``` +spring: + mvc: + contentnegotiation: + favor-parameter: true +``` + +或者,如果你更喜欢使用不同的参数名: + +属性 + +``` +spring.mvc.contentnegotiation.favor-parameter=true +spring.mvc.contentnegotiation.parameter-name=myparam +``` + +Yaml + +``` +spring: + mvc: + contentnegotiation: + favor-parameter: true + parameter-name: "myparam" +``` + +大多数标准媒体类型都支持开箱即用,但你也可以定义新的媒体类型: + +属性 + +``` +spring.mvc.contentnegotiation.media-types.markdown=text/markdown +``` + +Yaml + +``` +spring: + mvc: + contentnegotiation: + media-types: + markdown: "text/markdown" +``` + +后缀模式匹配不受欢迎,将在未来的版本中删除。如果你理解这些警告,并且仍然希望你的应用程序使用后缀模式匹配,则需要进行以下配置: + +属性 + +``` +spring.mvc.contentnegotiation.favor-path-extension=true +spring.mvc.pathmatch.use-suffix-pattern=true +``` + +Yaml + +``` +spring: + mvc: + contentnegotiation: + favor-path-extension: true + pathmatch: + use-suffix-pattern: true +``` + +或者,与其打开所有后缀模式,不如只支持已注册的后缀模式更安全: + +属性 + +``` +spring.mvc.contentnegotiation.favor-path-extension=true +spring.mvc.pathmatch.use-registered-suffix-pattern=true +``` + +Yaml + +``` +spring: + mvc: + contentnegotiation: + favor-path-extension: true + pathmatch: + use-registered-suffix-pattern: true +``` + +在 Spring Framework5.3 中, Spring MVC 支持几种实现策略,用于将请求路径匹配到控制器处理程序。它以前只支持`AntPathMatcher`策略,但现在也提供`PathPatternParser`策略。 Spring 现在的启动提供了一种可在新策略中选择和 OPT 的配置属性: + +属性 + +``` +spring.mvc.pathmatch.matching-strategy=path-pattern-parser +``` + +Yaml + +``` +spring: + mvc: + pathmatch: + matching-strategy: "path-pattern-parser" +``` + +有关为什么应该考虑这个新实现的更多详细信息,请参见[专门的博客文章](https://spring.io/blog/2020/06/30/url-matching-with-pathpattern-in-spring-mvc)。 + +| |`PathPatternParser`是一种优化的实现,但限制了[一些路径模式变体](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-ann-requestmapping-uri-templates)的使用,并且与后缀模式匹配(` Spring.mvc.pathmatch.use-suffix-pattern`,` Spring.mvc.pathmatch.use-registered-suffix-spattern`)或映射带有 Servlet 前缀的`DispatcherServlet`(` Spring.mvc. Servlet.path`)不兼容。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.8.配置 WebBindingInitializer + +Spring MVC 使用`WebBindingInitializer`对特定请求初始化`WebDataBinder`。如果你创建自己的`ConfigurableWebBindingInitializer``@Bean`, Spring boot 会自动配置 Spring MVC 来使用它。 + +#### 1.1.9.模板引擎 + +与 REST Web 服务一样,你也可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持各种模板化技术,包括 ThymeLeaf、FreeMarker 和 JSP。此外,许多其他模板引擎还包括它们自己的 Spring MVC 集成。 + +Spring 启动包括对以下模板引擎的自动配置支持: + +* [FreeMarker](https://freemarker.apache.org/docs/) + +* [Groovy](https://docs.groovy-lang.org/docs/next/html/documentation/template-engines.html#_the_markuptemplateengine) + +* [Thymeleaf](https://www.thymeleaf.org) + +* [Mustache](https://mustache.github.io/) + +| |如果可能的话,应该避免使用 JSP。
在使用嵌入式 Servlet 容器时,有几个[已知限制](#web.servlet.embedded-container.jsp-limitations)。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +当你使用这些模板引擎中的一个具有默认配置时,你的模板将从`src/main/resources/templates`中自动拾取。 + +| |取决于你如何运行你的应用程序,你的 IDE 可能会以不同的方式对 Classpath 进行排序。
在 IDE 中从其主方法运行应用程序会导致与使用 Maven 或 Gradle 运行应用程序时不同的排序。或者从其打包的 jar。
这可能会导致 Spring 引导无法找到预期的模板。
如果你有这个问题,你可以重新排序 IDE 中的 Classpath,以便首先放置模块的类和资源。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.1.10.错误处理 + +默认情况下, Spring Boot 提供了一个`/error`映射,该映射以合理的方式处理所有错误,并且在 Servlet 容器中将其注册为“全局”错误页。对于机器客户机,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个“whitelabel”错误视图,该视图以 HTML 格式呈现相同的数据(要对其进行自定义,请添加一个`View`,将其解析为`error`)。 + +如果你想定制默认的错误处理行为,那么可以设置许多`server.error`属性。参见附录中的[“服务器属性”](application-properties.html#appendix.application-properties.server)部分。 + +要完全替换缺省行为,可以实现`ErrorController`并注册该类型的 Bean 定义,或者添加`ErrorAttributes`类型的 Bean 来使用现有机制但替换内容。 + +| |`BasicErrorController`可以用作自定义`ErrorController`的基类,
如果你想为新的内容类型添加一个处理程序,这一点特别有用(默认情况是专门处理`text/html`,并为其他所有内容提供一个后备),
这样做,扩展`BasicErrorController`,添加一个带有`@RequestMapping`属性的公共方法,并创建新类型的 Bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +还可以定义一个用`@ControllerAdvice`注释的类,以定制 JSON 文档,从而返回特定的控制器和/或异常类型,如以下示例所示: + +``` +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +@ControllerAdvice(basePackageClasses = SomeController.class) +public class MyControllerAdvice extends ResponseEntityExceptionHandler { + + @ResponseBody + @ExceptionHandler(MyException.class) + public ResponseEntity handleControllerException(HttpServletRequest request, Throwable ex) { + HttpStatus status = getStatus(request); + return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status); + } + + private HttpStatus getStatus(HttpServletRequest request) { + Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); + HttpStatus status = HttpStatus.resolve(code); + return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR; + } + +} + +``` + +在前面的示例中,如果`YourException`由在与`SomeController`相同的包中定义的控制器抛出,则使用`CustomErrorType`POJO 的 JSON 表示,而不是`ErrorAttributes`表示。 + +在某些情况下,在控制器级别处理的错误不会被[度量基础设施](actuator.html#actuator.metrics.supported.spring-mvc)记录。通过将已处理的异常设置为请求属性,应用程序可以确保将此类异常与请求度量一起记录: + +``` +import javax.servlet.http.HttpServletRequest; + +import org.springframework.boot.web.servlet.error.ErrorAttributes; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@Controller +public class MyController { + + @ExceptionHandler(CustomException.class) + String handleCustomException(HttpServletRequest request, CustomException ex) { + request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex); + return "errorView"; + } + +} + +``` + +##### 自定义错误页面 + +如果你想为给定的状态代码显示自定义 HTML 错误页,那么可以将文件添加到`/error`目录。错误页可以是静态 HTML(即添加到任何静态资源目录下),也可以通过使用模板构建。文件的名称应该是确切的状态代码或系列掩码。 + +例如,要将`404`映射到静态 HTML 文件,你的目录结构如下: + +``` +src/ + +- main/ + +- java/ + | + + +- resources/ + +- public/ + +- error/ + | +- 404.html + +- +``` + +要使用 freemarker 模板映射所有`5xx`错误,你的目录结构如下: + +``` +src/ + +- main/ + +- java/ + | + + +- resources/ + +- templates/ + +- error/ + | +- 5xx.ftlh + +- +``` + +对于更复杂的映射,还可以添加实现`ErrorViewResolver`接口的 bean,如下例所示: + +``` +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; +import org.springframework.http.HttpStatus; +import org.springframework.web.servlet.ModelAndView; + +public class MyErrorViewResolver implements ErrorViewResolver { + + @Override + public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map model) { + // Use the request or status to optionally return a ModelAndView + if (status == HttpStatus.INSUFFICIENT_STORAGE) { + // We could add custom model values here + new ModelAndView("myview"); + } + return null; + } + +} + +``` + +还可以使用常规的 Spring MVC 特性,例如[@ExceptionHandler 方法](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-exceptionhandlers)和[@controlleradvice](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-ann-controller-advice)。然后,`ErrorController`将获取任何未处理的异常。 + +##### 映射 Spring MVC#### 以外的错误页面 + +对于不使用 Spring MVC 的应用程序,可以使用`ErrorPageRegistrar`接口直接注册`ErrorPages`。这个抽象可以直接与底层嵌入的 Servlet 容器一起工作,即使你没有 Spring MVC也可以工作。 + +``` +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; + +@Configuration(proxyBeanMethods = false) +public class MyErrorPagesConfiguration { + + @Bean + public ErrorPageRegistrar errorPageRegistrar() { + return this::registerErrorPages; + } + + private void registerErrorPages(ErrorPageRegistry registry) { + registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400")); + } + +} + +``` + +| |如果你注册了一个`ErrorPage`,其路径最终由`Filter`处理(这在一些非 Spring Web 框架中很常见,例如 Jersey 和 Wicket),那么`Filter`必须显式地注册为`ERROR`调度器,如以下示例所示:| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +``` +import java.util.EnumSet; + +import javax.servlet.DispatcherType; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MyFilterConfiguration { + + @Bean + public FilterRegistrationBean myFilter() { + FilterRegistrationBean registration = new FilterRegistrationBean<>(new MyFilter()); + // ... + registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class)); + return registration; + } + +} + +``` + +请注意,默认的`FilterRegistrationBean`不包括`ERROR`Dispatcher 类型。 + +##### WAR 部署中的错误处理 + +当部署到 Servlet 容器时, Spring 引导使用其错误页过滤器将具有错误状态的请求转发到适当的错误页。这是必要的,因为 Servlet 规范没有提供用于注册错误页的 API。根据部署 WAR 文件的容器和应用程序使用的技术,可能需要进行一些额外的配置。 + +如果响应尚未提交,则错误页筛选器只能将请求转发到正确的错误页。默认情况下,WebSphere Application Server8.0 及更高版本在成功完成 Servlet 的服务方法后提交响应。你应该通过将`com.ibm.ws.webcontainer.invokeFlushAfterService`设置为`false`来禁用此行为。 + +如果你正在使用 Spring 安全性,并且希望访问错误页中的主体,则必须配置 Spring Security 的筛选器,以便在错误分派时调用该筛选器。为此,将`spring.security.filter.dispatcher-types`属性设置为`async, error, forward, request`。 + +#### 1.1.11.CORS 支持 + +[跨源资源共享](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing)是一个[W3C 规范](https://www.w3.org/TR/cors/)由[most browsers](https://caniuse.com/#feat=cors)实现的[W3C 规范](https://www.w3.org/TR/cors/),它允许你以一种灵活的方式指定授权哪种类型的跨域请求,而不是使用一些安全性较低、功能较弱的方法,例如 IFRAME 或 JSONP。 + +截至版本 4.2, Spring MVC[supports CORS](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-cors)。在 Spring 引导应用程序中使用[控制器方法 CORS 配置](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-cors-controller)和[`@CrossOrigin`](https://docs.spring.io/spring-framework/docs/5.3.16/javadoc-api/org/springframework/web/bind/annotation/CrossOrigin.html)注释不需要任何特定的配置。[全局 CORS 配置](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-cors-global)可以通过使用自定义的`WebMvcConfigurer` Bean 方法注册`addCorsMappings(CorsRegistry)`方法来定义[全局 CORS 配置](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web.html#mvc-cors-global),如以下示例所示: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration(proxyBeanMethods = false) +public class MyCorsConfiguration { + + @Bean + public WebMvcConfigurer corsConfigurer() { + return new WebMvcConfigurer() { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/api/**"); + } + + }; + } + +} + +``` + +### 1.2.JAX-RS 和泽西岛 + +如果你更喜欢用于 REST 端点的 JAX-RS 编程模型,那么你可以使用其中一个可用的实现,而不是 Spring MVC。和完全可以开箱即用。CXF 要求你在应用程序上下文中将其`Servlet`或`Filter`注册为`@Bean`。Jersey 提供了一些本机 Spring 支持,因此我们还在 Spring 启动中为它提供了自动配置支持,以及一个启动器。 + +要开始使用 Jersey,将`spring-boot-starter-jersey`作为依赖项,然后需要一个类型为`ResourceConfig`的`@Bean`,在其中注册所有端点,如以下示例所示: + +``` +import org.glassfish.jersey.server.ResourceConfig; + +import org.springframework.stereotype.Component; + +@Component +public class MyJerseyConfig extends ResourceConfig { + + public MyJerseyConfig() { + register(MyEndpoint.class); + } + +} + +``` + +| |Jersey 对扫描可执行文档的支持相当有限,例如,当运行可执行 WAR 文件时,它无法扫描在[fully executable jar file](deployment.html#deployment.installing)或`WEB-INF/classes`中找到的包中的端点。
为了避免这种限制,不应使用`packages`方法,端点应该使用`register`方法单独注册,如前面的示例所示。| +|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +对于更高级的定制,你还可以注册任意数量的 bean 来实现`ResourceConfigCustomizer`。 + +所有注册的端点都应该是`@Components`,带有 HTTP 资源注释(`@get’和其他),如下例所示: + +``` +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import org.springframework.stereotype.Component; + +@Component +@Path("/hello") +public class MyEndpoint { + + @GET + public String message() { + return "Hello"; + } + +} + +``` + +由于`Endpoint`是 Spring `@Component`,因此其生命周期由 Spring 管理,你可以使用`@Autowired`注释来注入依赖项,并使用`@Value`注释来注入外部配置。默认情况下,泽西 Servlet 被注册并映射到`/*`。你可以通过将`@ApplicationPath`添加到你的`ResourceConfig`来更改映射。 + +默认情况下,在`ServletRegistrationBean`类型的`@Bean`中将泽西设置为 Servlet,命名为`jerseyServletRegistration`。默认情况下, Servlet 是惰性初始化的,但是你可以通过设置`spring.jersey.servlet.load-on-startup`来定制这种行为。你可以通过创建你自己的同名应用程序来禁用或重写它 Bean。还可以通过设置`spring.jersey.type=filter`(在这种情况下,要替换或覆盖的`@Bean`是`jerseyFilterRegistration`)来使用过滤器而不是 Servlet。过滤器有一个`@Order`,你可以用`spring.jersey.filter.order`对其进行设置。当使用 Jersey 作为过滤器时,必须有一个 Servlet 来处理未被 Jersey 截获的任何请求。如果你的应用程序不包含这样的 Servlet,那么你可能希望通过将`server.servlet.register-default-servlet`设置为`true`来启用缺省 Servlet。 Servlet 和过滤器注册都可以通过使用`spring.jersey.init.*`来指定属性的映射来给定 init 参数。 + +### 1.3.嵌入式 Servlet 容器支持 + +对于 Servlet 应用程序, Spring 引导包括对嵌入式[Tomcat](https://tomcat.apache.org/)、[Jetty](https://www.eclipse.org/jetty/)和[Undertow](https://github.com/undertow-io/undertow)服务器的支持。大多数开发人员使用适当的“启动器”来获得完全配置的实例。默认情况下,嵌入式服务器侦听端口`8080`上的 HTTP 请求。 + +#### 1.3.1.servlet、过滤器和侦听器 + +当使用嵌入式 Servlet 容器时,可以通过使用 Spring bean 或通过扫描 Servlet 组件来注册来自 Servlet 规范的 servlet、过滤器和所有侦听器(例如`HttpSessionListener`)。 + +##### 将 servlet、过滤器和侦听器注册为 Spring bean##### + +任何`Servlet`,`Filter`,或 Servlet `*Listener`实例中的 Spring Bean 都是在嵌入式容器中注册的。如果你希望在配置过程中引用来自`application.properties`的值,那么这将非常方便。 + +默认情况下,如果上下文只包含一个 Servlet,则将其映射到`/`。在多个 Servlet bean 的情况下, Bean 名称被用作路径前缀。过滤器映射到`/*`。 + +如果基于约定的映射不够灵活,则可以使用`ServletRegistrationBean`、`FilterRegistrationBean`和`ServletListenerRegistrationBean`类来完成控制。 + +通常情况下,保持过滤豆的无序状态是安全的。如果需要特定的顺序,你应该用`@Order`注释`Filter`,或者使其实现`Ordered`。不能通过用`@Order`注释其 Bean 方法来配置`Filter`的顺序。如果不能将`Filter`类更改为添加`@Order`或实现`Ordered`,则必须为`Filter`定义一个`FilterRegistrationBean`,并使用`setOrder(int)`方法设置注册 Bean 的顺序。避免配置读取请求主体`Ordered.HIGHEST_PRECEDENCE`的筛选器,因为它可能与应用程序的字符编码配置不一致。如果 Servlet 过滤器包装了该请求,则应将其配置为小于或等于`OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER`的顺序。 + +| |要查看应用程序中每个`Filter`的顺序,请启用`web`[logging group](features.html#features.logging.log-groups)的调试级别日志。
将在启动时记录已注册过滤器的详细信息,包括它们的顺序和 URL 模式。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +| |在注册`Filter`bean 时要小心,因为它们是在应用程序生命周期的早期进行初始化的。
如果需要注册与其他 bean 交互的`Filter`,请考虑使用[“委托过滤程序注册”](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/web/servlet/DelegatingFilterProxyRegistrationBean.html)。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.2. Servlet 上下文初始化 + +嵌入式 Servlet 容器不直接执行 Servlet 3.0+`javax.servlet.ServletContainerInitializer`接口或 Spring 的`org.springframework.web.WebApplicationInitializer`接口。这是一种有意的设计决策,旨在降低设计用于在 WAR 内部运行的第三方库可能破坏 Spring 引导应用程序的风险。 + +如果需要在 Spring 引导应用程序中执行 Servlet 上下文初始化,则应注册实现`org.springframework.boot.web.servlet.ServletContextInitializer`接口的 Bean。单一的`onStartup`方法提供了对`ServletContext`的访问,并且如果需要,可以很容易地用作现有`WebApplicationInitializer`的适配器。 + +##### 扫描 servlet、过滤器和侦听器 #### + +当使用嵌入式容器时,使用`@WebServlet`、`@WebFilter`和`@WebListener`注释的类的自动注册可以通过使用`@ServletComponentScan`来启用。 + +| |`@ServletComponentScan`在独立的容器中没有任何作用,相反,在独立的容器中使用的是容器的内置发现机制。| +|---|--------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.3.ServletWebServerApplicationContext + +在引擎盖下, Spring 引导使用不同类型的`ApplicationContext`用于嵌入式 Servlet 容器支持。`ServletWebServerApplicationContext`是`WebApplicationContext`的一种特殊类型,它通过搜索单个`ServletWebServerFactory`来引导自身。通常`TomcatServletWebServerFactory`、`JettyServletWebServerFactory`或`UndertowServletWebServerFactory`已被自动配置。 + +| |你通常不需要了解这些实现类。
大多数应用程序都是自动配置的,并且适当的`ApplicationContext`和`ServletWebServerFactory`是代表你创建的。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 1.3.4.自定义嵌入式 Servlet 容器 + +公共 Servlet 容器设置可以通过使用 Spring `Environment`属性来配置。通常,你会在`application.properties`或`application.yaml`文件中定义属性。 + +常见的服务器设置包括: + +* 网络设置:监听传入 HTTP 请求的端口,绑定到`server.address`的接口地址,等等。 + +* 会话设置:会话是否持久(“服务器. Servlet.会话.Persistent”)、会话超时(“服务器. Servlet.会话.Timeout”)、会话数据的位置(“服务器. Servlet.会话.存储-目录”)以及会话-cookie 配置(“服务器. Servlet.会话.cookie.*”)。 + +* 错误管理:错误页面的位置(“server.error.path”)等等。 + +* [SSL](howto.html#howto.webserver.configure-ssl) + +* [HTTP 压缩](howto.html#howto.webserver.enable-response-compression) + +Spring 引导尝试尽可能多地公开公共设置,但这并不总是可能的。对于这些情况,专用的名称空间提供了特定于服务器的定制(参见`server.tomcat`和`server.undertow`)。例如,[access logs](howto.html#howto.webserver.configure-access-logs)可以配置具有嵌入式 Servlet 容器的特定特征。 + +| |有关完整列表,请参见[“serverproperties”](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/Server属性.java)类。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### Samesite 饼干 + +Web 浏览器可以使用`SameSite`cookie 属性来控制跨站点请求中是否以及如何提交 cookie。该属性对于现代 Web 浏览器特别相关,因为它们已经开始更改缺省属性时使用的默认值。 + +如果要更改会话 cookie 的`SameSite`属性,可以使用`server.servlet.session.cookie.same-site`属性。自动配置的 Tomcat、 Jetty 和 Undertow 服务器支持此属性。它还用于配置基于`SessionRepository`bean 的 Spring 会话 Servlet。 + +例如,如果你希望你的会话 cookie 具有`SameSite`的`None`属性,则可以将以下内容添加到你的`application.properties`或`application.yaml`文件中: + +属性 + +``` +server.servlet.session.cookie.same-site=none +``` + +Yaml + +``` +server: + servlet: + session: + cookie: + same-site: "none" +``` + +如果要更改添加到`HttpServletResponse`的其他 cookie 上的`SameSite`属性,可以使用`CookieSameSiteSupplier`。`CookieSameSiteSupplier`传递了一个`Cookie`,并可能返回一个`SameSite`值,或`null`。 + +有许多方便的工厂和过滤方法,你可以使用它们来快速匹配特定的 Cookie。例如,添加下面的 Bean 将自动为所有名称与正则表达式`myapp.*`匹配的 cookie 应用`SameSite`的`SameSite`。 + +``` +import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +public class MySameSiteConfiguration { + + @Bean + public CookieSameSiteSupplier applicationCookieSameSiteSupplier() { + return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*"); + } + +} + +``` + +##### 程序化定制 + +如果你需要以编程方式配置你的嵌入式 Servlet 容器,那么你可以注册一个 Spring Bean 来实现`WebServerFactoryCustomizer`接口。`WebServerFactoryCustomizer` 提供对`ConfigurableServletWebServerFactory`的访问,其中包括许多自定义设置器方法。下面的示例以编程方式显示了端口的设置: + +``` +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.stereotype.Component; + +@Component +public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer { + + @Override + public void customize(ConfigurableServletWebServerFactory server) { + server.setPort(9000); + } + +} + +``` + +`TomcatServletWebServerFactory`、`JettyServletWebServerFactory`和`UndertowServletWebServerFactory`是`ConfigurableServletWebServerFactory`的专用变体,它们分别具有用于 Tomcat、 Jetty 和 Undertow 的附加自定义设置器方法。下面的示例展示了如何定制`TomcatServletWebServerFactory`,从而提供对 Tomcat 特定配置选项的访问: + +``` +import java.time.Duration; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + +@Component +public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer { + + @Override + public void customize(TomcatServletWebServerFactory server) { + server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis())); + } + +} + +``` + +##### 直接自定义配置 WebServerFactory##### + +对于需要从`ServletWebServerFactory`扩展的更高级的用例,你可以自己公开这种类型的 Bean。 + +为许多配置选项提供了设置器。如果你需要做一些更奇特的事情,还提供了几种受保护的方法“挂钩”。有关详细信息,请参见[源代码文档](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.html)。 + +| |自动配置的 Customizer 仍然应用于你的自定义工厂,因此请小心使用该选项。| +|---|---------------------------------------------------------------------------------------------------| + +#### 1.3.5.JSP 限制 + +当运行使用嵌入式 Servlet 容器(并打包为可执行归档文件)的 Spring 引导应用程序时,JSP 支持中存在一些限制。 + +* 对于 Jetty 和 Tomcat,如果使用战争包装,它应该可以工作。当使用`java -jar`启动时,可执行 WAR 将工作,并且还可以部署到任何标准容器。在使用可执行文件 jar 时,不支持 JSP。 + +* Undertow 不支持 JSP。 + +* 创建自定义`error.jsp`页面不会覆盖[error handling](#web.servlet.spring-mvc.error-handling)的默认视图。应该使用[自定义错误页面](#web.servlet.spring-mvc.error-handling.error-pages)。 + +## 2. 反应式 Web 应用程序 + +Spring 启动通过为 Spring WebFlux 提供自动配置简化了反应性 Web 应用程序的开发。 + +### 2.1.“ Spring WebFlux 框架” + +Spring WebFlux 是在 Spring Framework5.0 中引入的新的反应性 Web 框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步和非阻塞的,并且通过[反应堆项目](https://projectreactor.io/)实现了[反应流](https://www.reactive-streams.org/)规范。 + +Spring WebFlux 有两种风格:基于功能的和基于注释的。基于注释的模型非常接近 Spring MVC 模型,如以下示例所示: + +``` +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/users") +public class MyRestController { + + private final UserRepository userRepository; + + private final CustomerRepository customerRepository; + + public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) { + this.userRepository = userRepository; + this.customerRepository = customerRepository; + } + + @GetMapping("/{user}") + public Mono getUser(@PathVariable Long userId) { + return this.userRepository.findById(userId); + } + + @GetMapping("/{user}/customers") + public Flux getUserCustomers(@PathVariable Long userId) { + return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser); + } + + @DeleteMapping("/{user}") + public void deleteUser(@PathVariable Long userId) { + this.userRepository.deleteById(userId); + } + +} + +``` + +“WebFlux.FN”是一种功能变体,它将路由配置与请求的实际处理分离开来,如下例所示: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.RequestPredicate; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; + +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; + +@Configuration(proxyBeanMethods = false) +public class MyRoutingConfiguration { + + private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON); + + @Bean + public RouterFunction monoRouterFunction(MyUserHandler userHandler) { + return route() + .GET("/{user}", ACCEPT_JSON, userHandler::getUser) + .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers) + .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser) + .build(); + } + +} + +``` + +``` +import reactor.core.publisher.Mono; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class MyUserHandler { + + public Mono getUser(ServerRequest request) { + ... + } + + public Mono getUserCustomers(ServerRequest request) { + ... + } + + public Mono deleteUser(ServerRequest request) { + ... + } + +} + +``` + +WebFlux 是 Spring 框架的一部分,其[参考文献](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#webflux-fn)中提供了详细信息。 + +| |你可以定义任意多的`RouterFunction`bean 来模块化路由器的定义。如果需要应用优先级,可以订购
bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要开始,将`spring-boot-starter-webflux`模块添加到应用程序中。 + +| |在应用程序中同时添加`spring-boot-starter-web`和`spring-boot-starter-webflux`模块将导致 Spring 引导自动配置 Spring MVC,不是 WebFlux.
之所以选择这种行为,是因为许多 Spring 开发人员将`spring-boot-starter-webflux`添加到他们的 Spring MVC 应用程序中,以使用反应式`WebClient`。
你仍然可以通过将选择的应用程序类型设置为`SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)`来强制执行你的选择。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +“WebFlux.FN”是一种功能变体,它将路由配置与请求的实际处理分离开来,如下例所示: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.server.RequestPredicate; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerResponse; + +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; + +@Configuration(proxyBeanMethods = false) +public class MyRoutingConfiguration { + + private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON); + + @Bean + public RouterFunction monoRouterFunction(MyUserHandler userHandler) { + return route() + .GET("/{user}", ACCEPT_JSON, userHandler::getUser) + .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers) + .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser) + .build(); + } + +} + +``` + +``` +import reactor.core.publisher.Mono; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; + +@Component +public class MyUserHandler { + + public Mono getUser(ServerRequest request) { + ... + } + + public Mono getUserCustomers(ServerRequest request) { + ... + } + + public Mono deleteUser(ServerRequest request) { + ... + } + +} + +``` + +WebFlux 是 Spring 框架的一部分,其[参考文献](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#webflux-fn)中提供了详细信息。 + +| |你可以定义任意多的`RouterFunction`bean 来模块化路由器的定义。如果需要应用优先级,可以订购
bean。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| + +要开始,将`spring-boot-starter-webflux`模块添加到应用程序中。 + +| |在应用程序中同时添加`spring-boot-starter-web`和`spring-boot-starter-webflux`模块将导致 Spring 引导自动配置 Spring MVC,不是 WebFlux.
之所以选择这种行为,是因为许多 Spring 开发人员将`spring-boot-starter-webflux`添加到他们的 Spring MVC 应用程序中,以使用反应式`WebClient`。
你仍然可以通过将选择的应用程序类型设置为`SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)`来强制执行你的选择。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.1. Spring WebFlux 自动配置 + +Spring Boot 为 Spring WebFlux 提供了能够在大多数应用程序中很好地工作的自动配置。 + +自动配置在 Spring 的默认值之上添加了以下功能: + +* 为`HttpMessageReader`和`HttpMessageWriter`实例配置编解码器(描述[在本文的后面部分](#web.reactive.webflux.httpcodecs))。 + +* 对服务静态资源的支持,包括对 WebJAR 的支持(描述[在本文的后面部分](#web.servlet.spring-mvc.static-content))。 + +如果你希望保留 Spring Boot WebFlux 特性并且希望添加额外的[WebFlux 配置](https://docs.spring.io/spring-framework/docs/5.3.16/reference/html/web-reactive.html#webflux-config),则可以添加你自己的`@Configuration`类型的类别`WebFluxConfigurer`但是**没有**`@EnableWebFlux`。 + +如果你想完全控制 Spring WebFlux,那么可以添加你自己的`@Configuration`,并使用`@EnableWebFlux`进行注释。 + +#### 2.1.2.带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器 + +Spring WebFlux 使用`HttpMessageReader`和`HttpMessageWriter`接口来转换 HTTP 请求和响应。通过查看 Classpath 中可用的库,将它们配置为`CodecConfigurer`,以具有合理的默认值。 + +Spring Boot 为编解码器提供了专用的配置属性,`spring.codec.*`。它还通过使用`CodecCustomizer`实例应用进一步的定制。例如,`spring.jackson.*`配置键被应用于 Jackson 编解码器。 + +如果需要添加或自定义编解码器,可以创建自定义`CodecCustomizer`组件,如下例所示: + +``` +import org.springframework.boot.web.codec.CodecCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.codec.ServerSentEventHttpMessageReader; + +@Configuration(proxyBeanMethods = false) +public class MyCodecsConfiguration { + + @Bean + public CodecCustomizer myCodecCustomizer() { + return (configurer) -> { + configurer.registerDefaults(false); + configurer.customCodecs().register(new ServerSentEventHttpMessageReader()); + // ... + }; + } + +} + +``` + +你还可以利用[Boot 的自定义 JSON 序列化器和反序列化器](#web.servlet.spring-mvc.json)。 + +#### 2.1.3.静态内容 + +默认情况下, Spring 引导从 Classpath 中的一个名为`/static`(或`/public`或`/resources`或`/META-INF/resources`)的目录中提供静态内容。它使用 Spring WebFlux 中的`ResourceWebHandler`,这样你就可以通过添加自己的`WebFluxConfigurer`并重写`addResourceHandlers`方法来修改该行为。 + +默认情况下,资源映射在`/**`上,但是你可以通过设置`spring.webflux.static-path-pattern`属性对其进行调优。例如,将所有资源重新定位到`/resources/**`可以通过以下方式实现: + +属性 + +``` +spring.webflux.static-path-pattern=/resources/** +``` + +Yaml + +``` +spring: + webflux: + static-path-pattern: "/resources/**" +``` + +你还可以使用`spring.web.resources.static-locations`自定义静态资源位置。这样做可以用目录位置列表替换默认值。如果你这样做,默认的欢迎页面检测将切换到你的自定义位置。因此,如果在启动时的任何位置都有`index.html`,那么它就是应用程序的主页。 + +除了前面列出的“标准”静态资源位置之外,[Webjars content](https://www.webjars.org/)还有一个特殊情况。路径在`/webjars/**`中的任何资源,如果以 WebJARS 格式打包,都将从 jar 文件中得到服务。 + +| |Spring WebFlux 应用程序不严格依赖于 Servlet API,因此它们不能作为 WAR 文件部署,也不使用`src/main/webapp`目录。| +|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------| + +#### 2.1.4.欢迎页面 + +Spring 启动支持静态和模板化欢迎页面。它首先在配置的静态内容位置中查找`index.html`文件。如果没有找到一个,那么它将查找`index`模板。如果找到其中之一,它将自动用作应用程序的欢迎页面。 + +#### 2.1.5.模板引擎 + +与 REST Web 服务一样,你也可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持各种模板化技术,包括胸腺叶、自由标记和胡子。 + +Spring 启动包括对以下模板引擎的自动配置支持: + +* [FreeMarker](https://freemarker.apache.org/docs/) + +* [Thymeleaf](https://www.thymeleaf.org) + +* [Mustache](https://mustache.github.io/) + +当你使用这些模板引擎中的一个具有默认配置时,你的模板将从`src/main/resources/templates`中自动拾取。 + +#### 2.1.6.错误处理 + +Spring Boot 提供了一个`WebExceptionHandler`,它以合理的方式处理所有错误。它在处理顺序中的位置紧随 WebFlux 提供的处理程序之前,后者被认为是最后一个处理程序。对于机器客户机,它生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。对于浏览器客户端,有一个“WhiteLabel”错误处理程序,它以 HTML 格式呈现相同的数据。你也可以提供自己的 HTML 模板来显示错误(参见[next section](#web.reactive.webflux.error-handling.error-pages))。 + +定制此功能的第一步通常涉及使用现有机制,但要替换或增加错误内容。为此,可以添加`ErrorAttributes`类型的 Bean。 + +要更改错误处理行为,可以实现`ErrorWebExceptionHandler`并注册该类型的 Bean 定义。因为`ErrorWebExceptionHandler`是相当低级的, Spring Boot 还提供了一个方便的`AbstractErrorWebExceptionHandler`,让你以 WebFlux 功能方式处理错误,如以下示例所示: + +``` +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.web.WebProperties.Resources; +import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler; +import org.springframework.boot.web.reactive.error.ErrorAttributes; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.reactive.function.server.ServerResponse.BodyBuilder; + +@Component +public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler { + + public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, Resources resources, + ApplicationContext applicationContext) { + super(errorAttributes, resources, applicationContext); + } + + @Override + protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) { + return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml); + } + + private boolean acceptsXml(ServerRequest request) { + return request.headers().accept().contains(MediaType.APPLICATION_XML); + } + + public Mono handleErrorAsXml(ServerRequest request) { + BodyBuilder builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR); + // ... additional builder calls + return builder.build(); + } + +} + +``` + +为了获得更完整的图像,你还可以直接重写子类`DefaultErrorWebExceptionHandler`并覆盖特定的方法。 + +在某些情况下,[度量基础设施](actuator.html#actuator.metrics.supported.spring-webflux)不会记录在控制器或处理程序函数级别上处理的错误。通过将已处理的异常设置为请求属性,应用程序可以确保将此类异常与请求度量一起记录: + +``` +import org.springframework.boot.web.reactive.error.ErrorAttributes; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.reactive.result.view.Rendering; +import org.springframework.web.server.ServerWebExchange; + +@Controller +public class MyExceptionHandlingController { + + @GetMapping("/profile") + public Rendering userProfile() { + // ... + throw new IllegalStateException(); + } + + @ExceptionHandler(IllegalStateException.class) + public Rendering handleIllegalState(ServerWebExchange exchange, IllegalStateException exc) { + exchange.getAttributes().putIfAbsent(ErrorAttributes.ERROR_ATTRIBUTE, exc); + return Rendering.view("errorView").modelAttribute("message", exc.getMessage()).build(); + } + +} + +``` + +##### 自定义错误页面 + +如果你想为给定的状态代码显示自定义 HTML 错误页,那么可以将文件添加到`/error`目录。错误页可以是静态 HTML(即添加到任何静态资源目录下),也可以是用模板构建的。文件的名称应该是确切的状态代码或系列掩码。 + +例如,要将`404`映射到静态 HTML 文件,你的目录结构如下: + +``` +src/ + +- main/ + +- java/ + | + + +- resources/ + +- public/ + +- error/ + | +- 404.html + +- +``` + +要使用 mustache 模板映射所有`5xx`错误,你的目录结构如下: + +``` +src/ + +- main/ + +- java/ + | + + +- resources/ + +- templates/ + +- error/ + | +- 5xx.mustache + +- +``` + +#### 2.1.7.网页过滤器 + +Spring WebFlux 提供了一个`WebFilter`接口,该接口可以实现为过滤 HTTP 请求-响应交换。在应用程序上下文中找到的 `WebFilter’bean 将自动用于过滤每个交换。 + +在过滤器的顺序很重要的地方,它们可以实现`Ordered`或用`@Order`进行注释。 Spring 引导自动配置可以为你配置 Web 过滤器。当它这样做时,将使用下表所示的订单: + +|网页过滤器| Order | +|---------------------------------------|--------------------------------| +|`MetricsWebFilter`|`Ordered.HIGHEST_PRECEDENCE + 1`| +|`WebFilterChainProxy`( Spring security)| `-100` | +|`HttpTraceWebFilter`|`Ordered.LOWEST_PRECEDENCE - 10`| + +### 2.2.嵌入式反应式服务器支持 + +Spring 引导包括对以下嵌入式反应性 Web 服务器的支持:Reactor Netty、 Tomcat、 Jetty 和 Undertow。大多数开发人员使用适当的“启动器”来获得完全配置的实例。默认情况下,嵌入式服务器监听端口 8080 上的 HTTP 请求。 + +### 2.3.反应式服务器资源配置 + +在自动配置反应堆网络或 Jetty 服务器时, Spring 启动将创建特定的 bean,这些 bean 将向服务器实例提供 HTTP 资源:`ReactorResourceFactory`或`JettyResourceFactory`。 + +默认情况下,这些资源也将与 Reactor Netty 和 Jetty 客户端共享,以获得最佳性能,前提是: + +* 同样的技术也用于服务器和客户端。 + +* 客户机实例是使用由 Spring 引导自动配置的`WebClient.Builder` Bean 来构建的。 + +开发人员可以通过提供自定义`ReactorResourceFactory`或`JettyResourceFactory` Bean 来覆盖 Jetty 和反应器网络的资源配置-这将同时应用于客户机和服务器。 + +你可以在[WebClient 运行时部分](io.html#io.rest-client.webclient.runtime)中了解有关客户端资源配置的更多信息。 + +## 3. 优雅的关机 + +所有四个嵌入式 Web 服务器( Jetty、Reactor Netty、 Tomcat 和 Undertow)以及反应式和基于 Servlet 的 Web 应用程序都支持优雅的关闭。它作为关闭应用程序上下文的一部分出现,并在停止`SmartLifecycle`bean 的最早阶段执行。此停止处理使用超时,该超时提供了一个宽限期,在此期间,将允许完成现有请求,但不允许新的请求。不允许新请求的确切方式取决于所使用的 Web 服务器。 Jetty、反应器网和 Tomcat 将在网络层停止接受请求。 Undertow 将接受请求,但立即响应具有服务不可用的(503)响应。 + +| |使用 Tomcat 的优雅关机需要 Tomcat 9.0.33 或更高版本。| +|---|--------------------------------------------------------------| + +要启用优雅的关机,请配置`server.shutdown`属性,如以下示例所示: + +属性 + +``` +server.shutdown=graceful +``` + +Yaml + +``` +server: + shutdown: "graceful" +``` + +要配置超时周期,请配置`spring.lifecycle.timeout-per-shutdown-phase`属性,如以下示例所示: + +属性 + +``` +spring.lifecycle.timeout-per-shutdown-phase=20s +``` + +Yaml + +``` +spring: + lifecycle: + timeout-per-shutdown-phase: "20s" +``` + +| |如果 IDE 没有发送适当的`SIGTERM`信号,那么在 IDE 中使用 Graceful Shutdown 可能无法正常工作。
有关更多详细信息,请参见 IDE 的文档。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 4. Spring Security + +如果[Spring Security](https://spring.io/projects/spring-security)在 Classpath 上,那么默认情况下 Web 应用程序是安全的。 Spring 启动依赖于 Spring 安全性的内容协商策略来确定是使用`httpBasic`还是`formLogin`。要向 Web 应用程序添加方法级安全性,还可以添加带有所需设置的`@EnableGlobalMethodSecurity`。其他信息可以在[Spring Security Reference Guide](https://docs.spring.io/spring-security/reference/5.6.2/servlet/authorization/method-security.html)中找到。 + +默认的`UserDetailsService`只有一个用户。用户名是`user`,密码是随机的,并在应用程序启动时在信息级别打印,如以下示例所示: + +``` +Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35 +``` + +| |如果你微调了日志配置,请确保将`org.springframework.boot.autoconfigure.security`类别设置为 log`INFO`-level 消息。
否则,将不打印默认密码。| +|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +你可以通过提供`spring.security.user.name`和`spring.security.user.password`来更改用户名和密码。 + +默认情况下,在 Web 应用程序中获得的基本特性是: + +* Bean 具有内存存储和具有生成密码的单个用户(有关用户的属性,请参见[securityproperties.user](https://docs.spring.io/spring-boot/docs/2.6.4/api/org/springframework/boot/autoconfigure/security/Security属性.User.html))的`UserDetailsService`(在 WebFlux 应用程序的情况下为`ReactiveUserDetailsService`)。 + +* 基于表单的登录或 HTTP 基本安全性(取决于请求中的`Accept`头)用于整个应用程序(如果致动器在 Classpath 上,则包括致动器端点)。 + +* 用于发布身份验证事件的`DefaultAuthenticationEventPublisher`。 + +你可以通过为它添加一个 Bean 来提供一个不同的`AuthenticationEventPublisher`。 + +### 4.1.MVC 安全 + +默认的安全配置是在`SecurityAutoConfiguration`和`UserDetailsServiceAutoConfiguration`中实现的。`SecurityAutoConfiguration`imports`SpringBootWebSecurityConfiguration`用于 Web 安全和`UserDetailsServiceAutoConfiguration`配置身份验证,这在非 Web 应用程序中也是相关的。要完全关闭默认的 Web 应用程序安全配置,或者组合多个 Spring 安全组件,例如 OAuth2 客户机和资源服务器,请添加类型`SecurityFilterChain`的 Bean(这样做不会禁用`UserDetailsService`配置或执行器的安全性)。 + +要同时关闭`UserDetailsService`配置,可以添加类型为`UserDetailsService`、`AuthenticationProvider`或`AuthenticationManager`的 Bean。 + +可以通过添加自定义`SecurityFilterChain`或`WebSecurityConfigurerAdapter` Bean 来覆盖访问规则。 Spring Boot 提供了方便的方法,可以用来覆盖对执行器端点和静态资源的访问规则。`EndPointRequest’可以用来创建一个基于属性的。`PathRequest’可以用来为常用位置的资源创建一个。 + +### 4.2.WebFlux 安全性 + +与 Spring MVC 应用程序类似,你可以通过添加`spring-boot-starter-security`依赖项来保护你的 WebFlux 应用程序。默认的安全配置是在`ReactiveSecurityAutoConfiguration`和`UserDetailsServiceAutoConfiguration`中实现的。用于 Web 安全的 reactiveSecurityAutoConfiguration`imports`WebFluxSecurityConfiguration`和`UserDetailsServiceAutoConfiguration`配置身份验证,这在非 Web 应用程序中也是相关的。要完全关闭默认的 Web 应用程序安全配置,可以添加类型`WebFilterChainProxy`的 Bean(这样做不会禁用`UserDetailsService`配置或执行器的安全性)。 + +要关闭`UserDetailsService`配置,可以添加类型`ReactiveUserDetailsService`或`ReactiveAuthenticationManager`的 Bean。 + +Spring 使用诸如 OAuth2 客户端和资源服务器的多个安全组件的访问规则可以通过添加自定义 Bean 来配置。 Spring Boot 提供了方便的方法,这些方法可用于覆盖执行器端点和静态资源的访问规则。`EndPointRequest’可用于创建基于`ServerWebExchangeMatcher`的`management.endpoints.web.base-path`属性。 + +`PathRequest`可用于为常用位置中的资源创建`ServerWebExchangeMatcher`。 + +例如,你可以通过添加以下内容来定制你的安全配置: + +``` +import org.springframework.boot.autoconfigure.security.reactive.PathRequest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@Configuration(proxyBeanMethods = false) +public class MyWebFluxSecurityConfiguration { + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http.authorizeExchange((spec) -> { + spec.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll(); + spec.pathMatchers("/foo", "/bar").authenticated(); + }); + http.formLogin(); + return http.build(); + } + +} + +``` + +### 4.3.OAuth2 + +[OAuth2](https://oauth.net/2/)是 Spring 支持的一个广泛使用的授权框架。 + +#### 4.3.1.客户 + +如果你的 Classpath 上有`spring-security-oauth2-client`,则可以利用一些自动配置来设置 OAuth2/Open ID Connect 客户端。此配置使用`OAuth2Client属性`下的属性。同样的性质也适用于 Servlet 和活性应用。 + +你可以在`spring.security.oauth2.client`前缀下注册多个 OAuth2 客户机和提供者,如下例所示: + +属性 + +``` +spring.security.oauth2.client.registration.my-client-1.client-id=abcd +spring.security.oauth2.client.registration.my-client-1.client-secret=password +spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope +spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider +spring.security.oauth2.client.registration.my-client-1.scope=user +spring.security.oauth2.client.registration.my-client-1.redirect-uri=https://my-redirect-uri.com +spring.security.oauth2.client.registration.my-client-1.client-authentication-method=basic +spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization-code + +spring.security.oauth2.client.registration.my-client-2.client-id=abcd +spring.security.oauth2.client.registration.my-client-2.client-secret=password +spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope +spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider +spring.security.oauth2.client.registration.my-client-2.scope=email +spring.security.oauth2.client.registration.my-client-2.redirect-uri=https://my-redirect-uri.com +spring.security.oauth2.client.registration.my-client-2.client-authentication-method=basic +spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code + +spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server/oauth/authorize +spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server/oauth/token +spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server/userinfo +spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header +spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server/token_keys +spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name +``` + +Yaml + +``` +spring: + security: + oauth2: + client: + registration: + my-client-1: + client-id: "abcd" + client-secret: "password" + client-name: "Client for user scope" + provider: "my-oauth-provider" + scope: "user" + redirect-uri: "https://my-redirect-uri.com" + client-authentication-method: "basic" + authorization-grant-type: "authorization-code" + + my-client-2: + client-id: "abcd" + client-secret: "password" + client-name: "Client for email scope" + provider: "my-oauth-provider" + scope: "email" + redirect-uri: "https://my-redirect-uri.com" + client-authentication-method: "basic" + authorization-grant-type: "authorization_code" + + provider: + my-oauth-provider: + authorization-uri: "https://my-auth-server/oauth/authorize" + token-uri: "https://my-auth-server/oauth/token" + user-info-uri: "https://my-auth-server/userinfo" + user-info-authentication-method: "header" + jwk-set-uri: "https://my-auth-server/token_keys" + user-name-attribute: "name" +``` + +对于支持[OpenID 连接发现](https://openid.net/specs/openid-connect-discovery-1_0.html)的 OpenID Connect 提供者,可以进一步简化配置。提供程序需要配置`issuer-uri`,这是它断言为其发行者标识符的 URI。例如,如果提供的`issuer-uri`是“https://example.com”,那么将对“https://example.com/.well-known/openid-configuration”进行`OpenID Provider Configuration Request`。预期结果是`OpenID Provider Configuration Response`。下面的示例展示了如何使用`issuer-uri`配置 OpenID Connect 提供程序: + +属性 + +``` +spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/ +``` + +Yaml + +``` +spring: + security: + oauth2: + client: + provider: + oidc-provider: + issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/" +``` + +默认情况下, Spring Security 的`OAuth2LoginAuthenticationFilter`只处理匹配`/login/oauth2/code/*`的 URL。如果希望自定义`redirect-uri`以使用不同的模式,则需要提供处理该自定义模式的配置。例如,对于 Servlet 应用程序,你可以添加你自己的类似于以下内容的`SecurityFilterChain`: + +``` +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration(proxyBeanMethods = false) +public class MyOAuthClientConfiguration { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated(); + http.oauth2Login().redirectionEndpoint().baseUri("custom-callback"); + return http.build(); + } + +} + +``` + +| |Spring 引导自动配置由 Spring Security 用于管理客户端注册的`InMemoryOAuth2AuthorizedClientService`的
的`InMemoryOAuth2AuthorizedClientService`的功能有限,我们建议仅将其用于开发环境。
对于生产环境,可以考虑使用`JdbcOAuth2AuthorizedClientService`或创建自己的`OAuth2AuthorizedClientService`实现。| +|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +##### OAuth2 共同提供者的客户端注册 + +对于常见的 OAuth2 和 OpenID 提供者,包括 Google、GitHub、Facebook 和 OKTA,我们提供了一组提供者默认值(分别为 `google’、`github`、`facebook`和`okta`)。 + +如果不需要自定义这些提供程序,可以将`provider`属性设置为需要推断默认值的属性。此外,如果客户端注册的键与缺省支持的提供者匹配, Spring 启动也会推断出这一点。 + +换句话说,以下示例中的两个配置使用了 Google Provider: + +属性 + +``` +spring.security.oauth2.client.registration.my-client.client-id=abcd +spring.security.oauth2.client.registration.my-client.client-secret=password +spring.security.oauth2.client.registration.my-client.provider=google +spring.security.oauth2.client.registration.google.client-id=abcd +spring.security.oauth2.client.registration.google.client-secret=password +``` + +Yaml + +``` +spring: + security: + oauth2: + client: + registration: + my-client: + client-id: "abcd" + client-secret: "password" + provider: "google" + google: + client-id: "abcd" + client-secret: "password" +``` + +#### 4.3.2.资源服务器 + +如果你的 Classpath 上有`spring-security-oauth2-resource-server`,则 Spring 引导可以设置 OAuth2 资源服务器。对于 JWT 配置,需要指定一个 JWK 集 URI 或 OIDC Issuer URI,如以下示例所示: + +属性 + +``` +spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys +``` + +Yaml + +``` +spring: + security: + oauth2: + resourceserver: + jwt: + jwk-set-uri: "https://example.com/oauth2/default/v1/keys" +``` + +Properties + +``` +spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/ +``` + +Yaml + +``` +spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/" +``` + +| |如果授权服务器不支持 JWK 设置的 URI,则可以使用用于验证 JWT 签名的公钥配置资源服务器。
这可以使用`spring.security.oauth2.resourceserver.jwt.public-key-location`属性完成,值需要指向包含 PEM 编码的 X509 格式的公钥的文件。| +|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +同样的性质也适用于 Servlet 和活性应用。 + +或者,你可以为 Servlet 应用程序定义自己的`JwtDecoder` Bean,或者为反应性应用程序定义`ReactiveJwtDecoder`。 + +在使用不透明令牌而不是 JWTS 的情况下,你可以配置以下属性来通过内省来验证令牌: + +Properties + +``` +spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token +spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id +spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret +``` + +Yaml + +``` +spring: + security: + oauth2: + resourceserver: + opaquetoken: + introspection-uri: "https://example.com/check-token" + client-id: "my-client-id" + client-secret: "my-client-secret" +``` + +同样,同样的属性也适用于 Servlet 和无反应应用。 + +或者,你可以为 Servlet 应用程序定义自己的`OpaqueTokenIntrospector` Bean,或者为反应性应用程序定义`ReactiveOpaqueTokenIntrospector`。 + +#### 4.3.3.授权服务器 + +Spring 目前,安全性不提供对实现 OAuth2.0 授权服务器的支持。然而,该功能可从[Spring Security OAuth](https://spring.io/projects/spring-security-oauth)项目中获得,该项目最终将完全被 Spring 安全性所取代。在此之前,你可以使用`spring-security-oauth2-autoconfigure`模块轻松地设置 OAuth2.0 授权服务器;有关说明,请参见其[documentation](https://docs.spring.io/spring-security-oauth2-boot/)。 + +### 4.4.SAML2.0 + +#### 4.4.1.依赖方 + +如果你的 Classpath 上有`spring-security-saml2-service-provider`,则可以利用一些自动配置来设置 SAML2.0 依赖方。此配置使用`Saml2RelyingPartyProperties`下的属性。 + +依赖方注册表示身份提供程序 IDP 和服务提供程序 SP 之间的配对配置。你可以在`spring.security.saml2.relyingparty`前缀下注册多个依赖方,如下例所示: + +Properties + +``` +spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key +spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate +spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].private-key-location=path-to-private-key +spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].certificate-location=path-to-certificate +spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.verification.credentials[0].certificate-location=path-to-verification-cert +spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.entity-id=remote-idp-entity-id1 +spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.sso-url=https://remoteidp1.sso.url + +spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key +spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate +spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].private-key-location=path-to-private-key +spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].certificate-location=path-to-certificate +spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.verification.credentials[0].certificate-location=path-to-other-verification-cert +spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.entity-id=remote-idp-entity-id2 +spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.sso-url=https://remoteidp2.sso.url +``` + +Yaml + +``` +spring: + security: + saml2: + relyingparty: + registration: + my-relying-party1: + signing: + credentials: + - private-key-location: "path-to-private-key" + certificate-location: "path-to-certificate" + decryption: + credentials: + - private-key-location: "path-to-private-key" + certificate-location: "path-to-certificate" + identityprovider: + verification: + credentials: + - certificate-location: "path-to-verification-cert" + entity-id: "remote-idp-entity-id1" + sso-url: "https://remoteidp1.sso.url" + + my-relying-party2: + signing: + credentials: + - private-key-location: "path-to-private-key" + certificate-location: "path-to-certificate" + decryption: + credentials: + - private-key-location: "path-to-private-key" + certificate-location: "path-to-certificate" + identityprovider: + verification: + credentials: + - certificate-location: "path-to-other-verification-cert" + entity-id: "remote-idp-entity-id2" + sso-url: "https://remoteidp2.sso.url" +``` + +## 5. Spring 届会议 + +Spring 引导为广泛的数据存储范围提供[Spring Session](https://spring.io/projects/spring-session)自动配置。在构建 Servlet Web 应用程序时,可以自动配置以下存储: + +* JDBC + +* 雷迪斯 + +* 黑泽尔卡斯特 + +* MongoDB + +Servlet 自动配置取代了使用`@Enable*HttpSession`的需要。 + +在构建反应式 Web 应用程序时,可以自动配置以下存储: + +* 雷迪斯 + +* MongoDB + +反应式自动配置取代了使用`@Enable*WebSession`的需要。 + +如果在 Classpath 上存在单个 Spring 会话模块,则 Spring 引导自动使用该存储实现。如果有多个实现,则必须选择要用来存储会话的[`StoreType`](https://github.com/spring-projects/spring-boot/tree/v2.6.4/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/session/StoreType.java)。例如,要使用 JDBC 作为后端存储,你可以按以下方式配置应用程序: + +Properties + +``` +spring.session.store-type=jdbc +``` + +Yaml + +``` +spring: + session: + store-type: "jdbc" +``` + +| |通过将`store-type`设置为`none`,可以禁用 Spring 会话。| +|---|---------------------------------------------------------------------| + +每个商店都有特定的附加设置。例如,可以自定义 JDBC 存储表的名称,如下例所示: + +Properties + +``` +spring.session.jdbc.table-name=SESSIONS +``` + +Yaml + +``` +spring: + session: + jdbc: + table-name: "SESSIONS" +``` + +为了设置会话的超时,你可以使用`spring.session.timeout`属性。如果该属性未在 Servlet Web 应用程序中设置,则自动配置将返回到`server.servlet.session.timeout`的值。 + +你可以使用`@Enable*HttpSession`( Servlet)或`@Enable*WebSession`(反应式)来控制 Spring 会话的配置。这将导致自动配置后退。 Spring 然后可以使用注释的属性而不是先前描述的配置属性来配置会话。 + +## 6. Spring 仇恨 + +如果你开发了一个使用超媒体的 RESTful API, Spring Boot 为 Spring Hateoas 提供了自动配置,这在大多数应用程序中都能很好地工作。自动配置取代了使用`@EnableHypermediaSupport`的需要,并注册了许多 bean,以方便构建基于超媒体的应用程序,包括`LinkDiscoverers`(用于客户端支持)和`ObjectMapper`,这些配置用于将响应正确地编组到所需的表示中。`ObjectMapper`是通过设置各种`spring.jackson.*`属性来定制的,或者,如果存在一个属性,则通过`Jackson2ObjectMapperBuilder`来定制 Bean。 + +你可以通过使用`@EnableHypermediaSupport`来控制 Spring Hateoas 的配置。请注意,这样做会禁用前面描述的`ObjectMapper`定制。 + +| |`spring-boot-starter-hateoas`是特定于 Spring MVC 的,并且不应该与 Spring WebFlux 合并。
为了使用 Spring Hateoas 与 Spring WebFlux,可以在`org.springframework.hateoas:spring-hateoas`以及`spring-boot-starter-webflux`上添加一个直接依赖项。| +|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + +## 7. 接下来读什么? + +你现在应该对如何使用 Spring 启动来开发 Web 应用程序有了很好的了解。接下来的几个部分描述了 Spring 引导如何集成各种[数据技术](data.html#data)、[消息传递系统](messaging.html#messaging)和其他 IO 功能。你可以根据应用程序的需求选择其中的任何一个。