Appearance
17. “How-to” 指南
本节提供了一些在使用 Spring Boot 时经常出现的常见 ‘how do I do that…’ 问题的答案. 它的覆盖范围不是很详尽,但是确实覆盖了很多.
如果您有一个我们不在此讨论的特定问题,则可能需要查看 stackoverflow.com 以查看是否有人已经提供了答案. 这也是询问新问题的好地方 (请使用 spring-boot
标签) .
我们也很乐意扩展此部分. 如果您想添加 "操作方法",请向我们发送 pull request.
17.1. Spring Boot Application
本部分包括与 Spring Boot 应用程序直接相关的主题.
17.1.1. 创建自己的 FailureAnalyzer
FailureAnalyzer
这是拦截启动时将异常转化为人类可读消息 (包装在) 的一种好方法 FailureAnalysis
. Spring Boot 为与应用程序上下文相关的异常,JSR-303 验证等提供了此类分析器. 您也可以创建自己的. .
AbstractFailureAnalyzer
是一个方便的扩展,FailureAnalyzer
它检查要处理的异常中是否存在指定的异常类型. 您可以对此进行扩展,以便您的实现只有在异常出现时才有机会处理该异常. 如果由于某种原因无法处理该异常,请返回 null
以使另一个实现有机会处理该异常.
FailureAnalyzer
实现必须在 META-INF/spring.factories
中注册. 以下示例注册 ProjectConstraintViolationFailureAnalyzer
:
properties
org.springframework.boot.diagnostics.FailureAnalyzer=\com.example.ProjectConstraintViolationFailureAnalyzer
TIP
如果您需要访问 BeanFactory
或 Environment
,则 FailureAnalyzer
可以分别实现 BeanFactoryAware
或 EnvironmentAware
.
17.1.2. 自动配置故障排除
Spring Boot 自动配置会尽力 "做正确的事",但有时会失败,并且很难说出原因.
ConditionEvaluationReport
任何 Spring Boot 都有一个非常有用的功能 ApplicationContext
. 如果启用 DEBUG
日志记录输出,则可以看到它. 如果使用 spring-boot-actuator
(请参阅 Actuator 一章) , 那么还有一个 conditions
端点,该端点以 JSON 形式呈现报告. 使用该端点来调试应用程序,并在运行时查看 Spring Boot 添加了哪些功能 (尚未添加) .
通过查看源代码和 Javadoc,可以回答更多问题. 阅读代码时,请记住以下经验法则:
查找被调用的类
*AutoConfiguration
并阅读其源代码. 特别注意@Conditional*
注解,以了解它们启用了哪些功能以及何时启用. 添加--debug
到命令行或系统属性-Ddebug
以在控制台上获取在您的应用中做出的所有自动配置决策的日志. 在启用了 Actuator 的运行应用程序中,查看conditions
端点 (/actuator/conditions
或等效的JMX) 以获取相同信息.查找属于
@ConfigurationProperties
(例如ServerProperties
) 的类,然后从中读取可用的外部配置选项. 该@ConfigurationProperties
注解具有一个name
充当前缀外部性能属性. 因此,ServerProperties
拥有prefix="server"
和它的配置性能server.port,server.address
以及其他. 在启用了 Actuator 的运行应用程序中,查看configprops
端点.寻找对
bind
方法的使用,以一种轻松的方式Binder
将配置值明确地拉出Environment
. 它通常与前缀一起使用.查找
@Value
直接绑定到的注解Environment
.寻找
@ConditionalOnExpression
注解以响应SpEL表达式来打开或关闭功能,这些注解通常使用从中解析的占位符进行评估Environment
.
17.1.3. 启动之前自定义环境或 ApplicationContext
一个 SpringApplication
具有 ApplicationListeners
与 ApplicationContextInitializers
被用于应用自定义的上下文或环境. Spring Boot 加载了许多此类自定义项,以供内部使用 META-INF/spring.factories
. 注册其他自定义项的方法有多种:
在运行之前,通过对每个应用程序进行编程,方法是调用
SpringApplication
的addListeners
和addInitializers
方法.通过设置
context.initializer.classes
或context.listener.classes
属性,以声明的方式针对每个应用程序.声明性地,对于所有应用程序,通过添加
META-INF/spring.factories
和打包一个 jar 文件,这些文件都被应用程序库.
该 SpringApplication
将一些特殊的 ApplicationEvents
发送给监听器 (有些甚至在上下文创建之前) ,然后为 ApplicationContext
发布的事件注册监听器. 有关完整列表,请参见 ‘Spring Boot 特性’ 部分中的 “应用程序事件和监听器” .
还可以使用来自定义 Environment
刷新应用程序上下文之前的 EnvironmentPostProcessor
. 每个实现都应在 META-INF/spring.factories
中注册,如以下示例所示:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
该实现可以加载任意文件并将其添加到中 Environment
. 例如,以下示例从类路径加载 YAML 配置文件:
java
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);
}
}
}
TIP
在 Environment
已经准备好了 Spring Boot 默认加载的所有常用属性源. 因此可以从环境中获取文件的位置. 前面的示例将 custom-resource
属性源添加到列表的末尾,以便在其他任何常见位置定义的 key 具有优先权. 自定义实现可以定义另一个顺序.
TIP
虽然在 @SpringBootApplication
上使用 @PropertySource
似乎是在环境中加载自定义资源的便捷方法, 但我们不建议您这样做. 在刷新应用程序上下文之前, 不会将此类属性源添加到 Environment
中. 现在配置某些属性 (如 logging.*
和 spring.main.*
) 为时已晚,这些属性在刷新开始之前已读取.
17.1.4. 建立 ApplicationContext 层次结构 (添加父上下文或根上下文)
您可以使用 ApplicationBuilder
类创建父/子 ApplicationContext
层次结构. 有关更多信息,请参见 ‘Spring Boot 特性’ 部分中的 “Fluent Builder API(流式构建 API)”.
17.1.5. 创建一个非 Web 应用程序
并非所有的 Spring 应用程序都必须是 Web 应用程序 (或 Web 服务) . 如果要在 main
方法中执行一些代码,又要引导 Spring 应用程序以设置要使用的基础结构,则可以使用 Spring Boot 的 SpringApplication
功能. SpringApplication
根据是否认为需要 Web 应用程序来更改其 ApplicationContext
类. 您可以做的第一件事是让服务器相关的依赖 (例如 Servlet API) 脱离类路径. 如果你不能做到这一点 (例如,您从相同的代码库的两个应用程序) ,则可以在 SpringApplication
实例上显式调用 setWebApplicationType(WebApplicationType.NONE)
或设置 applicationContextClass
属性 (通过 Java API 或与外部属性) . 您可以将要作为业务逻辑运行的应用程序代码实现为 CommandLineRunner
并作为 @Bean
定义放到上下文中.
17.2. 属性和配置
本部分包括有关设置和读取属性,配置设置以及它们与 Spring Boot 应用程序的交互的主题.
17.2.1. 在构建时自动扩展属性
您可以使用现有的构建配置自动扩展它们,而不是对项目的构建配置中也指定的某些属性进行硬编码. 在 Maven 和 Gradle 中都是可能的.
使用 Maven 自动扩展属性
您可以使用资源过滤从 Maven 项目自动扩展属性. 如果使用 spring-boot-starter-parent
,则可以使用 @..@
占位符引用 Maven 的 ‘project properties’,如以下示例所示:
yaml
app:
encoding: "@project.build.sourceEncoding@"
java:
version: "@java.version@"
TIP
这样只会过滤生产配置 (也就是说,不会对进行过滤 src/test/resources
) .
TIP
如果启用该 addResources
标志,则 spring-boot:run
目标可以 src/main/resources
直接添加到类路径中 (用于热重载) . 这样做避免了资源过滤和此功能. 相反,您可以使用 exec:java
目标或自定义插件的配置. 有关更多详细信息,请参见 插件使用页面 .
如果您不使用入门级父级,则需要在 <build/>
元素中包括以下元素 pom.xml
:
xml
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
您还需要在其中包含以下元素 <plugins/>
:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
TIP
useDefaultDelimiters
如果在配置中使用标准的 Spring 占位符 (例如 ${placeholder}
) ,则 该属性很重要. 如果该属性未设置为 false
,则可以通过构建扩展它们.
使用 Gradle 自动扩展属性
您可以通过配置 Java 插件的 processResources
任务来自动扩展 Gradle 项目中的属性,如以下示例所示:
tasks.named('processResources') {
expand(project.properties)
}
然后,您可以使用占位符来引用 Gradle 项目的属性,如以下示例所示:
yaml
app:
name: "${name}"
description: "${description}"
TIP
Gradle 的 expand
方法使用 Groovy 的方法 SimpleTemplateEngine
来转换 ${..}
令牌. 该 ${..}
风格 与 Spring 自己的属性占位符机制冲突. 要将 Spring 属性占位符与自动扩展一起使用,请按以下步骤对 Spring 属性占位符进行转义: \${..}
.
17.2.2. 外部化配置 SpringApplication
SpringApplication
具有 bean 属性 (主要是 setter) ,因此在创建应用程序时可以使用其 Java API 修改其行为. 或者,您可以通过在中设置属性来外部化配置 spring.main.*
. 例如,在中 application.properties
,您可能具有以下设置:
yaml
spring:
main:
web-application-type: "none"
banner-mode: "off"
然后,启动时不会打印 Spring Boot 标语,并且应用程序也没有启动嵌入式 Web 服务器.
外部配置中定义的属性会覆盖用 Java API 指定的值,但用于创建 SpringApplication
的数据源除外. 考虑以下应用程序:
java
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
或者到 SpringApplicationBuilder
的 sources(…)
方法:
java
public class MyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder().bannerMode(Banner.Mode.OFF).sources(MyApplication.class).run(args);
}
}
给定上面的例子,如果我们有以下配置:
yaml
spring:
main:
sources: "com.example.MyDatabaseConfig,com.example.MyJmsConfig"
banner-mode: "console"
实际应用中,现在示出的 banner (如通过配置覆盖) ,并为 ApplicationContext
使用三个源(按以下顺序) :
MyApplication
(from the code)MyDatabaseConfig
(from the external config)MyJmsConfig
(from the external config)
17.2.3. 更改应用程序外部属性的位置
默认情况下,来自不同来源的属性将以定义的顺序添加到 Spring 的 Environment
中 (有关确切顺序,请参见 ‘Spring Boot 特性’ 部分中的 “外部化配置”) .
您还可以提供以下系统属性 (或环境变量) 来更改行为:
spring.config.name
(spring.config.name[format=envvar]
): 默认为application
作为文件名的根.spring.config.location
(spring.config.location[format=envvar]
): 要加载的文件 (例如类路径资源或 URL) .Environment
为此文档设置了单独的属性源,可以通过系统属性,环境变量或命令行来覆盖它.
无论您在环境中进行什么设置,Spring Boot 都将始终 application.properties
如上所述进行加载. 默认情况下,如果使用 YAML,则扩展名为 '.yml'
的文件也将添加到列表中.
提示:如果您想要有关正在加载的文件的详细信息,您可以 将 org.springframework.boot.context.config
的日志级别设置为 trace
.
17.2.4. 使用 ‘Short’ 命令行参数
有些人喜欢使用 (例如) --port=9000
而不是 --server.port=9000
在命令行上设置配置属性. 您可以通过使用占位符来启用此行为 application.properties
,如以下示例所示:
yaml
server:
port: "${port:8080}"
TIP
如果您从 spring-boot-starter-parent
POM 继承,则将的默认过滤器令牌 maven-resources-plugins
从更改 ${*}
为 @
(即,@maven.token@
而不是 ${maven.token}
) , 以防止与 Spring 样式的占位符冲突. 如果 application.properties
直接启用了 Maven 过滤,则可能还需要更改默认过滤器令牌以使用 其他定界符.
TIP
在这种特定情况下,端口绑定可在 PaaS 环境 (例如 Heroku 或 Cloud Foundry) 中工作. 在这两个平台中,PORT
环境变量是自动设置的,Spring 可以绑定到大写的 Environment
属性同义词.
17.2.5. 对外部属性使用 YAML
YAML 是 JSON 的超集,因此是一种方便的语法,用于以分层格式存储外部属性,如以下示例所示:
yaml
spring:
application:
name: "cruncher"
datasource:
driver-class-name: "com.mysql.jdbc.Driver"
url: "jdbc:mysql://localhost/test"
server:
port: 9000
创建一个名为 application.yml
的文件 ,并将其放在类路径的根目录中. 然后添加 snakeyaml
到您的依赖 (Maven 坐标 org.yaml:snakeyaml
,如果使用,则已经包含在内 spring-boot-starter
) . 将 YAML 文件解析为 Java Map<String,Object>
(如 JSON 对象) ,然后 Spring Boot 将 Map 压平,使其只具有一层,并使用句号作为分隔键,这是许多人习惯使用 Java 中的 Properties
文件的原因.
前面的示例 YAML 对应于以下 application.properties
文件:
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 特性” 部分中的 “使用 YAML”.
17.2.6. 设置 Active Spring Profiles
Spring Environment
为此提供了一个 API,但是您通常会设置一个 System
属性 (spring.profiles.active
) 或 OS 环境变量 (SPRING_PROFILES_ACTIVE
) . 另外,您可以使用 -D
参数启动应用程序 (请记住将其放在主类或 jar 存档之前) ,如下所示:
shell
$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
在 Spring Boot 中,您还可以设置激活的配置文件 application.properties
,如以下示例所示:
yaml
spring:
profiles:
active: "production"
以这种方式设置的值将由系统属性或环境变量代替,而不由 SpringApplicationBuilder.profiles()
方法替代. 因此,后一种 Java API 可用于扩充配置文件,而无需更改默认值.
有关更多信息,请参见 “Spring Boot 特性” 部分中的 “Profiles”
17.2.7. 设置默认的 Profile Name
default 配置文件是在没有配置文件处于活动状态时启用的配置文件。 默认情况下,默认配置文件的名称是 default
,但可以使用系统属性 (spring.profiles.default
) 或操作系统环境变量 (spring.profiles.default[format=envvar]
)。
在 Spring Boot 中,您还可以在 application.properties
中设置默认配置文件名称,如下例所示:
yaml
spring:
profiles:
default: "dev"
有关更多信息,请参阅 “Spring Boot features” 部分中的 ”部分中的“[Profiles](https://docs.jcohy.com/docs/spring-boot/2.6.11/htmlsingle/zh-cn/index.html#features.profiles)
”。 See 。
17.2.8. 根据环境更改配置
Spring Boot 支持多文档 YAML 和 Properties 文件(详细信息请参见 处理多文档文件), 可以根据激活的配置文件选择性的使用它们.
如果 YAML 文档包含 spring.config.activate.on-profile
配置,则将配置文件值 (以逗号分隔的配置文件列表或配置文件表达式) 输入到 Spring Environment.acceptsProfiles()
方法中. 如果配置文件表达式匹配, 则该文档将被包含在最终的合并中(否则, 则不包含) ,如以下示例所示:
yaml
server:
port: 9000
---
spring:
config:
activate:
on-profile: "development"
server:
port: 9001
---
spring:
config:
activate:
on-profile: "production"
server:
port: 0
在前面的示例中,默认端口为 9000. 但是,如果名为 ‘development’ 的 Spring profile 处于 active 状态,则端口为 9001. 如果 ‘production’ 为 active 状态,则该端口为 0.
TIP
文档按照它们遇到的顺序进行合并. 以后的值将覆盖以前的值.
17.2.9. 发现外部属性的内置选项
Spring Boot 在运行时将 application.properties
(或 .yml
文件和其他位置) 的外部属性绑定到应用程序中. 在一个位置上没有 (而且从技术上来说不是) 所有受支持属性的详尽列表, 因为他有可能来自类路径上的其他 jar 文件.
具有 Actuator 功能的正在运行的应用程序具有一个 configprops
端点,该端点显示了可通过访问的所有绑定和可绑定属性 @ConfigurationProperties
.
附录中包含一个 application.properties
示例,其中列出了 Spring Boot 支持的最常见属性. 最终列表来自搜索源代码中的 @ConfigurationProperties
和 @Value
注解,以及偶尔使用 Binder
. 有关加载属性的确切顺序的更多信息,请参见 ""外部化配置".
17.3. 嵌入式 Web 服务器
每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器. 此功能导致许多方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器. 本节回答这些问题.
17.3.1. 使用其他 Web 服务器
许多 Spring Boot starter 都包含默认的嵌入式容器.
对于 servlet 技术栈应用程序,通过
spring-boot-starter-web
包括来包括 Tomcatspring-boot-starter-tomcat
,但是您可以使用spring-boot-starter-jetty
或spring-boot-starter-undertow
代替.对于 reactive 技术栈的应用,
spring-boot-starter-webflux
包括响应式堆栈的 Netty 通过包括spring-boot-starter-reactor-netty
,但你可以使用spring-boot-starter-tomcat
,spring-boot-starter-jetty
或spring-boot-starter-undertow
代替.
切换到其他 HTTP 服务器时,您需要将默认依赖替换为所需的依赖. Spring Boot 为 HTTP 服务器提供了单独的 starter,以帮助简化此过程.
以下 Maven 示例显示了如何排除 Tomcat 并包括 Spring MVC 的 Jetty:
xml
<properties>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
TIP
Servlet API 的版本已被覆盖,因为与 Tomcat 9 和 Undertow 2 不同,Jetty 9.4 不支持 Servlet 4.0.
如果您希望使用支持 servlet 4.0 的 Jetty 10,您可以按照以下示例进行操作:
xml
<properties>
<jetty.version>10.0.8</jetty.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<exclusions>
<!-- Exclude the Jetty-9 specific dependencies -->
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
TIP
spring-boot-starter-reactor-netty
使用 WebClient
该类是必需的,因此即使您需要包括其他 HTTP 服务器,也可能需要保持对 Netty 的依赖.
请注意,除了排除 Tomcat 启动器之外,还需要排除一些特定于 Jetty9 的依赖项。
以下 Gradle 示例配置了必要的依赖项和 module replacement 以使用 Undertow 代替 Spring WebFlux 的 Reactor Netty:
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")
}
}
}
TIP
spring-boot-starter-reactor-netty
使用 WebClient
该类是必需的,因此即使您需要包括其他 HTTP 服务器,也可能需要保持对 Netty 的依赖.
17.3.2. 禁用 Web 服务器
如果您的类路径包含启动 Web 服务器所需的 bits,则 Spring Boot 将自动启动它. 要禁用此行为,请 WebApplicationType
在中配置 application.properties
,如以下示例所示:
yaml
spring:
main:
web-application-type: "none"
17.3.3. 更改 HTTP 端口
在独立应用程序中,主 HTTP 端口默认为 8080
但可以使用 server.port
(例如,在 application.properties
System 属性中或作为 System 属性) 进行设置. 由于轻松地绑定了 Environment
值,因此还可以使用 SERVER_PORT
(例如,作为 OS 环境变量) .
要完全关闭 HTTP 端点,但仍创建一个 WebApplicationContext
,请使用 server.port=-1
(这样做有时对测试很有用) .
有关更多详细信息,请参阅 ‘Spring Boot 特性’ 部分中的"[自定义内嵌 Servlet 容器](https://docs.jcohy.com/docs/spring-boot/2.6.11/htmlsingle/zh-cn/index.html#web.servlet.embedded-container.customizing)
" 或 ServerProperties
源代码.
17.3.4. 使用随机未分配的 HTTP 端口
要扫描可用端口 (使用 OS 本地来防止冲突) ,请使用 server.port=0
.
17.3.5. 在运行时发现 HTTP 端口
您可以从日志输出或 WebServerApplicationContext
通过其端口访问服务器正在运行的端口 WebServer
. 最好的方法是确保它已初始化,是添加一个 @Bean
类型 ApplicationListener<WebServerApplicationContext>
, 然后在发布事件时将其从事件中拉出.
使用的测试 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
还可以通过使用 @LocalServerPort
注解将实际端口注入字段中,如以下示例所示:
java
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@LocalServerPort
int port;
// ...
}
TIP
@LocalServerPort
是 @Value("${local.server.port}")
的元注解 . 不要尝试在常规应用程序中注入端口. 如我们所见,仅在初始化容器之后才设置该值. 与测试相反,应早处理应用程序代码回调 (在值实际可用之前) .
17.3.6. 启用 HTTP 响应压缩
Jetty,Tomcat 和 Undertow 支持 HTTP 响应压缩. 可以在中启用它 application.properties
,如下所示:
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
属性来配置此行为.
17.3.7. 配置 SSL
可以通过设置各种 server.ssl.*
属性来声明性地配置 SSL ,通常在 application.properties
或中 application.yml
. 以下示例显示了在中设置 SSL 属性 application.properties
:
yaml
server:
port: 8443
ssl:
key-store: "classpath:keystore.jks"
key-store-password: "secret"
key-password: "another-secret"
有关 Ssl
所有受支持属性的详细信息,请参见.
使用上述示例的配置意味着应用程序不再在端口 8080 上支持 HTTP 连接器. SpringBoot 不支持通过 application.properties
进行 HTTP 连接器和 HTTPS 连接器的配置 . 如果要同时拥有两者,则需要以编程方式配置其中之一. 我们建议您使用 application.properties
HTTPS 进行配置,因为 HTTP 连接器是两者中以编程方式进行配置的较容易方式.
17.3.8. 配置 HTTP/2
您可以使用 server.http2.enabled
配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持. 该支持取决于所选的 Web 服务器和应用程序环境,因为并非所有 JDK8 版本都支持该协议.
h2
(HTTP/2 over TLS)和 h2c
(HTTP/2 over TCP)都受支持。 要使用 h2
,还必须启用 SSL。 未启用 SSL 时,将使用 h2c
。例如,当您的应用程序是 running after a proxy server 执行 TLS 终止时,您可能希望使用 h2c
。
h2
支持的细节取决于所选的 Web 服务器和应用程序环境,因为并非所有 JDK 8 版本都具有支持开箱即用的协议。
Tomcat HTTP/2
默认情况下,Spring Boot 随 Tomcat 9.0.x 一起提供,他支持 h2c
,当使用 JDK 9 或更高版本时,Tomcat 9.0.x 支持 h2
. 另外,如果 libtcnative
库及其依赖已安装在主机操作系统上,则可以在 JDK 8 上使用 h2
.
如果没有,则必须使库目录可用于 JVM 库路径. 您可以使用 JVM 参数 (例如) 来执行此操作 -Djava.library.path=/usr/local/opt/tomcat-native/lib
. 有关更多信息,请参见 Tomcat 官方文档.
在没有该本地支持的情况下,在 JDK 8 上启动 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.1 SSL 支持开头.
Jetty HTTP/2
要支持 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 library
Reactor Netty HTTP/2
在 spring-boot-webflux-starter
默认情况下,Reactor Netty 作为服务器使用. Reactor Netty 支持使用 JDK 8 或更高版本的 h2c
, 无需配置其他依赖 使用 JDK 9 或更高版本的 JDK 支持,可以将 Reactor Netty 配置为 h2
. 对于 JDK 8 环境或最佳运行时性能,此服务器还支持带有本地库的 h2
.为此,您的应用程序需要具有其他依赖.
Spring Boot 管理 io.netty:netty-tcnative-boringssl-static
"uber jar" 的版本,其中包含所有平台的本地库. 开发人员可以选择使用分类器仅导入所需的依赖 (请参阅 Netty 官方文档) .
Undertow HTTP/2
从 Undertow 1.4.0+ 开始,在 JDK8 上毫无条件的支持 h2
和 h2c
.
17.3.9. 配置 Web 服务器
通常,您首先应该考虑使用许多可用的配置键之一,并通过在您的 application.properties
(或 application.yml
,或环境等) 中添加新条目来自定义 Web 服务器. 请参阅 “发现外部属性的内置选项”. 该 server.*
命名空间在这里是非常有用的,它包括命名空间一样 server.tomcat.*
, server.jetty.*
和其他对服务器的特定功能. 请参阅 公共应用程序属性 的列表.
前面的部分已经介绍了许多常见的用例,例如压缩,SSL 或 HTTP/2. 但是,如果您的用例不存在配置密钥,则应查看 WebServerFactoryCustomizer
. 您可以声明一个这样的组件,并访问与您选择的服务器相关的工厂: 您应该为所选服务器 (Tomcat,Jetty,Reactor Netty,Undertow) 和所选 Web 堆栈 (Servlet 或 Reactive) 选择对应的变体.
以下示例适用于带有 spring-boot-starter-web
(Servlet 技术栈) 的 Tomcat :
java
@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}
TIP
Spring Boot 在内部使用该机制来自动配置服务器. 自动配置的 WebServerFactoryCustomizer
Bean 的顺序为 0
,除非有明确说明的顺序,否则它将在任何用户定义的定制器之前进行处理.
一旦访问了 WebServerFactory
,就可以经常向其添加定制程序,以配置特定的部分,例如连接器,服务器资源或服务器本身-全部使用服务器特定的 API.
此外,Spring Boot 还提供:
Server | Servlet stack | Reactive stack |
---|---|---|
Tomcat | TomcatServletWebServerFactory | TomcatReactiveWebServerFactory |
Jetty | JettyServletWebServerFactory | JettyReactiveWebServerFactory |
Undertow | UndertowServletWebServerFactory | UndertowReactiveWebServerFactory |
Reactor | N/A | NettyReactiveWebServerFactory |
最后,您还可以声明自己的 WebServerFactory
组件,该组件将覆盖 Spring Boot 提供的组件.这样做时,自动配置的定制程序仍会应用您定制的工厂,因此请谨慎使用该选项
17.3.10. 将 Servlet, Filter, Listener 添加到应用程序
在一个 servlet 栈的应用,即用 spring-boot-starter-web
,有两种方法可以添加 Servlet
,Filter
,ServletContextListener
,和由 Servlet API 到您的应用程序支持的其他 listeners:
使用 Spring Bean 添加 Servlet, Filter, 或 Listener
要使用 Spring bean 添加 Servlet
,Filter
或 Servlet *Listener
,必须为其提供 @Bean
定义. 当您要注入配置或依赖时,这样做非常有用. 但是,您必须非常小心,以免引起过多其他 bean 的急切初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中. (例如,让它们依赖于您的 DataSource
或 JPA 配置不是一个好主意. ) 您可以通过在第一次使用 bean 时 (而不是在初始化时) 延迟初始化 bean 来解决这些限制.
对于过滤器和 Servlet,还可以通过添加 FilterRegistrationBean
或 ServletRegistrationBean
来代替基础组件或在基础组件之外添加映射和 init
参数.
TIP
如果在过滤器注册上未指定 dispatcherType
,则使用 REQUEST
. 这符合 Servlet 规范的默认调度程序类型.
像其他任何 Spring bean 一样,您可以定义 Servlet 过滤器 bean 的顺序. 请确保检查 “将 Servlet、Filter 和 Listener 注册为 Spring Beans” 部分.
禁用 Servlet 或 Filter 的注册
如前所述,任何 Servlet
或 Filter
Bean 都会自动向 Servlet 容器注册. 要禁用特定 Filter
或 Servlet
Bean 的注册,请为其创建注册 Bean 并将其标记为已禁用,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
@Bean
public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
}
使用类路径扫描添加 Servlets, Filters, 和 Listeners
通过使用 @ServletComponentScan
注解 @Configuration
类并指定包含要注册的组件的软件包,可以将 @WebServlet
, @WebFilter
, 和 @WebListener
注解的类自动注册到嵌入式 Servlet 容器中. 默认情况下,@ServletComponentScan
从带注解的类的包中进行扫描.
17.3.11. 配置访问日志
可以通过它们各自的命名空间为 Tomcat,Undertow 和 Jetty 配置访问日志.
例如,以下设置使用 自定义模式 记录对 Tomcat 的访问.
yaml
server:
tomcat:
basedir: "my-tomcat"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D ms)"
TIP
日志的默认位置是相对于 Tomcat 基本目录的日志目录. 默认情况下,logs
目录是一个临时目录,因此您可能需要修复 Tomcat 的基本目录或为日志使用绝对路径. 在前面的示例中,相对于应用程序的工作目录,日志位于 my-tomcat/logs
中.
可以用类似的方式配置 Undertow 的访问日志,如以下示例所示:
yaml
server:
undertow:
accesslog:
enabled: true
pattern: "%t %a %r %s (%D ms)"
options:
server:
record-request-start-time: true
请注意,除了启用访问日志记录和配置其模式外,还启用了记录请求开始时间。 在访问日志模式中包含响应时间 (%D
) 时,这是必需的。 日志存储在相对于应用程序工作目录的 logs
目录中. 您可以通过设置 server.undertow.accesslog.dir
属性来自定义此位置.
最后,Jetty 的访问日志也可以配置如下:
yaml
server:
jetty:
accesslog:
enabled: true
filename: "/var/log/jetty-access.log"
默认情况下,日志被重定向到 System.err
. 有关更多详细信息,请参见 Jetty 文档.
17.3.12. 在前端代理服务器后面运行
如果您的应用程序在代理,负载均衡器之后或在云中运行,则请求信息 (例如主机,端口,协议…) 可能会随之变化. 例如,您的应用程序可能正在 10.10.10.10:8080
上运行,但是 HTTP 客户端应该只能看到 example.org
.
RFC7239 "Forwarded Headers" 定义了 Forwarded
HTTP 请求头; 代理可以使用此请求头提供有关原始请求的信息. 您可以将应用程序配置为读取这些请求头,并在创建链接将其发送到 HTTP 302 响应,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. 您可以通过将 server.forward-headers-strategy
设置为 FRAMEWORK
来将其注册为 Servlet 过滤器.
TIP
如果您正在使用 Tomcat 并在代理处终止 SSL,则应将 server.tomcat.redirect-context-root
设置为 false
. 这允许在执行任何重定向之前遵守 X-Forwarded-Proto
头.
TIP
如果您的应用程序在 Cloud Foundry 或 Heroku 中运行,则 server.forward-headers-strategy
属性默认为 NATIVE
. 在所有其他情况下,它默认为 NONE
.
自定义 Tomcat 的代理配置
如果使用 Tomcat,则可以另外配置用于携带 “forwarded” 信息的 header 名称,如以下示例所示:
yaml
server:
tomcat:
remoteip:
remote-ip-header: "x-your-remote-ip-header"
protocol-header: "x-your-protocol-header"
Tomcat 还配置有一个默认正则表达式,该正则表达式与要信任的内部代理匹配. 有关其默认值,请参见附录中的 server.tomcat.remoteip.internal-proxies. 您可以通过在 application.properties
中添加一个 entry 来自定义配置,如以下示例所示:
yaml
server:
tomcat:
remoteip:
internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
TIP
您可以通过将 internal-proxies
设置为空来信任所有代理 (但在生产环境中不要这样做) .
您可以通过关闭自动功能来完全控制 Tomcat 的 RemoteIpValve
的配置 (为此,请设置 server.forward-headers-strategy=NONE
) ,然后在 WebServerFactoryCustomizer
bean 中添加新的 Valve 实例.
17.3.13. 使用 Tomcat 启用多个连接器
您可以将 org.apache.catalina.connector.Connector
添加到 TomcatServletWebServerFactory
,这可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> 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);
}
}
}
17.3.14. 使用 Tomcat 的 LegacyCookieProcessor
默认情况下,Spring Boot 使用的嵌入式 Tomcat 不支持 Cookie 格式的 "Version 0",因此您可能会看到以下错误:
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
如果有可能,您应该考虑将代码更新为仅存储符合以后 Cookie 规范的值. 但是,如果无法更改 cookie 的编写方式,则可以将 Tomcat 配置为使用 LegacyCookieProcessor
. 要切换到 LegacyCookieProcessor
,请使用添加了 TomcatContextCustomizer
的 WebServerFactoryCustomizer
bean,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyLegacyCookieProcessorConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
return (factory) -> factory.addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
}
}
17.3.15. 启用 Tomcat 的 MBean 注册表
默认情况下,嵌入式 Tomcat 的 MBean 注册表是禁用的. 这样可以最大程度地减少 Tomcat 的内存占用. 例如,如果要使用 Tomcat 的 MBean,以便可以通过 Micrometer 暴露它们,则必须使用 server.tomcat.mbeanregistry.enabled
属性,如以下示例所示:
yaml
server:
tomcat:
mbeanregistry:
enabled: true
17.3.16. 使用 Undertow 启用多个监听器
将 UndertowBuilderCustomizer
添加到 UndertowServletWebServerFactory
并将监听器添加到 Builder
,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyUndertowConfiguration {
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowListenerCustomizer() {
return (factory) -> factory.addBuilderCustomizers(this::addHttpListener);
}
private Builder addHttpListener(Builder builder) {
return builder.addHttpListener(8080, "0.0.0.0");
}
}
17.3.17. 使用 @ServerEndpoint 创建 WebSocket 端点
如果要在使用嵌入式容器的 Spring Boot 应用程序中使用 @ServerEndpoint
,则必须声明一个 ServerEndpointExporter
@Bean
,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
前面示例中显示的 Bean 将所有 @ServerEndpoint
注解的 Bean 注册到基础 WebSocket 容器. 当部署到独立的 servlet 容器时,此角色由 servlet 容器初始化程序执行,并且不需要 ServerEndpointExporter
Bean.
17.4. Spring MVC
Spring Boot 有许多启动器,其中包括 Spring MVC. 请注意,一些入门者包括对 Spring MVC 的依赖,而不是直接包含它. 本部分回答有关 Spring MVC 和 Spring Boot 的常见问题.
17.4.1. 编写 JSON REST 服务
只要 Jackson2 在类路径上, Spring Boot 应用程序中的任何 Spring @RestController
默认情况下都应呈现 JSON 响应,如以下示例所示:
java
@RestController
public class MyController {
@RequestMapping("/thing")
public MyThing thing() {
return new MyThing();
}
}
只要 Jackson 2 可以对 MyThing
进行序列化 (对于普通的 POJO 或 Groovy 对象为 true) ,则 [localhost:8080/thing](http://localhost:8080/thing)
默认情况下将以 JSON 表示. 请注意,在浏览器中,有时可能会看到 XML 响应,因为浏览器倾向于发送更喜欢 XML 的接受请求头.
17.4.2. 编写 XML REST 服务
如果类路径上具有 Jackson XML 扩展名 (jackson-dataformat-xml
) ,则可以使用它来呈现 XML 响应. 我们用于 JSON 的先前示例可以正常工作. 要使用 Jackson XML 渲染器,请将以下依赖添加到您的项目中:
xml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
如果 Jackson 的 XML 扩展名不可用而 JAXB 可用,则可以将 XML 呈现为附加要求,将 MyThing
注解为 @XmlRootElement
,如以下示例所示:
java
@XmlRootElement
public class MyThing {
private String name;
// getters/setters ...
}
JAXB 仅可与 Java 8 一起使用. 如果您使用的是较新的 Java 版本,请在项目中添加以下依赖:
xml
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
TIP
要使服务器呈现 XML 而不是 JSON,您可能必须发送一个 Accept: text/xml
请求头 (或使用浏览器) .
17.4.3. 自定义 Jackson ObjectMapper
Spring MVC (客户端和服务器端) 使用 HttpMessageConverters
在 HTTP 交换中协商内容转换. 如果 Jackson 在类路径中,则您已经获得了 Jackson2ObjectMapperBuilder
提供的默认转换器,该转换器的实例已为您自动配置.
ObjectMapper
(或用于 Jackson XML 转换器的 XmlMapper
) 实例 (默认创建) 具有以下自定义属性:
MapperFeature.DEFAULT_VIEW_INCLUSION
被禁用DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
被禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
被禁用
Spring Boot 还具有一些功能,可以更轻松地自定义此行为.
您可以通过使用环境来配置 ObjectMapper
和 XmlMapper
实例. Jackson 提供了一套广泛的简单的开/关功能,可用于配置其处理的各个方面. 在六个枚举 (在 Jackson 中) 中描述了这些功能,这些枚举映射到环境中的属性:
Enum | Property | Values |
---|---|---|
com.fasterxml.jackson.databind.DeserializationFeature | spring.jackson.deserialization.<feature_name> | true , false |
com.fasterxml.jackson.core.JsonGenerator.Feature | spring.jackson.generator.<feature_name> | true , false |
com.fasterxml.jackson.databind.MapperFeature | spring.jackson.mapper.<feature_name> | true , false |
com.fasterxml.jackson.core.JsonParser.Feature | spring.jackson.parser.<feature_name> | true , false |
com.fasterxml.jackson.databind.SerializationFeature | spring.jackson.serialization.<feature_name> | true , false |
com.fasterxml.jackson.annotation.JsonInclude.Include | spring.jackson.default-property-inclusion | always , non_null , non_absent , non_default , non_empty |
例如,要启用漂亮打印,请设置 spring.jackson.serialization.indent_output = true
. 请注意,由于使用了 宽松绑定,因此 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
的所有自动配置.
如果您提供任何类型为 MappingJackson2HttpMessageConverter
的 @Bean
,它们将替换 MVC 配置中的默认值. 此外,还提供了 HttpMessageConverters
类型的便捷 bean (如果使用默认的 MVC 配置,该 bean 始终可用) . 它提供了一些有用的方法来访问默认的和用户增强的消息转换器.
有关更多详细信息,请参见 “自定义 @ResponseBody 渲染” 部分和 WebMvcAutoConfiguration
源代码.
17.4.4. 自定义 @ResponseBody 渲染
Spring 使用 HttpMessageConverters
渲染 @ResponseBody
(或 @RestController
的响应) . 您可以通过在 Spring Boot 上下文中添加适当类型的 bean 来贡献额外的转换器. 如果您添加的 Bean 仍是默认情况下将包含的类型 (例如 JSON 转换的 MappingJackson2HttpMessageConverter
) ,它将替换默认值. 提供了 HttpMessageConverters
类型的便捷 bean,如果使用默认的 MVC 配置,它将始终可用. 它提供了一些有用的方法来访问默认的和用户增强的消息转换器 (例如,如果您想将它们手动注入到自定义的 RestTemplate
中,则可能会很有用) .
与正常的 MVC 用法一样,您提供的任何 WebMvcConfigurer
Bean 也可以通过重写 configureMessageConverters
方法来贡献转换器. 但是,与普通的 MVC 不同,您只能提供所需的其他转换器 (因为 Spring Boot 使用相同的机制来提供其默认值) . 最后,如果通过提供自己的 @EnableWebMvc
配置选择退出 Spring Boot 默认 MVC 配置,则可以完全控制并使用 WebMvcConfigurationSupport
中的 getMessageConverters
手动执行所有操作.
有关更多详细信息,请参见 WebMvcAutoConfiguration
源代码.
17.4.5. 处理分段文件上传
Spring Boot 包含 Servlet 3 javax.servlet.http.Part
API以支持上传文件. 默认情况下,Spring Boot 用单个请求将 Spring MVC 配置为每个文件最大大小为 1MB,最大文件数据为 10MB
. 您可以使用 MultipartProperties
类中暴露的属性覆盖这些值,存储中间数据的位置 (例如,存储到 /tmp
目录) 以及将数据刷新到磁盘的阈值. 例如,如果要指定文件不受限制,请将 spring.servlet.multipart.max-file-size
属性设置为 -1
.
当您想在 Spring MVC 控制器处理程序方法中以 MultipartFile
类型的 @RequestParam
注解参数接收多部分编码文件数据时,多部分支持会很有帮助.
有关更多详细信息,请参见 MultipartAutoConfiguration
源码.
TIP
建议使用容器的内置支持进行分段上传,而不要引入其他依赖,例如 Apache Commons File Upload.
17.4.6. 关闭 Spring MVC DispatcherServlet
默认情况下,所有内容均从应用程序 (/
) 的根目录提供. 如果您希望映射到其他路径,则可以如下配置:
yaml
spring:
mvc:
servlet:
path: "/mypath"
如果您有其他 Servlet,则可以为每个 Servlet
声明一个 @Bean
或 ServletRegistrationBean
类型,Spring Boot 会将它们透明地注册到容器中. 因为 servlet 是通过这种方式注册的,所以可以将它们映射到 DispatcherServlet
的子上下文,而无需调用它.
自己配置 DispatcherServlet
是不寻常的,但是如果您确实需要这样做,则还必须提供 DispatcherServletPath
类型的 @Bean
,以提供自定义 DispatcherServlet
的路径.
17.4.7. 关闭默认的 MVC 配置
完全控制 MVC 配置的最简单方法是为您自己的 @Configuration
提供 @EnableWebMvc
注解. 这样做会使您掌握所有 MVC 配置.
17.4.8. 自定义 ViewResolvers
ViewResolver
是 Spring MVC 的核心组件,将 @Controller
中的视图名称转换为实际的 View
实现. 请注意,ViewResolvers
主要用于 UI 应用程序,而不是 REST 样式的服务 (View 不用于呈现 @ResponseBody
) . 有很多 ViewResolver
实现可供选择,Spring 本身对是否应使用哪个视图没有意见. 另一方面,Spring Boot 根据在类路径和应用程序上下文中找到的内容为您安装一个或两个. DispatcherServlet
使用它在应用程序上下文中找到的所有解析器, 依次尝试每个解析器,直到获得结果为止. 如果添加自己的解析器,则必须知道其顺序以及解析器的添加位置.
WebMvcAutoConfiguration
将以下 ViewResolvers
添加到您的上下文中:
一个名为
defaultViewResolver
的InternalResourceViewResolver
. 这一章查找可以通过使用DefaultServlet
呈现的物理资源 (包括静态资源和 JSP 页面,如果使用的话) .它在视图名称中应用前缀和后缀,然后在 Servlet 上下文中查找具有该路径的物理资源 (默认值均为空,但可通过spring.mvc.view.prefix
和spring.mvc.view.suffix
进行外部配置访问) . 您可以通过提供相同类型的 bean 覆盖它.名为
beanNameViewResolver
的BeanNameViewResolver
. 这是视图解析器链的有用成员,可以获取与要解析的视图同名的所有 bean. 不必重写或替换它.仅当实际上存在
View
类型的bean时,才添加一个名为viewResolver
的ContentNegotiatingViewResolver
. 这是一个 ‘master’ 解析器,委派给所有其他解析器,并尝试查找与客户端发送的 ‘Accept’ HTTP 请求头匹配的内容. 您可能想学习有关ContentNegotiatingViewResolver
的有用博客 ,以了解更多信息,并且您也可以查看源代码以获取详细信息. 您可以通过定义一个名为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
在类路径中) ,则您还将有一个名为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
.
有关更多详细信息,请参见以下部分:
17.5. Jersey
17.5.1. 使用 Spring Security 保护 Jersey 端点
可以使用 Spring Security 来保护基于 Jersey 的 Web 应用程序,其方式与用来保护基于 Spring MVC 的 Web 应用程序的方式几乎相同. 但是,如果您想将 Spring Security 的方法级安全性与 Jersey 一起使用,则必须将 Jersey 配置为使用 setStatus(int)
而不是 sendError(int)
. 这可以防止 Jersey 在 Spring Security 有机会向客户端报告身份验证或授权失败之前提交响应.
必须在应用程序的 ResourceConfig
bean 上将 jersey.config.server.response.setStatusOverSendError
属性设置为 true
,如以下示例所示:
java
@Component
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {
public JerseySetStatusOverSendErrorConfig() {
register(Endpoint.class);
setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
}
}
17.5.2. 与另一个 Web 框架一起使用 Jersey
要将 Jersey 与其他 Web 框架 (例如 Spring MVC) 一起使用,应对其进行配置,以便它将允许其他框架处理无法处理的请求. 首先,通过将 spring.jersey.type
应用程序属性配置为 filter
值,将 Jersey 配置为使用 Filter 而不是 Servlet. 其次,配置您的 ResourceConfig
以转发可能导致 404 的请求,如以下示例所示.
java
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Endpoint.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
17.6. HTTP Clients
Spring Boot 提供了许多可与 HTTP Clients 一起使用的 starters. 本节回答与使用它们有关的问题.
17.6.1. 配置 RestTemplate 使用代理
如 自定义 RestTemplate 中所述,您可以将 RestTemplateCustomizer
与 RestTemplateBuilder
一起使用以构建自定义的 RestTemplate
. 建议使用此方法来创建配置为使用代理的 RestTemplate
.
代理配置的确切详细信息取决于所使用的基础客户端请求工厂.
17.6.2. 配置基于 Reactor Netty 的 WebClient 使用的 TcpClient
当 Reactor Netty 在类路径上时,将自动配置基于 Reactor Netty 的 WebClient
. 要自定义客户端对网络连接的处理,请提供一个 ClientHttpConnector
bean. 下面的示例配置 60 秒的连接超时并添加 ReadTimeoutHandler
:
java
@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);
}
}
TIP
注意将 ReactorResourceFactory
用于连接提供程序和事件循环资源. 这确保了用于服务器接收请求和客户端发出请求的资源的有效共享.
17.7. Logging
除了通常由 Spring Framework 的 spring-jcl 模块提供的 Commons Logging API 之外,Spring Boot 没有强制性的日志记录依赖性. 要使用 Logback,您需要在类路径中包括它和 spring-jcl
. 最简单的方法是通过启动程序,所有启动程序都依赖于 spring-boot-starter-logging
. 对于 Web 应用程序,只需要 spring-boot-starter-web
,因为它暂时依赖于日志记录启动器. 如果使用 Maven,则以下依赖会为您添加日志记录:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot 有一个 LoggingSystem
抽象,它试图根据类路径的内容来配置日志. 如果可以使用 Logback,则它是首选.
如果您需要对日志记录进行的唯一更改是设置各种记录器的级别,则可以使用 "logging.level" 前缀在 application.properties
中进行设置,如以下示例所示:
yaml
logging:
level:
org.springframework.web: "debug"
org.hibernate: "error"
您还可以使用 logging.file.name
来设置要写入日志的文件的位置 (除了控制台) .
要配置日志记录系统的更细粒度的设置,您需要使用所讨论的 LoggingSystem
支持的本地配置格式. 默认情况下,Spring Boot 从系统的默认位置 (例如,用于 Logback 的 classpath:logback.xml
) 拾取本地配置,但是您可以使用 logging.config
属性设置配置文件的位置.
17.7.1. 配置 Logback Logging
如果您需要对 logback 进行自定义,假如超出了 application.properties
可以实现的范围,则需要添加一个标准的 logback 配置文件. 您可以将 logback.xml
文件添加到类路径的根目录中,以进行 logback 查找. 如果要使用 Spring Boot Logback 扩展,也可以使用 logback-spring.xml
.
TIP
Logback 文档有一个特定的部分,其中 详细介绍了配置.
Spring Boot 提供了许多您自己的配置中 included
的 logback 配置. 这些包括旨在允许重新应用某些常见的 Spring Boot 约定.
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
文件如下所示:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>
您的日志配置文件也可以利用 LoggingSystem
为您创建的 System 属性:
${PID}
: The 当前进程 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
.
通过使用自定义的 Logback 转换器,Spring Boot 还可以在控制台上提供一些不错的 ANSI 颜色终端输出 (但不在日志文件中) . 有关示例,请参见 defaults.xml
配置中的 CONSOLE_LOG_PATTERN
.
如果 Groovy 在类路径中,则还应该能够使用 logback.groovy
配置 Logback. 如果存在,则优先考虑此设置.
TIP
Groovy 配置不支持 Spring 扩展. 不会检测到任何 logback-spring.groovy
文件.
配置仅文件输出的 Logback
如果要禁用控制台日志记录并将输出仅写入文件,则需要一个自定义的 logback-spring.xml
,该文件将导入 file-appender.xml
而不是 console-appender.xml
,如以下示例所示:
xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
您还需要将 logging.file.name
添加到 application.properties
或 application.yaml
中,如以下示例所示:
yaml
logging:
file:
name: "myapplication.log"
17.7.2. 配置 Log4j 日志
如果 Spring Boot 在类路径上,则它支持 Log4j 2 进行日志记录配置. 如果使用 starters 来组装依赖,则必须排除 Logback,然后改为包括 log4j 2. 如果您不使用 starters,则除了 Log4j 2 外,还需要 (至少) 提供 spring-jcl
.
推荐的方法可能是通过 starters 程序,即使它需要对排除对象进行一些调整. 以下示例显示了如何在 Maven 中设置 starters:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
Gradle 提供了几种不同的方法来设置 starters。 一种方法是使用 module replacement。 为此,请声明对 Log4j 2 starter 的依赖,并告诉 Gradle 任何出现的默认日志记录 starter 都应替换为 Log4j 2 starter,如以下示例所示:
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")
}
}
}
TIP
Log4j 入门人员将依赖集中在一起,以满足常见的日志记录要求 (例如让 Tomcat 使用 java.util.logging
,但使用 Log4j 2 配置输出) .
TIP
为了确保将使用 java.util.logging
执行的调试日志记录路由到 Log4j 2,请通过将 java.util.logging.manager
系统属性设置为 org.apache.logging.log4j.jul.LogManager
来配置其 JDK logging adapter.
使用 YAML 或 JSON 配置 Log4j 2
除了默认的 XML 配置格式外,Log4j 2 还支持 YAML 和 JSON 配置文件. 要将 Log4j 2 配置为使用备用配置文件格式,请将适当的依赖添加到类路径中,并命名您的配置文件以匹配您选择的文件格式,如以下示例所示:
Format | Dependencies | 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 |
使用复合配置来配置 Log4j 2
Log4j 2 支持将多个配置文件组合成一个复合配置。 要在 Spring Boot 中使用此支持,请配置一个或多个辅助配置文件的位置 logging.log4j2.config.override
。 辅助配置文件将与主配置合并,无论主配置的源是 Spring Boot 的默认值、标准位置(例如 log4j.xml
)还是 logging.config
属性配置的位置。
17.8. 数据访问
Spring Boot 包含许多用于处理数据源的 starters. 本节回答与这样做有关的问题.
17.8.1. 配置自定义数据源
要配置自己的 DataSource
,请在配置中定义该类型的 @Bean
. Spring Boot 重用您的 DataSource
到任何需要的地方,包括数据库初始化. 如果需要外部化某些设置,则可以将 DataSource
绑定到环境 (请参见 “第三方配置”) .
以下示例显示了如何在 Bean 中定义数据源:
java
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration { @Bean
@ConfigurationProperties(prefix = "app.datasource")
public SomeDataSource dataSource() { return new SomeDataSource(); }}
以下示例显示如何通过设置属性来定义数据源:
yaml
app:
datasource:
url: "jdbc:h2:mem:mydb"
username: "sa"
pool-size: 30
假设您的 SomeDataSource
具有 URL,用户名和连接池大小的常规 JavaBean 属性,则在将 DataSource
提供给其他组件之前,将自动绑定这些设置.
Spring Boot 还提供了一个名为 DataSourceBuilder
的实用工具生成器类,可用于创建标准数据源之一 (如果它位于类路径上) . 构建者可以根据类路径中可用的内容来检测要使用的内容. 它还基于 JDBC URL 自动检测驱动程序.
下面的示例演示如何使用 DataSourceBuilder
创建数据源:
java
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
要使用该 DataSource
运行应用程序,您需要的只是连接信息. 还可以提供特定于池的设置. 有关更多详细信息,请检查将在运行时使用的实现.
以下示例显示如何通过设置属性来定义 JDBC 数据源:
yaml
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
但是,有一个陷阱. 由于未暴露连接池的实际类型,因此在自定义 DataSource
的元数据中不会生成任何键,并且 IDE 中也无法完成操作 (因为 DataSource
接口未暴露任何属性) . 另外,如果您碰巧在类路径上有 Hikari,则此基本设置将不起作用,因为 Hikari 没有 url
属性 (但确实具有 jdbcUrl
属性) . 在这种情况下,您必须按照以下方式重写配置:
yaml
app:
datasource:
jdbc-url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
您可以通过强制连接池使用并返回专用的实现而不是 DataSource
来解决此问题. 您无法在运行时更改实现,但是选项列表将是明确的.
以下示例显示了如何使用 DataSourceBuilder
创建 HikariDataSource
:
java
@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 自动创建的 DataSource
. 但是,这会将您的配置分为两个命名空间: spring.datasource
上的 url
, username
, password
, type
和 driver
,其余部分放在您的自定义命名空间 (app.datasource
) 上. 为避免这种情况,可以在自定义命名空间上重新定义自定义 DataSourceProperties
,如以下示例所示:
java
@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
转换,因此可以按以下方式进行配置:
yaml
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
TIP
Spring Boot 会将针对 Hikari 的设置暴露给 spring.datasource.hikari
. 本示例使用更通用的 configuration
子命名空间,因为该示例不支持多个数据源实现.
TIP
由于您的自定义配置选择使用 Hikari,因此 app.datasource.type
无效. 实际上,构建器会使用您可以在其中设置的任何值进行初始化,然后由对 .type ()
的调用覆盖.
有关更多详细信息,请参见 "Spring Boot 特性" 部分中的 “配置数据源” 和 DataSourceAutoConfiguration
类.
17.8.2. 配置两个数据源
如果需要配置多个数据源,则可以应用上一节中介绍的相同技巧. 但是,您必须将其中一个 DataSource
实例标记为 @Primary
,因为将来各种自动配置都希望能够按类型获取一个.
如果您创建自己的数据源,则会取消自动配置. 在以下示例中,我们提供与自动配置在主数据源上提供的功能完全相同的功能集:
java
@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();
}
}
TIP
必须将 firstDataSourceProperties
标记为 @Primary
,以便数据库初始化程序功能使用您的副本 (如果使用初始化程序) .
这两个数据源也都必须进行高级定制. 例如,您可以按以下方式配置它们:
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
,如以下示例所示:
java
@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 Boot 在自动配置中使用的逻辑相同. 请注意,每个 configuration
子命名空间均基于所选实现提供高级设置.
17.8.3. 使用 Spring Data Repositories
Spring Data 可以创建各种风格的 @Repository
接口的实现. 只要那些 @Repositories
包含在 @EnableAutoConfiguration
类的同一包 (或子包) 中,Spring Boot 就会为您处理所有这些操作.
对于许多应用程序,您所需要做的就是在类路径上放置正确的 Spring Data 依赖. 有一个用于 JPA 的 spring-boot-starter-data-jpa
,一个用于Mongodb的 spring-boot-starter-data-mongodb
,等等. 首先,创建一些存储库接口来处理 @Entity
对象.
Spring Boot 会根据发现的 @EnableAutoConfiguration
尝试猜测 @Repository
定义的位置. 要获得更多控制权,请使用 @EnableJpaRepositories
注解 (来自Spring Data JPA).
有关 Spring Data 的更多信息,请参见 Spring Data 项目页面.
17.8.4. 将 @Entity
定义与 Spring 配置分开
Spring Boot 会根据发现的 @EnableAutoConfiguration
尝试猜测 @Entity
定义的位置. 要获得更多控制,可以使用 @EntityScan
注解,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {
// ...
}
17.8.5. 配置 JPA 属性
Spring Data JPA 已经提供了一些独立于供应商的配置选项 (例如用于 SQL 日志记录的那些) ,并且 Spring Boot 暴露了这些选项,还为 Hibernate 提供了更多选项作为外部配置属性. 其中的一些会根据上下文自动检测到,因此您不必进行设置.
spring.jpa.hibernate.ddl-auto
是一种特殊情况,因为根据运行时条件,它具有不同的默认值. 如果使用嵌入式数据库,并且没有模式管理器 (例如 Liquibase 或 Flyway) 正在处理 DataSource
,则默认为 create-drop
. 在所有其他情况下,它默认为 none
.
JPA 提供程序检测到要使用的方言. 如果您希望自己设置方言,请设置 spring.jpa.database-platform
属性.
下例显示了最常用的设置选项:
yaml
spring:
jpa:
hibernate:
naming:
physical-strategy: "com.example.MyPhysicalNamingStrategy"
show-sql: true
另外,创建本地 EntityManagerFactory
时,spring.jpa.properties.*
中的所有属性均作为普通 JPA 属性 (前缀被去除) 传递.
TIP
您需要确保在 spring.jpa.properties.*
下定义的名称与 JPA 提供程序期望的名称完全匹配. Spring Boot 不会尝试对这些条目进行任何形式的宽松绑定.
例如,如果要配置 Hibernate 的批处理大小,则必须使用 spring.jpa.properties.hibernate.jdbc.batch_size
. 如果您使用其他形式,例如 batchSize
或 batch-size
,则 Hibernate 将不会应用该设置.
TIP
如果您需要对 Hibernate 属性应用高级自定义,请考虑注册在创建 EntityManagerFactory
之前将被调用的 HibernatePropertiesCustomizer
bean. 这优先于自动配置应用的任何内容.
17.8.6. 配置 Hibernate 命名策略
Hibernate 使用 两种不同的命名策略 将名称从对象模型映射到相应的数据库名称. 可以分别通过设置 spring.jpa.hibernate.naming.physical-strategy
和 spring.jpa.hibernate.naming.implicit-strategy
属性来配置物理和隐式策略实现的标准类名. 另外,如果在应用程序上下文中可以使用 ImplicitNamingStrategy
或 PhysicalNamingStrategy
Bean,则 Hibernate 将自动配置为使用它们.
默认情况下,Spring Boot 使用 CamelCaseToUnderscoresNamingStrategy
配置物理命名策略. 此实现提供了与 Hibernate 4 相同的表结构: 所有点都由下划线替换,而骆驼套也由下划线替换. 默认情况下,所有表名均以小写形式生成. 例如,一个 TelephoneNumber
实体被映射到 telephone_number
表.如果您需要大小写混合的标识符,请定义一个自定义 CamelCaseToUnderscoresNamingStrategy
bean,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {
@Bean
public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
return new CamelCaseToUnderscoresNamingStrategy() {
@Override
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return false;
}
};
}
}
If you prefer to use Hibernate 5’s default instead, set the following property:
properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
或者,您可以配置以下 bean:
java
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
return new PhysicalNamingStrategyStandardImpl();
}
}
有关更多详细信息,请参见 HibernateJpaAutoConfiguration
和 JpaBaseConfiguration
.
17.8.7. 配置 Hibernate 二级缓存
可以为一系列缓存提供程序配置 Hibernate 二级缓存. 与其将 Hibernate 配置为再次查找缓存提供程序,不如提供尽可能在上下文中可用的缓存提供程序.
如果您使用的是 JCache,这非常简单. 首先,确保 org.hibernate:hibernate-jcache
在类路径上可用. 然后,添加一个 HibernatePropertiesCustomizer
bean,如以下示例所示:
java
@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {
@Bean
public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
}
}
这个定制器将配置 Hibernate 使用与应用程序相同的 CacheManager
. 也可以使用单独的 CacheManager
实例. 有关详细信息,请参阅 Hibernate 用户指南.
17.8.8. 在 Hibernate 组件中使用依赖注入
默认情况下,Spring Boot 注册一个使用 BeanFactory
的 BeanContainer
实现,以便转换器和实体监听器可以使用常规的依赖注入.
您可以通过注册删除或更改 hibernate.resource.beans.container
属性的 HibernatePropertiesCustomizer
来禁用或调整此行为.
17.8.9. 使用自定义 EntityManagerFactory
要完全控制 EntityManagerFactory
的配置,您需要添加一个名为 ‘entityManagerFactory’ 的 @Bean
. 如果存在这种类型的 Bean,Spring Boot 自动配置将关闭其实体管理器.
17.8.10. 使用多个 EntityManagerFactories
如果您需要针对多个数据源使用 JPA,则每个数据源可能需要一个 EntityManagerFactory
。 Spring ORM 中的 LocalContainerEntityManagerFactoryBean
允许您根据需要配置 EntityManagerFactory
。 您还可以重用 JpaProperties
来绑定每个 EntityManagerFactory
的设置,如下例所示:
java
@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
的 DataSource
bean 创建了一个 EntityManagerFactory
。 它扫描与 Order
位于同一包中的实体。 可以使用 app.first.jpa
命名空间映射其他 JPA 属性。
TIP
当您自己为 LocalContainerEntityManagerFactoryBean
创建 bean 时,在自动配置的 LocalContainerEntityManagerFactoryBean
创建期间应用的所有自定义设置都将丢失. 例如,对于 Hibernate,spring.jpa.hibernate
前缀下的任何属性都不会自动应用于您的 LocalContainerEntityManagerManagerBean
. 如果您依靠这些属性来配置诸如命名策略或 DDL 模式之类的东西,那么在创建 LocalContainerEntityManagerManagerBean
bean 时将需要显式配置.
您应该为需要 JPA 访问权限的任何其他数据源提供类似的配置。 为了完成此目标,您还需要为每个 EntityManagerFactory
配置一个 JpaTransactionManager
。 或者,您可以使用跨越两者的 JTA 事务管理器。
如果使用 Spring Data,则需要相应地配置 @EnableJpaRepositories
,如下例所示:
java
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {}
java
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {}
17.8.11. 使用传统的 persistence.xml 文件
默认情况下,Spring Boot 不会搜索或使用 META-INF/persistence.xml
. 如果您更喜欢使用传统的 persistence.xml
,则需要定义自己的 LocalBeanManagerFactoryBean
类型的 @Bean
(ID为 ‘entityManagerFactory’) ,并在其中设置持久性单元名称.
有关默认设置,请参见 JpaBaseConfiguration
.
17.8.12. 使用 Spring Data JPA 和 Mongo 存储库
Spring Data JPA 和 Spring Data Mongo 都可以为您自动创建 Repository
实现. 如果它们都存在于类路径中,则可能必须做一些额外的配置以告诉 Spring Boot 要创建哪个存储库. 最明确的方法是使用标准 Spring Data @EnableJpaRepositories`
和 @EnableMongoRepositories
注解并提供 Repository
接口的位置.
还有一些标记 (spring.data.*.repositories.enabled
和 spring.data.*.repositories.type
) 可用于在外部配置中打开和关闭自动配置的存储库. 这样做很有用,例如,如果您想关闭 Mongo 存储库并仍然使用自动配置的 MongoTemplate
.
对于其他自动配置的 Spring Data 存储库类型 (Elasticsearch,Solr等) ,存在相同的障碍和相同的功能. 要使用它们,请相应地更改注解和标志的名称.
17.8.13. 定制 Spring Data 的 Web 支持
Spring Data 提供了 Web 支持,简化了 Web 应用程序中 Spring Data 存储库的使用. Spring Boot 在 spring.data.web
命名空间中提供属性以自定义其配置. 请注意,如果您使用的是 Spring Data REST,则必须改为使用 spring.data.rest
命名空间中的属性.
17.8.14. 将 Spring Data Repositories 暴露为 REST 端点
如果已为应用程序启用了 Spring MVC,则 Spring Data REST 可以为您将 Repository
实现作为 REST 端点暴露.
Spring Boot 暴露了一组有用的属性 (来自 spring.data.rest
命名空间) ,这些属性来自定义 RepositoryRestConfiguration
. 如果需要提供其他定制,则应使用 RepositoryRestConfigurer
bean.
TIP
如果您未在自定义 RepositoryRestConfigurer
上指定任何顺序,则该顺序在一个 Spring Boot 内部使用后运行. 如果您需要指定订单,请确保该订单大于 0.
17.8.15. 配置 JPA 使用的组件
如果要配置 JPA 使用的组件,则需要确保在 JPA 之前初始化该组件. 当组件被自动配置后,Spring Boot 会为您处理. 例如,当自动配置 Flyway 时,会将 Hibernate 配置为依赖 Flyway,以便 Flyway 有机会在 Hibernate 尝试使用数据库之前对其进行初始化.
如果您自己配置组件,则可以使用 EntityManagerFactoryDependsOnPostProcessor
子类作为设置必要依赖的便捷方法. 例如,如果您将 Hibernate Search 和 Elasticsearch 用作其索引管理器,则必须将任何 EntityManagerFactory
Bean 配置为依赖于 elasticsearchClient
Bean,如以下示例所示:
java
/** * {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
* {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
*/
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
super("elasticsearchClient");
}
}
17.8.16. 使用两个数据源配置 jOOQ
如果需要将 jOOQ 与多个数据源一起使用,则应该为每个数据源创建自己的 DSLContext
. 有关更多详细信息,请参阅 JooqAutoConfiguration .
TIP
特别是,可以重用 JooqExceptionTranslator
和 SpringTransactionProvider
以提供与自动配置对单个 DataSource
所做的功能相似的功能.
17.9. 初始化 Database
可以使用不同的方式初始化 SQL 数据库,具体取决于堆栈是什么. 当然,如果数据库是一个单独的过程,您也可以手动执行. 建议使用单一机制进行模式生成.
17.9.1. 使用 JPA 初始化数据库
JPA 具有用于 DDL 生成的功能,可以将其设置为在启动时针对数据库运行. 这是通过两个外部属性控制的:
spring.jpa.hibernate.ddl-auto (枚举)
spring.jpa.generate-ddl
(boolean) 开启和关闭该功能,并且与供应商无关.spring.jpa.hibernate.ddl-auto
(enum)是一种 Hibernate 功能,可以更精细地控制行为. 此功能将在本指南的后面部分详细介绍.
17.9.2. 使用 Hibernate 初始化数据库
您可以显式设置 spring.jpa.hibernate.ddl-auto
,并且标准的 Hibernate 属性值为none
, validate
, update
, create
, and create-drop
. Spring Boot 根据是否认为您的数据库已嵌入为您选择默认值. 如果未检测到模式管理器, 则默认为 create-drop
,在所有其他情况下默认为 none
. 通过查看 Connection
类型和 JDBC url 可以检测到嵌入式数据库. hsqldb
,h2
和 derby
是候选的,其他则不是. 从内存数据库转换为 "真实" 数据库时,请务必不要假设新平台中表和数据的存在. 您必须显式设置 ddl-auto
或使用其他机制之一来初始化数据库.
TIP
您可以通过启用 org.hibernate.SQL
记录器来输出模式创建. 如果启用 调试模式,此操作将自动为您完成.
另外,如果 Hibernate 从头开始创建架构 (即,如果 ddl-auto
属性设置为 create
或 create-drop
) ,则在启动时会在类路径的根目录中执行一个名为 import.sql
的文件. 如果您小心的话,这对于演示和测试很有用,但可能不希望出现在生产环境的类路径中. 这是一个 Hibernate 功能 (与 Spring 无关) .
17.9.3. 使用 SQL 脚本初始化数据库
Spring Boot 可以自动创建数据源的架构 (DDL脚本) 并对其进行初始化 (DML脚本) . 它从标准根类路径位置 (分别为 schema.sql
和 data.sql
) 加载SQL. 另外,Spring Boot 处理 schema-${platform}.sql
和 data-${platform}.sql
文件 (如果存在) ,其中 platform
是 spring.datasource.platform
的值. 这使您可以在必要时切换到特定于数据库的脚本. 例如,您可以选择将其设置为数据库的供应商名称 (hsqldb
,h2
,oracle
,mysql
,postgresql
等) .
默认情况下,SQL 数据库初始化仅在使用嵌入式内存数据库时执行。 要始终初始化 SQL 数据库,无论其类型如何,请将 spring.sql.init.mode
设置为 always
。 同样,要禁用初始化,请将 spring.sql.init.mode
设置为 never
。 默认情况下,Spring Boot 启用其基于脚本的数据库初始化程序的快速失败特性。 这意味着,如果脚本导致异常,应用程序将无法启动。 您可以通过设置 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
来填充它。
如果您使用的是 Higher-level Database Migration Tool,如 Flyway 或 Liquibase,则应单独使用它们来创建和初始化架构。 不建议在 Flyway 或 Liquibase 旁边使用基本的 schema.sql
和 data.sql
脚本,并且将在未来的版本中删除支持。
17.9.4. 初始化一个 Spring Batch 数据库
如果您使用 Spring Batch,则它随大多数流行的数据库平台一起预包装了 SQL 初始化脚本. Spring Boot 可以检测您的数据库类型并在启动时执行这些脚本. 如果您使用嵌入式数据库,则默认情况下会发生这种情况. 您还可以为任何数据库类型启用它,如以下示例所示:
yaml
spring:
batch:
jdbc:
initialize-schema: "always"
您还可以通过设置 spring.batch.initialize-schema
为 never
显式关闭初始化.
17.9.5. 使用高级数据库迁移工具
Spring Boot 支持两个更高级别的迁移工具: Flyway 和 Liquibase.
在启动时执行 Flyway 数据库迁移
要在启动时自动运行 Flyway 数据库迁移,请将 org.flywaydb:flyway-core
添加到您的类路径中.
通常,迁移是格式为 V<VERSION>__<NAME>.sql
(带有 <VERSION>
下划线分隔的版本,例如 ‘1’ 或 ‘2_1’) 的脚本. 默认情况下,它们位于名为 classpath:db/migration
的目录中, 但是您可以通过设置 spring.flyway.locations
来修改该位置. 这是一个或多个 classpath:
或 filesystem:
位置的逗号分隔列表. 例如,以下配置将在默认类路径位置和 /opt/migration
目录中搜索脚本:
yaml
spring:
flyway:
locations: "classpath:db/migration,filesystem:/opt/migration"
您还可以添加特殊的 {vendor}
占位符以使用特定于供应商的脚本. 假设以下内容:
yaml
spring:
flyway:
locations: "classpath:db/migration/{vendor}"
前面的配置没有使用 db/migration
,而是根据数据库的类型 (例如MySQL的 db/migration/mysql
) 来设置要使用的目录. 受支持的数据库列表在 DatabaseDriver
中可用.
迁移也可以用 Java 编写. 将使用实现 JavaMigration
的任何 bean 自动配置 Flyway.
FlywayProperties
提供了 Flyway 的大多数设置以及少量的其他属性,这些属性可用于禁用迁移或关闭位置检查. 如果您需要对配置进行更多控制,请考虑注册 FlywayConfigurationCustomizer
bean.
Spring Boot 调用 Flyway.migrate()
来执行数据库迁移. 如果您想要更多控制,请提供一个实现 FlywayMigrationStrategy
的 @Bean
.
Flyway 支持 SQL 和 Java callbacks. 要使用基于 SQL 的回调,请将回调脚本放置在 classpath:db/migration
目录中. 要使用基于 Java 的回调,请创建一个或多个实现 Callback 的 bean. 任何此类 bean 都会自动在 Flyway 中注册. 可以使用 @Order
或通过实现 Ordered
来排序它们. 也可以检测到实现了不推荐使用的 FlywayCallback
接口的 Bean,但是不能与 Callback Bean 一起使用.
默认情况下,Flyway 在您的上下文中自动装配 (@Primary
) 数据源,并将其用于迁移. 如果您想使用其他数据源,则可以创建一个并将其 @Bean
标记为 @FlywayDataSource
. 如果这样做并想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary
. 另外,您可以通过在外部属性中设置 spring.flyway.[url,user,password]
来使用 Flyway 的本地数据源. 设置 spring.flyway.url
或 spring.flyway.user
足以使 Flyway 使用其自己的 DataSource
. 如果未设置这三个属性中的任何一个,则将使用其等效的 spring.datasource
属性的值.
您还可以使用 Flyway 为特定情况提供数据. 例如,您可以将特定于测试的迁移放置在 src/test/resources
中,并且仅在您的应用程序开始进行测试时才运行它们. 另外,您可以使用特定于配置文件的配置来自定义 spring.flyway.locations
,以便仅在特定配置文件处于 active 状态时才运行某些迁移. 例如,在 application-dev.properties
中,您可以指定以下设置:
yaml
spring:
flyway:
locations: "classpath:/db/migration,classpath:/dev/db/migration"
通过该设置,仅在 dev
环境下才运行 dev/db/migration
中的迁移.
在启动时执行 Liquibase 数据库迁移
要在启动时自动运行 Liquibase 数据库迁移,请将 org.liquibase:liquibase-core
添加到您的类路径中.
TIP
当您将 org.liquibase:liquibase-core
添加到类路径时,默认情况下,在应用程序启动期间和测试运行之前都会运行数据库迁移。 这种行为可以通过使用 spring.liquibase.enabled
属性来定制,在 main
和 test
配置中设置不同的值。 不可能使用两种不同的方式来初始化数据库(例如,用于应用程序启动的 Liquibase,用于测试运行的 JPA)。
默认情况下,从 db/changelog/db.changelog-master.yaml
中读取主更改日志,但是您可以通过设置 spring.liquibase.change-log
来更改位置. 除了 YAML,Liquibase 还支持 JSON,XML 和 SQL 更改日志格式.
默认情况下,Liquibase 在您的上下文中自动装配 (@Primary
) 数据源,并将其用于迁移. 如果需要使用其他数据源,则可以创建一个并将其 @Bean
标记为 @LiquibaseDataSource
. 如果这样做,并且想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary
. 另外,您可以通过在外部属性中设置 spring.liquibase.[driver-class-name,url,user,password]
来使用Liquibase 的本地数据源. 设置 spring.liquibase.url
或 spring.liquibase.user
足以使 Liquibase 使用其自己的数据源. 如果未设置这三个属性中的任何一个,则将使用其等效的 spring.datasource
属性的值.
有关可用设置 (例如上下文,默认架构等) 的详细信息,请参见 LiquibaseProperties
.
17.9.6. 依赖于一个初始化的数据库
作为应用程序上下文刷新的一部分,在应用程序启动时执行数据库初始化。 为了允许在启动期间访问已初始化的数据库,会自动检测充当数据库初始化程序的 bean 和需要已初始化该数据库的 bean。 初始化依赖于已初始化数据库的 Bean 被配置为依赖于那些初始化它的 Bean。 如果在启动期间,您的应用程序尝试访问数据库但尚未初始化,您可以配置对初始化数据库并要求数据库已初始化的 bean 的额外检测。
检测数据库初始化程序
Spring Boot 将自动检测以下类型的初始化 SQL 数据库的 bean:
DataSourceScriptDatabaseInitializer
EntityManagerFactory
Flyway
FlywayMigrationInitializer
R2dbcScriptDatabaseInitializer
SpringLiquibase
如果您为数据库初始化库使用第三方启动器,它可能会提供一个检测器,以便也自动检测其他类型的 bean。 要检测其他 bean,请在 META-INF/spring.factories
中注册 DatabaseInitializerDetector
的实现。
检测依赖于数据库初始化的 Bean
Spring Boot 将根据数据库初始化自动检测以下类型的 bean:
AbstractEntityManagerFactoryBean
(unless configprop:spring.jpa.defer-datasource-initialization[] is set totrue
)DSLContext
(jOOQ)EntityManagerFactory
(unless configprop:spring.jpa.defer-datasource-initialization[] is set totrue
)JdbcOperations
NamedParameterJdbcOperations
如果您使用的是第三方 starter 数据访问库,它可能会提供一个检测器,以便也自动检测其他类型的 bean。 要检测其他 bean,请在 META-INF/spring.factories
中注册 DependsOnDatabaseInitializationDetector
的实现。 或者,使用 @DependsOnDatabaseInitialization
注解 bean 的类或其 @Bean
方法。
17.10. 消息
Spring Boot 提供了许多包含消息传递的 starters. 本部分回答了将消息与 Spring Boot 一起使用所引起的问题.
17.10.1. 禁用事务 JMS 会话
如果您的 JMS 代理不支持事务处理会话,则必须完全禁用对事务的支持. 如果创建自己的 JmsListenerContainerFactory
,则无需执行任何操作,因为默认情况下无法进行处理. 如果您想使用 DefaultJmsListenerContainerFactoryConfigurer
重用 Spring Boot 的默认设置,则可以按以下方式禁用事务会话:
java
@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;
}
}
前面的示例将覆盖默认工厂,并且应将其应用于应用程序定义的任何其他工厂 (如果有) .
17.11. Batch Applications
当人们从 Spring Boot 应用程序中使用 Spring Batch 时,经常会出现许多问题. 本节解决这些问题.
17.11.1. 指定批处理数据源
默认情况下,批处理应用程序需要一个 DataSource
来存储作业详细信息. 批处理在您的上下文中自动装配单个数据源,并将其用于处理. 要使批处理使用应用程序的主 DataSource
以外的其他 DataSource
,请声明一个 DataSource
bean,并用 @BatchDataSource
注解其 @Bean
方法. 如果这样做并想要两个数据源,请记住创建另一个数据源并将其标记为 @Primary
. 为了更好地控制,请实现 BatchConfigurer
. 有关更多详细信息,请参见 The Javadoc of @EnableBatchProcessing
的Javadoc.
有关 Spring Batch 的更多信息,请参见 Spring Batch 项目页面.
17.11.2. 在启动时执行 Spring Batch 作业
通过在 @Configuration
类的某个位置添加 @EnableBatchProcessing
,可以启用 Spring Batch 自动配置.
默认情况下,它将在启动时在应用程序上下文中执行所有作业 (有关详细信息,请参见 JobLauncherApplicationRunner) . 您可以通过指定 spring.batch.job.names
(采用作业名称模式的逗号分隔列表) 来缩小到一个或多个特定作业.
{spring-boot-autoconfigure-module-code}/batch/BatchAutoConfiguration.java[BatchAutoConfiguration] 和 {spring-batch-api}/core/configuration/annotation/EnableBatchProcessing.html[@EnableBatchProcessing] 获取更多信息.
17.11.3. 在命令行上运行
Spring Boot 将任何以 --
开头的命令行参数转换为一个属性以添加到 Environment
中,请参见 访问命令行属性. 不应该将其作为参数传递给批处理作业. 要在命令行上指定批处理参数,请使用常规格式 (即不使用 --
) ,如以下示例所示:
shell
$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue
如果在命令行上指定 Environment
的属性,它将被作业忽略. 考虑以下命令:
shell
$ java -jar myapp.jar --server.port=7070 someParameter=someValue
这仅为批处理作业提供了一个参数: someParameter=someValue
17.11.4. 存储 Job Repository
Spring Batch 需要一个用于 Job 仓库的数据存储. 如果使用 Spring Boot,则必须使用实际的数据库. 请注意,它可以是内存数据库,请参见 docs.spring.io/spring-batch/docs/4.3.x/reference/html/ job.html#configuringJobRepository[配置作业存储库].
17.12. Actuator
Spring Boot 包括 Spring Boot Actuator. 本节回答了经常因使用而引起的问题.
17.12.1. Actuator 端点更改 HTTP 端口或地址
在独立应用程序中,Actuator HTTP 端口默认与主 HTTP 端口相同. 要使应用程序在其他端口上进行监听,请设置外部属性: management.server.port
. 要监听完全不同的网络地址 (例如,当您拥有用于管理的内部网络和用于用户应用程序的外部网络) 时,还可以将 management.server.address
设置为服务器能够绑定到的有效IP地址.
有关更多详细信息,请参阅 生产就绪功能 部分中的 ManagementServerProperties
源代码和 “自定义 Management 服务器端口” .
17.12.2. 自定义 ‘whitelabel’ 错误页面
如果遇到服务器错误,Spring Boot 会安装一个 ‘whitelabel’ 错误页面,您会在浏览器客户端中看到该错误页面 (使用 JSON 和其他媒体类型的机器客户端应该看到带有正确错误代码的明智响应) .
TIP
设置 server.error.whitelabel.enabled=false
可以关闭默认错误页面. 这样做将还原您正在使用的 servlet 容器的默认值. 请注意,Spring Boot 仍然尝试解决错误视图,因此您应该添加自己的错误页面,而不是完全禁用它.
用自己的方法覆盖错误页面取决于您使用的模板技术. 例如,如果您使用 Thymeleaf,则可以添加 error.html
模板. 如果使用 FreeMarker,则可以添加 error.ftlh
模板. 通常,您需要使用错误名称解析的 View 或处理 /error
路径的 @Controller
. 除非您替换了某些默认配置,否则您应该在 ApplicationContext
中找到一个 BeanNameViewResolver
,因此,以 @Bean
命名的错误将是一种简单的方法. 有关更多选项,请参见 ErrorMvcAutoConfiguration
.
有关如何在 Servlet 容器中注册处理程序的详细信息,另请参见 “错误处理” 部分.
17.12.3. Sanitize Sensitive Values
env
和 configprops
端点返回的信息可能有些敏感,因此默认情况下会清理匹配特定模式的键 (即,它们的值将替换为 ******
) .
可以分别使用 management.endpoint.env.keys-to-sanitize
和 management.endpoint.configprops.keys-to-sanitize
来定制要使用的模式.
Spring Boot 对此类密钥使用明智的默认设置: 例如,对任何以单词 "password", "secret", "key", "token", "vcap_services", "sun.java.command" 结尾的密钥进行清理. 也可以改用正则表达式,例如 *credentials.*
来清理任何将单词 credentials
作为密钥一部分保存的密钥.
此外,Spring Boot 对具有以下结尾之一的类似 URI 的值的敏感部分进行清理:
address
addresses
uri
uris
url
urls
URI 的敏感部分使用 <scheme>://<username>:<password>@<host>:<port>/
格式标识。 例如,对于属性 myclient.uri=http://user1:password1@localhost:8081
,经过清理的结果值为 http://user1:******@localhost:8081
。
自定义 Sanitization
Sanitization 可以通过两种不同的方式进行定制。
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
配置其他模式。
要对清理进行更多控制,请定义一个 SanitizingFunction
bean。 调用该函数的 SanitizableData
提供了对键和值以及它们来自的 PropertySource
的访问。 例如,这允许您清理来自特定属性源的每个值。 每个 SanitizingFunction
按顺序调用,直到函数更改可清理数据的值。 如果没有函数更改其值,则执行内置的基于 key 的清理。
17.12.4. 映射健康状态
Spring Boot 通过 Status
类型响应式系统运行状况. 如果要监视或警告特定应用程序的运行状况,可以通过 Micrometer 暴露这些状态. 默认情况下,Spring Boot 使用状态代码 “UP”, “DOWN”, “OUT_OF_SERVICE” 和 “UNKNOWN”. 要暴露这些状态,您需要将这些状态转换为一组数字,以便它们可以与 Micrometer Gauge
一起使用.
以下示例显示了一种方法:
java
@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;
}
}
17.13. 安全
本部分解决有关使用 Spring Boot 时的安全性的问题,包括因将 Spring Security 与 Spring Boot 一起使用而引起的问题.
有关 Spring Security 的更多信息,请参见 Spring Security 项目页面.
17.13.1. 关闭 Spring Boot 安全性配置
如果您在应用程序中使用 WebSecurityConfigurerAdapter
或 SecurityFilterChain
定义一个 @Configuration
Bean,它将关闭 Spring Boot 中的默认 Webapp 安全设置.
17.13.2. 更改 UserDetailsService 并添加用户帐户
如果提供类型为 AuthenticationManager
,AuthenticationProvider
或 UserDetailsService
的 @Bean
,则不会为 InMemoryUserDetailsManager
创建默认的 @Bean
. 这意味着您拥有完整的 Spring Security 功能集 (例如 各种身份验证选项) .
添加用户帐户的最简单方法是提供自己的 UserDetailsService
bean.
17.13.3. 在代理服务器后运行时启用 HTTPS
对于所有应用程序而言,确保所有主要端点仅可通过 HTTPS 进行访问都是一项重要的工作. 如果您将 Tomcat 用作 Servlet 容器,则 Spring Boot 如果检测到某些环境设置,则会自动添加 Tomcat 自己的 RemoteIpValve
, 并且您应该能够依靠 HttpServletRequest
来报告它是否安全 (甚至在代理服务器的下游) 处理真实的 SSL 终止) . 标准行为由某些请求头 (x-forwarded-for
和 x-forwarded-proto
) 的存在或不存在决定,它们的名称是常规名称, 因此它应可与大多数前端代理一起使用. 您可以通过将一些条目添加到 application.properties
来打开,如以下示例所示:
yaml
server:
tomcat:
remoteip:
remote-ip-header: "x-forwarded-for"
protocol-header: "x-forwarded-proto"
(这些属性中的任何一个都会在阀上切换. 或者,您可以通过添加 TomcatServletWebServerFactory
bean 来添加 RemoteIpValve
. )
要将 Spring Security 配置为对所有 (或某些) 请求都需要安全通道,请考虑添加自己的 SecurityFilterChain
,其中添加了以下 HttpSecurity
配置:
java
@Configuration
public class MySecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Customize the application security ...
http.requiresChannel((channel) -> channel.anyRequest().requiresSecure());
return http.build();
}
}
17.14. Hot Swapping(热交换)
Spring Boot 支持热插拔. 本部分回答有关其工作方式的问题.
17.14.1. 重新加载静态内容
有几种热加载选项. 推荐的方法是使用 spring-boot-devtools
,因为它提供了其他开发时功能,例如对应用程序快速重启和LiveReload 的支持以及合理的开发时配置 (例如模板缓存) . Devtools通过监视类路径的更改来工作. 这意味着必须 "构建" 静态资源更改才能使更改生效. 默认情况下,当您保存更改时,这在 Eclipse 中自动发生. 在 IntelliJ IDEA中,"生成项目" 命令将触发必要的构建. 由于默认的重新启动排除项, 对静态资源的更改不会触发应用程序的重新启动. 但是,它们确实会触发实时重新加载.
另外,在 IDE 中运行 (特别是在调试时打开) 是进行开发的好方法 (所有现代 IDE 都允许重新加载静态资源,并且通常还允许热交换 Java 类更改) .
最后,可以配置 Maven 和 Gradle 插件 (请参见 addResources
属性) 以支持从命令行运行,并直接从源代码重新加载静态文件. 如果要使用高级工具编写该代码,则可以将其与外部 css/js 编译器进程一起使用.
17.14.2. 重新加载模板,而无需重新启动容器
Spring Boot 支持的大多数模板技术都包含用于禁用缓存的配置选项 (在本文档的后面介绍) . 如果您使用 spring-boot-devtools
模块,那么在开发时会 自动配置 这些属性.
Thymeleaf 模板
If you use Thymeleaf, set spring.thymeleaf.cache
to false
. See ThymeleafAutoConfiguration
for other Thymeleaf customization options.
FreeMarker 模板
如果使用 FreeMarker,请将 spring.freemarker.cache
设置为 false
. 有关其他 FreeMarker 定制选项,请参见 FreeMarkerAutoConfiguration
.
Groovy 模板
如果使用 Groovy 模板,请将 spring.groovy.template.cache
设置为 false
. 有关其他 Groovy 定制选项,请参见 GroovyTemplateAutoConfiguration
.
17.14.3. 快速重启应用程序
spring-boot-devtools
模块包括对应用程序自动重启的支持. 尽管不如 JRebel 这样的技术快,但通常比 “cold start” 要快得多. 在研究本文档后面讨论的一些更复杂的重载选项之前,您可能应该先尝试一下.
有关更多详细信息,请参见 开发者工具 部分.
17.14.4. 重新加载 Java 类而无需重新启动容器
许多现代的 IDE (Eclipse,IDEA等) 都支持字节码的热交换. 因此,如果所做的更改不影响类或方法的签名,则应干净地重新加载而没有副作用.
17.15. 测试
Spring Boot 包括许多测试实用程序和支持类,以及提供常见测试依赖项的 starter。 本节回答有关测试的常见问题。
17.15.1. 使用 Spring Security 进行测试
Spring Security 提供了对以特定用户身份运行测试的支持. 例如,下面的代码段中的测试将与具有 ADMIN
角色的经过身份验证的用户一起运行.
java
@WebMvcTest(UserController.class)
class MySecurityTests {
@Autowired
private MockMvc mvc;
@Test
@WithMockUser(roles = "ADMIN")
void requestProtectedUrlWithUser() throws Exception {
this.mvc.perform(get("/"));
}
}
Spring Security 提供了与 Spring MVC Test 的全面集成,并且在使用 @WebMvcTest
slice 和 MockMvc
测试控制器时也可以使用它.
有关 Spring Security 的测试支持的更多详细信息,请参阅 Spring Security 的 参考文档.
17.15.2. 在集成测试中使用 Testcontainers
Testcontainers 库提供了一种方法来管理在 Docker 容器中运行的服务. 它与 JUnit 集成,允许您编写一个测试类,该类可以在运行任何测试之前启动容器. Testcontainers 在编写与真实的后端服务 (例如 MySQL,MongoDB,Cassandra 等) 进行通信的集成测试时特别有用.Testcontainers 可以在 Spring Boot 测试中使用,如下所示:
java
@SpringBootTest
@Testcontainers
class MyIntegrationTests {
@Container
static Neo4jContainer<?> neo4j = new Neo4jContainer<>("neo4j:4.2");
@Test
void myTest() {
// ...
}
}
在运行任何测试之前,这将启动运行 Neo4j 的 Docker 容器 (如果 Docker 在本地运行) . 在大多数情况下,您将需要使用正在运行的容器中的详细信息 (例如容器 IP 或端口) 来配置应用程序.
这可以通过静态 @DynamicPropertySource
方法完成,该方法允许向 Spring Environment 添加动态属性值.
java
@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 通信.
17.15.3. Structure @Configuration
classes for inclusion in slice tests
切片测试通过将 Spring Framework 的组件扫描限制为基于其类型的一组有限组件来工作。 对于不是通过组件扫描创建的任何 bean,例如,使用 @Bean
注解创建的 bean,切片测试将无法从应用程序上下文中包含/排除它们。 考虑这个例子:
java
@Configuration(proxyBeanMethods = false)
public class MyConfiguration { @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests((requests) -> requests.anyRequest().authenticated()); return http.build(); } @Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() { return DataSourceBuilder.create().type(BasicDataSource.class).build(); }}
对于具有上述 @Configuration
类的应用程序的 @WebMvcTest
,您可能希望在应用程序上下文中有 SecurityFilterChain
bean,以便您可以测试您的控制器端点是否得到正确保护。 但是,@WebMvcTest 的组件扫描过滤器不会选择 MyConfiguration
,因为它与过滤器指定的任何类型都不匹配。 您可以通过使用 @Import(MyConfiguration.class)
注解测试类来显式包含配置。 这将加载 MyConfiguration
中的所有 bean,包括测试 Web 层时不需要的 BasicDataSource
bean。 将配置类拆分为两个将启用仅导入安全配置。
java
@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration { @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeRequests((requests) -> requests.anyRequest().authenticated()); return http.build(); }}
java
@Configuration(proxyBeanMethods = false)
public class MyDatasourceConfiguration { @Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() { return DataSourceBuilder.create().type(BasicDataSource.class).build(); }}
当某个 domain 的 bean 需要包含在切片测试中时,拥有单个配置类可能效率低下。 相反,将应用程序的配置构建为具有特定 domain 的 bean 的多个粒度类可以启用仅针对特定切片测试导入它们。
17.16. 构建
Spring Boot 包括 Maven 和 Gradle 的构建插件. 本部分回答有关这些插件的常见问题.
17.16.1. 生成构建信息
Maven 插件和 Gradle 插件都允许生成包含项目的坐标,名称和版本的构建信息. 还可以将插件配置为通过配置添加其他属性. 当存在这样的文件时,Spring Boot 会自动配置 BuildProperties
bean.
要使用 Maven 生成构建信息,请为 build-info
目标添加执行,如以下示例所示:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.6.11</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
TIP
有关更多详细信息,请参见 Spring Boot Maven 插件文档.
下面的示例对 Gradle 执行相同的操作:
springBoot {
buildInfo()
}
TIP
有关更多详细信息,请参见 Spring Boot Gradle 插件文档 .
17.16.2. 生成 Git 信息
Maven 和 Gradle 都允许生成一个 git.properties
文件,其中包含有关构建项目时 git 源代码仓库状态的信息.
对于 Maven 用户,spring-boot-starter-parent
POM包含一个预先配置的插件,用于生成 git.properties
文件. 要使用它,请将以下声明添加到您的 POM 中:
xml
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>
Gradle 用户可以使用 gradle-git-properties
插件获得相同的结果,如以下示例所示:
plugins {
id "com.gorylenko.gradle-git-properties" version "2.3.2"
}
Maven 和 Gradle 插件都允许配置 git.properties
中包含的属性.
TIP
git.properties
中的提交时间应与以下格式匹配: yyyy-MM-dd’T’HH:mm:ssZ
. 这是上面列出的两个插件的默认格式. 使用这种格式,可以将时间解析为 Date
,并将其序列化为 JSON 后,由杰克逊的日期序列化配置设置控制.
17.16.3. 自定义依赖版本
spring-boot-dependencies POM 管理常见依赖的版本. Maven 和 Gradle 的 Spring Boot 插件允许使用构建属性来自定义这些托管依赖版本.
TIP
每个 Spring Boot 发行版均针对这组特定的第三方依赖进行了设计和测试. 覆盖版本可能会导致兼容性问题.
要在 Maven 中覆盖依赖版本,请参阅 Maven 插件文档的 这一部分.
要在 Gradle 中覆盖依赖版本,请参阅 Gradle 插件文档的 这一部分.
17.16.4. 使用 Maven 创建可执行 JAR
spring-boot-maven-plugin
可用于创建可执行的 “fat” JAR. 如果使用 spring-boot-starter-parent
父POM,则可以声明插件,然后将 jar 重新打包,如下所示:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果您不使用父 POM,则仍然可以使用该插件. 但是,您必须另外添加一个 <executions>
部分,如下所示:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>{spring-boot-version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
有关完整用法的详细信息,请参阅 插件文档 .
17.16.5. 使用 Spring Boot 应用程序作为依赖
像 war 文件一样,Spring Boot 应用程序也不打算用作依赖. 如果您的应用程序包含要与其他项目共享的类,则建议的方法是将该代码移到单独的模块中. 然后,您的应用程序和其他项目可以依赖单独的模块.
如果您无法按照上面的建议重新排列代码,则必须配置 Spring Boot 的 Maven 和 Gradle 插件以生成单独的 artifacts ,该 artifacts 适合用作依赖. 可执行存档不能用作依赖,因为 可执行jar格式 BOOT-INF/classes
中的应用程序类打包. 这意味着当将可执行 jar 用作依赖时,找不到它们.
为了产生两个 artifacts ,一个可以用作依赖,另一个可以执行,必须指定分类器. 该分类器应用于可执行归档文件的名称,保留默认归档文件以用作依赖.
要在 Maven 中配置 exec
的 classifier,可以使用以下配置:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
17.16.6. 运行可执行 jar 时提取特定的库
可执行 jar 中的大多数嵌套库不需要解压即可运行. 但是,某些库可能会有问题. 例如,JRuby 包含其自己的嵌套 jar 支持,它假定 jruby-complete.jar
始终可以直接作为文件直接使用.
为了处理任何有问题的库,您可以标记在可执行 jar 首次运行时应自动解压缩特定的嵌套 jar. 这种嵌套的 jar 会写在 java.io.tmpdir
系统属性标识的临时目录下.
TIP
应注意确保已配置您的操作系统,以便在应用程序仍在运行时,它不会删除已解压缩到临时目录中的 jar.
例如,为了指示应该使用 Maven 插件将 JRuby 标记为要解包,您可以添加以下配置:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
</plugins>
</build>
17.16.7. 创建带有排除项的不可执行的 JAR
通常,如果您具有一个可执行文件和一个不可执行的 jar 作为两个单独的构建产品,则可执行版本具有库 jar 中不需要的其他配置文件. 例如,application.yml
配置文件可能被排除在不可执行的 JAR 中.
在 Maven 中,可执行 jar 必须是主要 artifacts ,您可以为库添加一个 classified jar,如下所示:
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>lib</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>lib</classifier>
<excludes>
<exclude>application.yml</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
17.16.8. 远程调试以 Maven 开头的 Spring Boot 应用程序
要将远程调试器附加到使用 Maven 启动的 Spring Boot 应用程序,可以使用 maven 插件 的 jvmArguments
属性.
有关更多详细信息,请参见此 示例.
17.16.9. 在不使用 spring-boot-antlib 的情况下从 Ant 构建可执行归档文件
要使用 Ant 进行构建,您需要获取依赖,进行编译,然后创建一个 jar 或 war 存档. 要使其可执行,可以使用 spring-boot-antlib
模块,也可以按照以下说明进行操作:
如果您要构建 jar,请将应用程序的类和资源打包在嵌套的
BOOT-INF/classes
目录中. 如果要打仗,请照常将应用程序的类打包在嵌套的WEB-INF/classes
目录中.将运行时依赖添加到 jar 的嵌套
BOOT-INF/lib
目录中,或将其添加到 war 的WEB-INF/lib
中. 切记不要压缩存档中的条目.将
provided
的 (嵌入式容器) 依赖添加到jar的嵌套BOOT-INF/lib
目录中,或将 war 添加到WEB-INF/lib
提供的嵌套目录中. 切记不要压缩存档中的条目.在归档文件的根目录中添加
spring-boot-loader
类 (以便可以使用Main-Class
) .使用适当的启动器 (例如 jar 文件的
JarLauncher
) 作为清单中的Main-Class
属性,并通过设置Start-Class
属性指定它作为清单条目所需的其他属性.
以下示例显示了如何使用 Ant 构建可执行归档文件:
xml
<target name="build" depends="compile">
<jar destfile="target/${ant.project.name}-${spring-boot.version}.jar" compress="false">
<mappedresources>
<fileset dir="target/classes" />
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources>
<fileset dir="src/main/resources" erroronmissingdir="false"/>
<globmapper from="*" to="BOOT-INF/classes/*"/>
</mappedresources>
<mappedresources>
<fileset dir="${lib.dir}/runtime" />
<globmapper from="*" to="BOOT-INF/lib/*"/>
</mappedresources>
<zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
<manifest>
<attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" />
</manifest>
</jar>
</target>
17.17. 传统部署
Spring Boot 支持传统部署以及更现代的部署形式. 本节回答有关传统部署的常见问题.
17.17.1. 创建可部署的 War 文件
TIP
由于 Spring WebFlux 不严格依赖 Servlet API,并且默认情况下将应用程序部署在嵌入式 Reactor Netty 服务器上,因此 WebFlux 应用程序不支持 War 部署.
产生可部署 war 文件的第一步是提供 SpringBootServletInitializer
子类并覆盖其 configure
方法. 这样做可以利用 Spring Framework 的 Servlet 3.0 支持,并让您在 Servlet 容器启动应用程序时对其进行配置. 通常,您应该更新应用程序的主类以扩展 SpringBootServletInitializer
,如以下示例所示:
java
@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,如下所示:
xml
<packaging>war</packaging>
如果使用 Gradle,则需要修改 build.gradle
以将 war 插件应用于项目,如下所示:
apply plugin: 'war'
该过程的最后一步是确保嵌入式 servlet 容器不干扰 war 文件所部署到的 servlet 容器. 为此,您需要将嵌入式 Servlet 容器依赖性标记为已提供.
如果使用 Maven,则以下示例将 servlet 容器 (在本例中为 Tomcat) 标记为已提供:
xml
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- ... -->
</dependencies>
如果使用 Gradle,则以下示例将 servlet 容器 (在本例中为 Tomcat) 标记为已提供:
dependencies {
// ...
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// ...
}
TIP
与 Gradle 的 compileOnly
配置相比,providerRuntime
更受欢迎. 除其他限制外,compileOnly
依赖不在测试类路径上,因此任何基于 Web 的集成测试都将失败.
如果您使用 Spring Boot 构建工具,则将提供的嵌入式 servlet 容器依赖标记为提供,将生成可执行的 war 文件,其中提供的依赖打包在lib提供的目录中. 这意味着,除了可以部署到 Servlet 容器之外,还可以通过在命令行上使用 java -jar
运行应用程序.
17.17.2. 将现有应用程序转换为 Spring Boot
对于非 Web 应用程序,将现有的 Spring 应用程序转换为 Spring Boot 应用程序应该很容易. 为此,请丢弃创建您的 ApplicationContext
的代码,并将其替换为对 SpringApplication
或 SpringApplicationBuilder
的调用. Spring MVC Web 应用程序通常适合于首先创建可部署的 war 应用程序,然后再将其迁移到可执行的 war 或 jar. 请参阅有关将 将 jar 转换为 war 的入门指南.
要通过扩展 SpringBootServletInitializer
(例如,在名为 Application
的类中) 并添加Spring Boot @SpringBootApplication
注解来创建可部署的 war ,请使用类似于以下示例中所示的代码:
java
@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;
}
}
请记住,无论您在源代码中放入什么内容,都仅是 Spring ApplicationContext
. 通常,任何已经起作用的东西都应该在这里工作. 可能有些 bean 可以在以后删除,并让 Spring Boot 为它们提供自己的默认值,但是应该可以使某些东西工作,然后再执行此操作.
可以将静态资源移至类路径根目录中的 /public
(或 /static
或 /resources
或 /META-INF/resources
) . 这同样适用于 messages.properties
(Spring Boot会在类路径的根目录中自动检测到该消息) .
在 Spring DispatcherServlet 和 Spring Security 中使用 Vanilla 不需要进一步更改. 如果您的应用程序中具有其他功能 (例如,使用其他 servlet 或过滤器) ,则可能需要通过替换 web.xml
中的那些元素来向 Application
上下文中添加一些配置,如下所示:
类型为
Servlet
或ServletRegistrationBean
的@Bean
将该 bean 安装在容器中,就好像它是web.xml
中的<servlet />
和<servlet-mapping />
一样.类型为
Filter
或FilterRegistrationBean
的@Bean
的行为类似 (作为<filter />
和<filter-mapping />
) .可以通过应用程序中的
@ImportResource
添加 XML 文件中的ApplicationContext
. 或者,可以在几行中重新创建已经大量使用注解配置的简单情况作为@Bean
定义.
war 文件运行后,可以通过向 Application
中添加 main
方法使其变为可执行文件,如以下示例所示:
java
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
TIP
如果您打算以 war 或可执行应用程序的形式启动应用程序,则需要使用 SpringBootServletInitializer
回调可用的方法和类似于以下类的 main
方法中的共享方法来共享构建器的自定义项:
java
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return customizerBuilder(builder);
}
public static void main(String[] args) {
customizerBuilder(new SpringApplicationBuilder()).run(args);
}
private static SpringApplicationBuilder customizerBuilder(SpringApplicationBuilder builder) {
return builder.sources(MyApplication.class).bannerMode(Banner.Mode.OFF);
}
}
应用程序可以分为多个类别:
没有
web.xml
的 Servlet 3.0+ 应用程序.带有
web.xml
的应用程序.具有上下文层次结构的应用程序.
没有上下文层次结构的应用程序.
所有这些都应该适合翻译,但是每种可能都需要稍微不同的技术.
如果 Servlet 3.0+ 应用程序已经使用了 Spring Servlet 3.0+ 初始化程序支持类,那么它们可能会很容易转换. 通常,来自现有 WebApplicationInitializer
的所有代码都可以移入 SpringBootServletInitializer
. 如果您现有的应用程序具有多个 ApplicationContext
(例如,如果使用 AbstractDispatcherServletInitializer
) ,则您可以将所有上下文源组合到一个 SpringApplication
中. 您可能会遇到的主要并发症是,如果合并无效,则需要维护上下文层次结构. 有关示例,请参见有关 构建层次结构的条目 . 通常需要分解包含特定于 Web 的功能的现有父上下文,以便所有 ServletContextAware
组件都位于子上下文中.
还不是 Spring 应用程序的应用程序可以转换为 Spring Boot 应用程序,前面提到的指南可能会有所帮助. 但是,您可能仍然遇到问题. 在这种情况下,我们建议 使用 spring-boot
标签在Stack Overflow上提问.
17.17.3. 将 WAR 部署到 WebLogic
要将 Spring Boot 应用程序部署到 WebLogic,必须确保 servlet 初始化程序直接实现 WebApplicationInitializer
(即使您从已经实现它的基类进行扩展) .
WebLogic 的典型初始化程序应类似于以下示例:
java
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {}
如果使用 Logback,则还需要告诉 WebLogic 首选打包版本,而不是服务器预先安装的版本. 您可以通过添加具有以下内容的 WEB-INF/weblogic.xml
文件来实现:
xml
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd http://xmlns.oracle.com/weblogic/weblogic-web-app https://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>