17. “如何做”指南
这部分提供一些常用的“我做这些…如何做”的问题的答案,当使用Spring Boot时,这些问题经常发生。它的覆盖范围并不全面,但确实涵盖了相当多的内容。
如果你有特别的问题,我们没有涵盖到,你可能想要检查stackoverflow.com来查看,如果某人已经提供了一个答案。这个也是一个非常好的地方回答新的问题(请使用spring-boot
标签)。
17.1. Spring Boot应用程序
这部分包含直接相关Spring Boot应用程序的主题。
17.1.1. 创建你自己的FailureAnalyzer
FailureAnalyzer
是一个很好的方式在启动时来拦截一个异常并将它转为一个人为可读取的信息。封装进一个FailureAnalysis
。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
。
17.1.2. 排除自动配置问题
Spring Boot自动配置尽最大的努力“做正确的事情”,但是有时候失败,他告诉为什么是非常困难的。
在任何Spring Boot ApplicationContext
中存在一个非常有用的ConditionEvaluationReport
。如果你启用BEBUG
日志输出,你可以看到它。如果你使用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.prot
,server.address
和其他。在启用actuator的运行中的应用程序,可以在configprops
端点查看. - 查找在
Binder
上的bind
方法的用法,以轻松的方式显式地从Environment
中提取配置值。它通常带有前缀。 - 查找
@Value
注解,直接和Environment
绑定 - 查找
@ConditionalOnExpression
注解,它在响应SpEL表达式时,打开和关闭特性,通常使用从Environment
解析的占位符进行计算。
17.1.3. 在启动之前定制环境或者ApplicationContext
SpringApplication
有ApplicationListeners
和ApplicationContextInitializers
,他们被用来上下文和环境的定制。Spring Boot从META-INF/spring.factories
加载许多这样的定制以内部使用。有超过一种方式来注册额外的定制:
- 程序化的,每个应用程序,在你调用它之前,在
SpringApplication
中通过调用addListeners
和addInitializers
方法。 - 声明式的,每个应用程序,通过设置
context.initializer.class
或者context.listener.class
属性。 - 声明式的,对于所有应用程序,通过添加
META-INF/spring.factories
并打包一个jar文件,所有的应用程序将它作为一个类库使用。
SpringApplication
发送一些特定的ApplicationEvents
到监听器(一些甚至在上下文创建之前),然后也为通过ApplicationContext
发布的事件注册监听器。请查看在Spring Boot特性章节中的“应用程序事件和监听器”了解完整列表。
它也可以在应用程序上下文刷新之前使用EnvironmentPostProcessor
来定制Environment
。每一个实现应该在META-INF/spring.factories
注册,如下示例所示:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
这个实现可以加载任意的文件并将他们添加到Environment
.例如,下面的示例加载来自类路径的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
已经使用所有的默认情况下Spring Boot加载的通常的属性源做好准备。因此可以从环境中获取文件的位置。前面的示例在列表的最后添加custom-resource
属性,以便在任何通常的其他位置定义的key有优先级。自定义的实现可以定义另一个顺序。
当在你的
@SpringBootApplication
上使用@PropertySource
可能看起来成为一个方便的方式来加载在Environment
中的一个自定义资源,我们不建议这样做。这些属性资源是不会被添加到Environment
,直到应用程序上下文已经刷新。对于配置某个属性来说,这样太晚了,例如logging.*
和spring.main.*
,他们在刷新开始之前被读取。
17.1.4. 构建一个ApplicationContext层次(添加一个Parent或者Root上下文)
你可以使用ApplicationBuilder
类来创建parent/child ApplicationContext
层次。请查看在Spring Boot特性章节“流式Builder API”了解更多信息。
17.1.5. 创建一个非web应用程序
并不是所有的Spring application必须是web应用程序(或者web服务)。如果你想在main
方法执行一些代码但是也引导一个Spring应用程序建立基础设施来使用,你可以使用Spring Boot的SpringApplication
特性。SpringApplication
改变它的ApplicationContext
类,取决于它是否认为它需要一个web应用程序。你可以做的第一件事来帮助它是将与服务器相关的依赖项(例如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项目属性,如下示例所示:
app:
encoding: "@project.build.sourceEncoding@"
java:
version: "@java.version@"
只有生产配置是这样过滤的(换句话说,没有对
src/test/resources
应用任何过滤)。
如果你启用
addResources
标识,spring-boot:run
目标可以直接添加src/main/resources
到类路径(出于热部署目的)。这样绕过了资源过滤和这个特性,你可以使用eec:java
目标或者定制插件的配置。请查看插件用法页面了解更多详情。
如果你没有使用启动器父类,你需要你的pom.xml
的<build/>
元素内包含以下元素:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
你也需要在<plugins/>
内包含以下元素:
<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>
如果在配置中,你使用标准的Spring占位符(例如
${placeholder}
),useDefaultDelimiters
属性是重要的。如果该属性没有设置为false
,通过构建可能扩展这些属性。
使用Gradle自动化属性扩展
你可以通过配置Java 插件的processResources
任务自动化扩展来自Gradle项目的属性,如下示例所示:
tasks.named('processResources') {
expand(project.properties)
}
你可以通过使用占位符引用你的Gradle项目的属性,如下示例所示:
app:
name: "${name}"
description: "${description}"
Gradle的
expand
方法使用Groovy的SimpleTemplateEngine
,它转换${...}
符号。${...}
格式与Spring自己的属性占位符机制冲突。要与自动化扩展一起使用Spring属性占位符,转译Spring 属性占位符,如下\${..}
。
17.2.2. 外部化SpringApplication配置
SpringApplication
有bean属性设置器,所以你可以在创建应用程序时使用它的Java API来修改他的行为。或者,你可以通过设置在spring.main.*
属性外部化配置。例如,在application.properties
,你可能有以下设置:
spring:
main:
web-application-type: "none"
banner-mode: "off"
然后Spring Boot banner在启动时不会打印,并且应用程序不会启动内嵌的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);
}
}
或者提供给SpringApplicationBuilder
的sources(...)
方法:
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"
banner-mode: "console"
真正的应用程序将展示banner(因为配置重写)并为ApplicationContext
使用了三个源。应用程序源是:
MyApplication
(来自代码)MyDatabaseConfig
(来自外部配置)MyJmsConfig
(来自外部配置)
17.2.3. 更改应用程序外部属性的位置
默认情况下,来自不同源的属性以被定义的顺序被添加到Spring Environment
(请查看在‘Spring Boot特性的章节’的“外部化配置”了解确切的顺序)。
你也可以提供以下系统属性(或者环境变量)来改变此行为:
spring.config.name
(SPRING_CONFIG_NAME
):application
作为文件名称的根路径的默认值spring.config.location
(SPRING_CONFIG_LOCATION
):要加载的文件(例如类路径资源或者URL)。为这个文档设置了单独的Environment
属性源,可以通过系统属性,环境变量或者命令行覆盖它。
和你在环境中所设置的无关,Spring Boot一直如上所述地加载application.properties
。默认情况下,如果YAML被使用,然后带有’.yml’文件扩展也将被添加到列表。
Spring Boot以DEBUG
级别记录被加载的配置文件,以及在TRACE
级别为找到的候选文件。
请查ConfigFileApplicationListener看了解更多详情。
17.2.4. 使用“短”命令行参数
一些人喜欢在命令行使用(例如)--port=9000
代替--server.port=9000
来设置配置属性。你可以通过使用在application.properties
中的占位符启用这个行为,如下示例所示:
server:
port: "${port:8080}"
如果你继承
spring-boot-starter-parent
POM,maven-resources-plugins
的默认的过滤器token已经从${*}
更改为@
(也就是,@maven.token
代替${maven.token}
)来避免与Spring风格占位符冲突。如果你已经为application.properties
直接启用Maven过滤器,你可能也想要改变默认的过滤器token以使用其他的界定符。
在特定的情况下,端口绑定工作在PaaS环境中,如Heroku或者Foundry。在这两个平台中,
PORT
环境变量被自动设置并且Spring可以绑定到Environment
属性的大写同义词。
17.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
的文件和将它放入到根类路径。然后添加snakeyaml
到你的依赖(Maven 坐标org.yaml:snakeyaml
,如果你使用了spring-boot-starter
则已经包含)。YAML文件被转换为Java Map<String,Object>
(像JSON对象),并且Spring Boot平铺这个map以便他是一级深度并有分隔周期key,因为许多人在Java中习惯使用Properties
文件。
上面的示例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
请查看Spring Boot特性章节中的“使用YAML工作”了解更多关于YAML信息。
17.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 Boot中,你也可以在application.properties
中设置活跃的配置文件,如下示例所示:
spring:
profiles:
active: "production"
这种方式设置的值通过使用系统属性或者环境变量设置替换,但是不会被SpringApplicationBuilder.profiles()
方法替换。因此,后者Java API可以在没有更改默认值的情况下用来增加配置文件。
请查看在Spring Boot特性章节中“配置文件”了解更多信息。
17.2.7. 设置默认的配置文件名称
默认的配置文件是一个如果没有配置文件是活跃被启用的配置文件。默认情况下,默认的配置文件的名称是default
,但是可以通过系统属性(spring.profiles.default
)或者OS环境变量(SPRING_PROFILES_DEFAULT
)更改。
在Spring Boot中,你也可以在application.properties
中设置默认的配置文件名称,如下示例所示:
spring:
profiles:
default: "dev"
请查看在Spring Boot特性章节中“配置文件”了解更多信息。
17.2.8. 依据环境修改配置
Spring Boot 支持多文档YAML和属性文件(请查看使用多文档文件工作了解细节),这些文件基于活跃的配置文件有条件的被激活。
如果一个文档包括spring.config.activate.on-profile
键,然后配置文件的值(逗号分隔的配置文件列表或者配置文件表达式)被输入到Spring Environment.acceptProfiles()
方法。如果配置文件表达式匹配,则该文档将包含在最终合并中(否则不包含),如下示例所示:
server:
port: 9000
---
spring:
config:
activate:
on-profile: "development"
server:
port: 9001
---
spring:
config:
activate:
on-profile: "production"
server:
port: 0
在上面的示例中,默认的端口是9000.然而,如果称为 'development’的Spring配置文件是活跃的,然后这个端口是9001.如果’production’是活跃的,然后端口是0.
文档以在文档中遇到的顺序的合并。最后的值覆盖前面的值。
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服务器。这个特性导致许多如果做问题,包括如果修改内嵌服务器和如何配置内嵌服务器。这部分将回答这些问题。
17.3.1. 使用另一个web服务器
许多Spring Boot启动器包含默认的内嵌容器:
- 对于servlet 栈应用程序,
spring-boot-starter-web
通过包括spring-boot-starter-tomcat
包含Tomcat,但是你可以使用spring-boot-starter-jetty
或者spring-boot-starter-undertomw
代替。 - 对于响应式栈应用程序,
spring-boot-starter-webflux
通过包含spring-boot-starter-reactor-netty
包含Reactor Netty,但是你可以使用spring-boot-starter-tomcat
,spring-boot-starter-jetty
,或者spring-boot-starter-undertomw
代替。
当转换到一个不同的HTTP服务器,你需要将默认的依赖项转换为你所需要的依赖项。为了帮助这个过程,Spring Boot为每一个支持的HTTP服务器提供一个单独的启动器。
以下Maven示例展示如何排除Tomcat和包含Jetty用于Spring MVC:
<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>
servlet API的版本已经被重写,因为与Tomcat 9和Undertow2不同,Jetty9.4不支持servlet 4.0。
如果你希望使用Jetty 10,它不支持servlet 4.0,你可以正如下方示例展示的这样做:
<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>
注意除了排除Tomcat启动器外,还需要排除两个特定的Jetty9依赖。
以下Gradle示例配置了必须的依赖和模块替换来使用Undertow代替Reator Netty用于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")
}
}
}
使用
WebClient
类需要spring-boot-starter-reactor-netty
,所以即使你需要包含一个不同的HTTP服务器,你可能需要保持对Netty的依赖。
17.3.2. 禁用web服务器
如果你的类路径包含必须的片段来启动一个web服务器,Spring Boot将自动启动它。要禁用这种行为,在application.properties
中配置WebApplicationType
,如下示例所示:
spring:
main:
web-application-type: "none"
17.3.3. 修改HTTP端口
在一个单独的应用程序中,主要的HTTP端口默认是8080
,但是可以使用server.port
设置(例如,在application.properties
或者系统属性)。感谢宽松的Environment
值绑定,你也可以使用SERVER_PORT
(例如,作为一个系统变量)。
要完全关闭HTTP端点但仍创建一个WebApplicationContext
,使用servet.prot=-1
(这样做有时对于测试是有用的)。
要了解更多详情,请查看在Spring Boot特性章节中的“定制内嵌servelt容器”,或者ServerProperties
源码。
17.3.4.使用一个随机的未赋值的HTTP端口
要对空闲端口(使用OS native防止冲突)扫描,使用servet.port=0
17.3.5. 在运行时发现HTTP端口
你可以从日志输出或者通过它的WebServer
从WebServerApplicationContext
访问正在运行服务器的端口。获取它并确保它被初始化的最好的方法是添加一个类型为ApplicationListener<WebServerInitializedEvent>
的@Bean
并在发布时将容器从事件中拉出。
使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOW_PORT)
的测试也可以通过使用@LocalServerPort
注解注入真实的端口到一个字段:
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@LocalServerPort
int port;
// ...
}
@LocalServerPort
是一个元注解用于@Value("${local.server.prot}")
。不要尝试注入这个端口到常规的应用程序。正如我们刚刚看到的,只有在容器已经被初始化之后才设置这个值。与测试相反,应用程序代码回调会提前处理(在这个值真正可用之前)。
17.3.6. 启用HTTP响应压缩
Jetty,Tomcat,Reactor Netty和Undertow支持HTTP响应压缩。他可以在applicaton.properties
被启用,如下:
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
SSL可以通过设置多个servler.ssl.*
属性声明式配置,典型方式在application.properties
或者application.yml
.以下示例展示使用Java KeyStore文件设置SSL属性:
server:
port: 8443
ssl:
key-store: "classpath:keystore.jks"
key-store-password: "secret"
key-password: "another-secret"
以下示例展示使用PEM-编码证书和私钥文件设置SSL属性:
server:
port: 8443
ssl:
certificate: "classpath:my-cert.crt"
certificate-private-key: "classpath:my-cert.key"
trust-certificate: "classpath:ca-cert.crt"
请查看Ssl
了解所有支持的属性的详情。
使用配置,例如前面示例表示应用程序不在支持在端口8080上无装饰的HTTP链接。Spring Boot不支持通过application.properties
同时配置HTTP链接和HTTPS连接。如果你想有两者,你需要程序化地配置他们中的一个。我们建议使用application.properties
来配置HTTPS,因为HTTP连接是两个比较容易进行程序化配置。
17.3.8. 配置HTTP/2
你可以在你的Spring Boot应用程序中使用server.http2.enable
配置属性启用HTTP/2 支持。h2
(基于TLS的HTTP/2)和h2c
(基于TCP的HTTP/2)都被支持。要使用h2
,SSL也必须启用。当SSL没有启用,h2c
将被使用。h2
支持的明细取决于选择的Web服务器和应用程序环境,因为该协议不被所有的JDK 8发行版支持开箱即用。
HTTP/2与Tomcat
Spring Boot默认情况下使用Tomcat 9.0.x传输,当使用JDK 9或者更晚版本,它支持h2c
开箱即用和h2
开箱即用。或者,如果早主机操作系统libtcnative
类库和它的依赖已经安装,h2
可以在JDK 8上使用。
类库目录必须对JVM库路径可用(如果还没有的话)。你可以使用JVM参数这样做,例如-Djava.library.path=/usr/local/opt/tomcat-native/lib
。更多关于这个的信息在官方的Tomcat文档。
在JDK 8上使用启用的HTTP/2 和 SSL启动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支持启动。
HTTP/2与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
和Conscrypt类库没有JDK要求。
HTTP/2与Reactor Netty
默认情况下,spring-boot-webflux-starter
使用Reactor Netty当做服务器.Reactor Netty使用JDK8或者更晚版本支持h2c
,不使用额外的依赖项。Reactor Netty使用JDK 9 或者更晚版本的JDK支持h2
.对于JDK 8环境,或者对于最佳的运行时表现,这个服务器也本机类库支持h2
。要启用该功能,你的应用程序需要一个额外的依赖。
Spring Boot管理io.netty:netty-tcnative-boringssl-static
的“uber jar”的版本,包括所有平台的本地库。开发者可以使用classifier选择只引入所需要的依赖(请查看Netty官方文档)。
HTTP/2与Undertow
自从Undertow 1.4.0+起,h2
和h2c
两者都在JDK 8基础上得到支持,无需任何额外的依赖项。
17.3.9. 配置Web服务器
通常,首先你应该考虑使用许多可用的配置键中的一个和通过在application.properties
或者application.yml
文件中添加新的条目定制你的web服务器。请查看“发现外部化配置的内置选项”。server.*
命名空间在这里是非常有用的,并且对于特定的服务器特性,它包含像server.tomcat.*
,server.jetty.*
和其他的命名空间。请查看常用的应用程序属性列表
以前的章节已经涵盖许多常用的用例,例如压缩,SSL或者HTTP/2。然而,如果对于你的用例,一个配置key不存在,你应该查看WebServerFactoryCustomizer
。你可以声明这样一个组件并获得你选择的相关的服务器工厂访问权:你应该为选择的服务器选择变体。(Tomcat,Jetty,Reactor Netty,Undertow)和已选择的web技术栈(servlet或者reactive)。
下方的示例是针对使用spring-boot-starter-web
的Tomcat的(servlet技术栈):
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<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}
Spring Boot使用内部的基础设施来自动配置服务器。自动配置的
WebServerFactoryCustomizer
bean有一个0
排序并且在任何用户的定制之前被处理,除非它有一个显示地另有规定的顺序。
一旦你使用定制已经获得WebServerFactory
的访问权,你可以使用它来配置特定的部分,像连接,服务器资源或者服务器本身-所有使用特定服务器API。
此外,Spring Boot还提供:
服务器 | Servlet技术栈 | Reactive 技术栈 |
---|---|---|
Tomcat | TomcatServletWebServerFactory | TomcatReactiveWebServerFactory |
Jetty | JettyServletWebServerFactory | JettyReactiveWebServerFactory |
Undertow | UndertowServletWebServerFactory | UndertowReactiveWebServerFactory |
Reactor | N/A | NettyReactiveWebServerFactory |
作为最后的手段,你也可以声明你自己的WebServerFactory
bean,它将重写Spring Boot提供的那个。当你这样做,自动配置的定制仍应用到你自定义工厂,所以小心使用该选项。
17.3.10. 添加Servlet,Filter或者Listener到应用程序
在servlet技术栈应用程序,它是使用spring-boot-starter-web
,有两种方式将Servlet
,Filter
,ServletContextListener
和其他Servlet API支持的监听器添加到你的应用程序:
- 使用Spring Bean添加Servlet,Filter或者Listener。
- 使用类路径扫描添加Servlet,Filter或者Listener。
使用Spring Bean添加Servlet,Filter或者Listener
要使用Spring bean添加Servlet
,Filter
或者servlet*Listener
,你必须为它提供@Bean
定义。当你想要注入配置或者依赖项时,这样做是非常有用的。然而,你必须非常小心,他们不要引起太多其他bean的提前初始化,因为必须在应用程序的生命周期的早期,将他们安装到容器中(例如,让他们依赖于DataSource或者JPA配置不是一个好主意)。你可以通过第一次使用bean时,而不是初始化的时候,延迟初始化bean来绕过这些限制。
过滤器和servlet的这种情况,你也可以通过添加FilterRegistrationBean
或者ServletRegistrationBean
添加映射和初始化参数,而不是添加底层组件。
如果在一个过滤器注册上没有
dispatcherType
被指定,REQUEST
被使用。这与servlet规范的默认的调度类型保持一致。
就像任何其他的Spring bean,你可以定义servlet过滤器bean顺序;请确保查看"注册Servlet,Filters和Listeners为Spring Bean"章节。
禁用Servlet或者Filter注册
正如前面所描述的,任何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<MyFilter> registration(MyFilter filter) {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
}
通过使用类路径扫描添加Servlets,Filters和Listeners
使用内嵌的servlet容器通过注解@Configuration
类与@ServletComponentScan
并指定包含你需要注册组件的包,可以自动注册@WebServlet
,@WebFilter
和@WebListener
注解的类。默认情况下,@ServletComponentScan
扫描被注册类的包。
17.3.11. 配置访问日志
访问日志对Tomcat,Undertow和Jetty自动配置通过他们各自的命名空间。
例如,以下设置使用自定义格式日志访问Tomcat:
server:
tomcat:
basedir: "my-tomcat"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D ms)"
默认的日志的位置是
logs
目录相对于tomcat基本目录。默认情况下,logs
目录是临时目录,所以你可以想要确定Tomcat的基本目录或者使用一个日志的绝对路径。在前面的示例中,在相对于应用程序的工作目录my-tomcat/longs
中日志是可用的。
对于Undertow访问日志可以以类似的方式配置,如下示例所示:
server:
undertow:
accesslog:
enabled: true
pattern: "%t %a %r %s (%D ms)"
日志存储在logs
目录相对于应用程序的工作目录。你可以通过设置server.undertow.accesslog.dir
属性定制这个位置。
最后,对于Jetty访问日志也可以如下配置:
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
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 Framework提供ForwardedHeaderFilter。你可以通过设置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
。在所有其他实例中默认为NONE
。
定制Tomcat的代理配置
如果你使用Tomcat,你可以额外的配置header名称,用来携带“转发”信息。如下示例所示:
server:
tomcat:
remoteip:
remote-ip-header: "x-your-remote-ip-header"
protocol-header: "x-your-protocol-header"
也可以使用正则表达式配置Tomcat,以便匹配授信的内部代理。请查看在附录中的server.tomcat.remoteip.internal-proxies
条目了解他的默认值。你可以通过添加application.yaml
条目定制此值的配置,如下示例所示:
server:
tomcat:
remoteip:
internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
你可以设置
internal-proies
为空相信所有的代理(但是不要再生产环境这样做)
你可以通过关闭自动(为此,设置server.forward-headers-strategy=NONE
)来完全控制Tomcat的RemoteIpValue
的配置并且使用WebServerFactoryCustomizer
bean添加一个新的值实例。
17.3.13. 启用Tomcat的多个连接器
你可以添加org.apache.catalina.connector.Connector
到TomcatServletWebServerFactory
,它可以允许多个连接器,包括HTPP和HTTPS链接,如下示例所示:
import org.apache.catalina.connector.Connector;
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 MyTomcatConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> sslConnectorCustomizer() {
return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createConnectorConnector());
}
private Connector createConnectorConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(8080);
return connector;
}
}
17.3.14. 使用Tomcat的LegacyCookieProcessor
默认情况下,Spring Boot使用的内嵌的Tomcat不支持"Version 0"的Cookie格式,所有你可能看到以下错误:
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<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
属性来这样做。如下示例所示:
server:
tomcat:
mbeanregistry:
enabled: true
17.3.16. 启用Undertow多监听器
添加一个UndertowBuilderCustomizer
到UndertowServletWebServerFactory
并添加一个监听器到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<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
,如下示例所示:
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使用底层的WebSocket容器注册了任意@ServerEndpoint
注解的bean。当部署到独立的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响应,如下示例所示:
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
提供一个它的JSON响应。注意,在一个浏览器中,有时你可能看到XML响应,因为浏览器倾向于发送偏好XML的接收头数据。
17.4.2. 写一个XML REST服务
如果在类路径中存在Jackson XML扩展(jackson-dataformat-xml
),你可以使用它来呈现XML响应。前面示例我们用于JSON也可以工作。要使用Jackson XML呈现器,添加以下依赖到你的项目:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
如果Jackson的XML扩展不可用而JAXB可用,XML可以使用将MyThing
注解为@XmlRootElement
附加的要求呈现。如下示例所示:
import jakarta.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class MyThing {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
JAXB只有在Java 8是开箱即用的。如果你使用最新的Java生成,添加以下依赖到你的项目:
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
要使服务器呈现XML而不是JSON,你可能必须发送
Accept:text/xml
头数据(或者使用浏览器)。
17.4.3. 定制Jackson ObjectMapper
Spring MVC(客户端和服务器端)使用HttpMessageConverters
在HTTP交换中协商内容转换。如果Jackson在类路径中,你已经获取Jackson2ObjectMapperBuilder
提供的默认的转换器,它的实例是自动为你注册的。
ObjectMapper
(或者XmlMapper
用于JacksonXML转换器)实例(默认创建的)存在以下定制的属性:
MapperFeature.DEFAULT_VIEW_INCLUSION
是禁用的DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
是禁用的SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
是禁用的
Spring Boot有一些特性可以更容易定制这种行为。
你可以通过使用环境配置ObjectMapper
和XmlMapper
实例。Jackjson提供一套广泛的开/关特性,可以使用它来配置他处理的各个方面。这些特性使用六个枚举描述(在Jackson中),映射到环境的属性上:
枚举 | 属性 | 值 |
---|---|---|
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.databing.Module
类型的bean是使用自动配置的Jackson2ObjectMapperBuilder
自动注册的并应用到任何它创建的ObjectMapper`实例。当你将新的特性添加到你的应用程序时,这个提供了一个全局的机制用于贡献自定义模块。
如果你想完全替换默认的ObjectMapper
,可以定义一个该类型的@Bean
并标记它为@Primary
,或者,如果你更喜欢基于构造器的方式,定义一个Jackson2ObjectMapperBuilder
的@Bean
。注意,在任意一种情况,这样做禁用了所有的ObjectMapper
的自动配置。
如果你提供任何类型为MappingJackson2HttpMessageConverter
的@Bean
,他们替换在MVC配置的默认值。而且,一个方便的HttpMessageConverters
类型的bean被提供(如果你使用默认的MVC配置,是一直可用的)。它有一些有用的方法来访问默认的和用户增加体验的消息转换器。
请查看“定制@ResponseBody展示”章节和WebMvcAutoConfiguration
源码了解更多详情。
17.4.4. 定制@ResponseBody展示
Spring使用HttpMessageConverters
来呈现@ResponseBody
(或者来自@RestController
的响应)。你可以通过在Spring Boot上下文中添加合理类型的bean贡献额外的转换器。如果你添加的bean属于默认情况下应该包含的类型(例如用于JSON转换的MappingJackson2HttpMessageConverter
),它替换默认值。如果你使用默认的MVC配置,一个类型为HttpMessageConverters
方便的bean被提供并且一直可用。它有一些有用的方法来访问默认和用户增强的信息转换器(例如,如果你想手工注入他们到一个自定义的RestTemplate
,它是非常有用的)。
在正常的MVC使用中,任何你提供的WebMvcConfigurer
bean也可以通过重写configureMessageConverters
方法贡献转换器。然而,不像使用正常的MVC,你可以只提供你需要的额外的转换器(因为Spring Boot使用相同机制来贡献他的默认的)。最终,如果你通过提供你自己的@EnableWebMvc
配置来选择退出Spring Boot默认的MVC配置,你可以通过使用来自WebMvcConfigurationSupport
的getMessageConverters
完全控制并手工做任何事情。
请查看WebMvcAutoConfiguration
源码了解更多详情。
17.4.5. 处理Multipart File上传
Spring Boot拥抱servlet 5 jakarta.servlet.http.Part
API来支持上传文件。默认情况下,Spring Boot配置 Spring MVC,每个文件最大大小为1MB,在单个请求中文件数据的最大大小为10MB。你可以覆盖这些值,中间数据存储的位置(例如,存储在/tmp
目录)以及通过使用MultipartProperties
在暴露的属性将数据刷新到磁盘的阈值。例如,如果你想指定文件不受限制,设置spring.servlet.multipart.max-file-size
为-1
。
当你想要接收multipart编码的文件数据作为在Spring MVC控制层处理器方法中带有@RequestParam
注解类型为MultipartFile
参数,multipart支持是非常有帮助的。
请查看MultipartAutoConfiguration
源码了解更多详情。
推荐使用容器对multipart上传的内建支持而不是引入一个额外的依赖,例如Apache Common File Upload。
17.4.6. 关闭Spring MVC DispatcherServlet
默认情况下,所有的内容从应用程序的根目录(/
)提供。如果你宁愿映射到一个不同的路径,你可以如下配置一个:
spring:
mvc:
servlet:
path: "/mypath"
如果你有额外的servlet,你可以声明类型为Servlet
或ServletRegistrationBean
的bean用于每一个并且Spring Boot将透明地注册他们到容器中。因为servlet以该方式注册,他们可以映射到DispatcherServlet
子上下文,而不用调用它。
自己配置DispatcherServlet
并不常见,但是你真的需要这样做,也必须提供类型DispatcherServletPath
的@Bean
来提供你自定义的DispatcherServlet
路径。
17.4.7. 关闭默认的MVC配置
完全控制MVC配置的最容易的方式是提供你自己的带有@EnableWebMvc
注解的@Configuration
。这样做就把所有的MVC配置交给了你。
17.4.8. 定制ViewResolvers
ViewResolver
是一个Spring MVC的核心组件,转化在@Controller
中视图的名称为真正的View
实现。注意在UI应用程序中,主要使用ViewResolvers
,而不是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
。这是一个复合解析器,委托给其他解析器并尝试发现与被客户端发送的’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
。
要了解更多详情,请查看以下部分:
- WebMvcAutoConfiguration
- ThymeleafAutoConfiguration
- FreeMarkerAutoConfiguration
- GroovyTemplateAutoConfiguration
17.5. HTTP客户端
Spring Boot提供多个与HTTP客户一起工作的启动器。这部分解答关于使用他们的问题。
17.5.1 配置RestTemplate来使用代理
正如在RestTemplate定制中所描述的那样,你可以使用RestTemplateCustomizer
和RestTemplateBuilder
来构建一个定制的RestTemplate
。这个推荐的方法用于创建配置为使用代理的RestTemplate
。
代理配置的确切细节依赖于正在使用的底层客户端请求工厂。
17.5.2. 配置Reactor基于Netty的WebClient使用的TcpClient
当Reactor Netty在类路径中,Reactor基于Netty的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
用于连接提供者和事件轮询资源的使用。这确保接收请求的服务器和发出请求的客户端有效地资源共享。
17.6. 日志
Spring Boot没有强制日志依赖,除了Commons Logging API,它通常是由Spring Farmework的spring-jcl
模块提供。要使用Logback,你需要在类路径中包括它和spring-jcl
。这推荐的方式做这些是通过启动器,启动器都取决于spring-boot-starter-logging
。对于一个web应用程序,你只需要spring-boot-starter-web
,因为它依赖于日志启动器。如果你使用Maven,以下依赖项为你添加日志:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot存在一个LoggingSystem
抽象,它尝试基于路径的内容配置日志。如果Logback可用,它是首选。
如果你需要对日志记录进行唯一的更改是设置各种日志记录器的级别,你可以在application.properties
中使用“logging.level”前缀做到这些,如下示例所示:
logging:
level:
org.springframework.web: "debug"
org.hibernate: "error"
你也可以通过logging.file.name
设置文件的位置来指定哪个写入日志(除了控制台)。
要配置更细粒度日志系统的设置,你需要使用LoggingSystem
支持的本机配置格式。默认情况下,Spring Boot从系统的默认地址获取本机配置(例如Logback的classpath:logback.xml
),但是你可以使用logging.config
属性设置配置文件的位置。
17.6.1. 配置Logback用于记录日志
如果你需要应用定制到logback,除了通过application.properties
实现的这些,你可能需要添加一个标准的logback配置文件。你可以添加logback.xml
文件到根类路径用于logback查找。如果你想使用Spring Boot Logbac扩展,你也可以使用logback-spring.xml
。
Logback文档有专门的章节详细地介绍了配置。
Spring Boot提供许多从你的配置中包含的logback配置。这些包含被设计为允许某个常用的Spring Boot约定被重新应用。
以下文件被提供在org/springframework/boot/logging/logback/
下:
default.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 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>
你的logback配置文件也可以利用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 Boot通过使用自定义的Logback转换器也提供一些非常好的ANSI带有颜色的终端输出(但不是在文件日志中)。关于示例,请查看在defaults.xml
配置中的CONSOLE_LOG_PATTERN
。
如果Groovy在类路径中,你应该也可以使用logback.groovy
配置Logback。如果存在,该设置优先。
Groovy配置不支持Spring扩展。任何
logback-spring.groovy
文件都不会被检测到。
配置Logback用于文件输出
如果你想要禁用控制台输出并将输出写入到文件,你需要一个自定义的logback-spring.xml
,它引入file-appender.xml
但是没有console-appender.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
,如下示例所示:
logging:
file:
name: "myapplication.log"
17.6.2. 配置Log4j用于记录日志
如果Log4j 2在类路径中,Spring Boot支持Log4j 2用于日志配置。如果你使用启动器用于组装依赖项,你必须排除Logback然后包含log4j 2代替。如果你不使用启动器,除了Log4j 2之外,你需要提供(至少)spring-jcl
。
推荐路径是通过启动器,即使这需要一些晃动。以下示例展示了如何使用Maven设置启动器:
<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提供几种不同的方式设置启动器。一种方式是使用模块替换。为此,声明一个Log4j 2启动器依赖并告诉Gradle任何默认日志启动器的事件应该使用Log4j 2启动器替换。如下示例所示:
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
但使用Log4j 2配置输出)。
要保证使用
java.util.logging
提供的debug日志被路由到Log4j 2,通过设置java.util.logging.manager
系统属性为org.apache.logging.log4j.jul.LogManager
配置它的JDK日志适配器。
使用YAML或者JSON来配置Log4j 2
除了他的默认的XML配置格式,Log4j 2也支持YAML和JSON配置文件。要使用一个可替代的配置文件格式来要配置Log4j 2,添加合理的依赖到类路径并命名你的配置文件以匹配你选择的文件格式,如下示例所示:
格式 | 依赖项 | 文件名称 |
---|---|---|
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.7 数据访问
Spring Boot包含多个和数据源一起工作启动器。这部分解答与此相关问题。
17.7.1. 配置一个自定义的数据源
要配置你自己的DataSource
,在你的配置中定义一个该类型的@Bean
。Spring Boot在需要的任何地方重用你的DataSource
,包括数据库初始化。如果你需要外部化一些设置,你可以绑定你的DataSource
到环境中(请查看“第三方配置”)。
以下示例展示了如何在一个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();
}
}
以下示例展示如何通过设置属性定义个数据源:
app:
datasource:
url: "jdbc:h2:mem:mydb"
username: "sa"
pool-size: 30
假设SomeDataSource
具有URL,用户名和池大小的常规的JavaBean属性,用户名和池大小,在DataSource
对其他组件可用之前,这些设置将自动绑定。
Spring Boot也提供一个实用的构造器类,叫DataSourceBuilder
,它可以被用来创建标准的数据源之一(如果它在类路径中)。这个构造器可以检测基于在类路径中哪个可用的一个来使用。他也基于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
运行一个app,你需要的只是连接信息。特定的池设置也可以被提供。查看在运行时要使用的实现了解更多细节。
以下示例展示设置属性如何定义一个JDBC数据源:
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
然而,有一个问题。因为链接池实际类型没有暴露,在元数据中没有key生成用于你的自定义DataSource
和IDE中也没有可用的完成功能(因为DataSource
接口暴露任何属性)。而且,如果你碰巧在类路径有Hikari,这个基本设置不会工作,因为Hikari没有url
属性(但是有jdbcUrl
属性)。这种情况,你必须重写你的配置,如下:
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自动创建的DataSource。然而,这将你的配置分为两个命名空间:在spring.datasource
上的url
,username
,password
,type
和driver
和在你的自定义的命名空间(app.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
转换,你可以如下配置它:
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
configuration:
maximum-pool-size: 30
Spring Boot将
spring.datasource.hikari
暴露特定于Hikari设置。这个示例使用一个通用的configuration
子命名空间因为本示例不支持多数据源实现。
因为你的自定义配置选择使用Hikari,
app.datasource.type
没有效果。实际上,这个构造器使用你在那设置任何值来初始化的,然后通过调用.type()
重写。
请查看在Spring Boot特性章节中的“配置一个数据源”和DataSourceAutoConfiguration
了解更多详情。
17.7.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
标记以便数据库初始化器特性使用你的副本(如果你使用初始化器)。
两个数据源也被绑定用于高级定制。例如,你可以如下配置他们:
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
子命名空间基于选择的实现提供高级设置。
17.7.3. 使用Spring Data仓库
Spring Data可以创建多种风格的@Repository
接口实现。只要这些@Repositories
被包含在与@EnableAutoConfiguration
类相同的包(或者子包)Spring Boot为你处理所有这些实现。
对于许多应用程序,你需要的是将正确的Spring Data依赖项放在类路径。有spring-boot-starter-data-jpa
用于JPA,spring-boot-starter-data-mongodb
用于Mongodb,还有各种其他启动器用于支持的技术。首先,创建一些仓库接口来处理你的@Entity
对象。
Spring Boot尝试基于它发现的@EnableAutoConfiguration
猜测@Repository
定义的位置。要获取更多控制,使用@EnableJpaRepositories
注解(来自Spring Data JPA)。
要了解更多Spring Data,请查看Spring Data 项目页面。
17.7.4. 将@Entity定义与Spring配置分离
Spring Boot尝试基于它找到的@EnableAutoConfiguration
猜测@Entity
定义的位置。要获取更多控制,你可以使用@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 {
// ...
}
17.7.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
属性。
下方示例展示了要设置的大多数常用的选项:
spring:
jpa:
hibernate:
naming:
physical-strategy: "com.example.MyPhysicalNamingStrategy"
show-sql: true
除此之外,当本地的EntityManagerFactory
被创建时,在spring.jpa.properties.*
中所有的属性作为普通的JPA属性传递(使用去除前缀)。
你需要保证在
spring.jap.properties.*
下定义的名称与JPA提供商所期望的名称完全匹配。Spring Boot对这些条目不会提供任何类型的宽松绑定。
例如,如果你想配置Hibernate的批次大小你必须使用spring.jpa.properties.hibernate.jdbc.batch_size
。如果你使用其他格式,例如batchSize
或者batch-size
,Hibernate将不会应用这个设置。
如果你需要应用高级定制到Hibernate属性,考虑注册一个
HibernatePropertiesCustomizer
bean,它将在创建EntityManagerFactory
之前被调用。这优先于自动配置应用的任何操作。
17.7.6. 配置Hibernate命名策略
Hibernate使用两个不同的命名策略来映射对象模型名称与对应的数据库名称。物理的全量限定类名称和隐式策略实现通过分别设置spring.jpa.hibernate.naming.physical-strategy
和spring.jpa.hibernate.naming.implicit-strategy
属性配置。或者,如果ImplicitNamingStrategy
或者PhysicalNamingStrategy
bean在应用程序上下文可用,Hibernate将自动配置使用他们。
默认情况下,Spring Boot使用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;
}
};
}
}
如果你更喜欢使用Hibernate5的默认代替,设置以下属性:
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
和JpaBaseConfiguration
了解更多详情。
17.7.7. 配置Hibernate二级缓存
可以为一系列缓存提供者配置Hibernate二级缓存。与其配置Hibernat来查找缓存提供者,不如尽可能提供上下文中可用的缓存提供者。
使用JCache这样做,首先确保org.hibernate:hibernate-jcache
在类路径中可用,然后添加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
实例。要了解详情,请查看Hibernate用户指南。
17.7.8. 在Hibernate组件中使用依赖注入
默认情况下,Spring Boot注册一个使用BeanFactory
的BeanController
实现以便转换器和实体监听器可以使用常规的依赖注入。
你可以通过注册HibernatePropertiesCustomizer
禁用或者调整这个行为,它移除或者修改hibernate.resource.beans.container
属性。
17.7.9. 使用自定义的EntityManagerFactory
要完全控制EntityManagerFactory
配置,你需要添加一个名称为‘entityManagerFactory’的@Bean
。在该类型bean存在的情况下,Spring Boot自动配置关闭他的实体管理器。
17.7.10. 使用多个EntityManagerFactories
如果你需要对多个数据源使用JPA,你可能每一个数据源需要一个EntityManagerFactory
。来自Spring ORM的LocalContainerEntityManagerFactoryBean
允许你配置一个EntityManagerFactory
用于你所需要的。你也可以为每一个EntityManagerFactory
重用JpaProperties
来绑定设置,如下示例所示:
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
的DataSource
创建了一个EntityManagerFactory
。它扫描位于与Order
相同包位置的实体。它可以使用app.first.jpa
命名空间映射额外的JPA属性。
当你为
LocalContainerEntityManagerFactoryBean
本身创建一个bean,在自动配置的LocalContainerEntityManagerFactoryBean
创建期间应用的任何定制是丢失的。例如,在Hibernate这种情况下,在spring.jpa.hibernate
前缀的任何属性将不会自动应用到你的LocalContainerEntityManagerFactoryBean
。如果你依赖这些属性用于配置类似于命名策略或者DDL模式的东西,当创建LocalContainerEntityManagerFactoryBean
bean时,你将需要显示配置它。
你应该为需要JPA访问的任何额外的数据源提供一个类似的配置。为整齐划一,你应该也为每一个EntityManagerFactory
配置一个JpaTransactionManager
。或者,你可以使用一个跨两者的JTA事务管理器。
如果你使用Spring Data,你需要配置对应的@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 {
}
17.7.11. 使用一个传统的persistence.xml文件
默认情况下,Spring Boot不会搜索和使用META-INF/persistence.xml
。如果你更喜欢使用传统的persistence.xml
,你需要定义你自己的类型为LocalEntityManagerFactoryBean
的@Bean
(使用entityManagerFactory
的ID)和在这里设置持久化单元名称。
请查看JpaBaseConfiguration
了解默认设置。
17.7.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 repository类型(Elasticsearch,Solr等等)。要使用他们工作,修改对应的注解和标志的名称。
17.7.13. 定制Spring Data’s Web支持
Spring Data提供web支持,简化在web应用程序中Spring Data 仓库的使用。Spring Boot在spring.data.web
命名空间提供属性用于定制它的配置。注意,如果你正在使用Spring Data REST,你必须使用spring.data.rest
的属性代替。
17.7.14. 作为REST端点暴露Spring Data Repositories
Spring Data REST可以作为REST端点为你暴露Repository
实现,前提是应用程序已经启用Spring MVC。
Spring Boot暴露一组有用的定制RepositoryRestConfiguration
的属性(来自spring.data.rest
命名空间)。如果你需要提供额外的定制,你应该使用RepositoryRestConfigurer
bean。
如果你没有对自定义的
RepositoryRestConfigurer
指定任何顺序,他在Spring Boot内部使用之后运行。如果你需要指定一个顺序,确保大于0.
17.7.15. 配置一个JPA使用的组件
如果你想配置一个JPA使用的组件,然后你需要保证这个组件在JPA之前被初始化。当这个组件自动配置时,Spring Boot会为你处理这个问题。例如,当Flyway自动配置时,Hibernate被配置依赖Flyway以便Hibernate尝试使用它时,Flyway有机会初始化数据库。
如果你自己正在配置一个组件,你可以使用EntityManagerFactoryDependsOnPostProcessor
子类作为设置必须的依赖项一种方便的方式。例如,如果你使用Hibernate Search用Elasticsearch作为他的索引管理器,任何EntityManagerFactory
bean必须依赖elasticsearchClient
bean进行配置。如下示例所示:
import jakarta.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");
}
}
17.7.16. 使用两个数据源配置jOOQ
如果你需要使用多个数据源配置jOOQ,你应该为每一个创建你自己的DSLContent
。请查看JooqAutoConfiguration了解更多细节。
特别是,
JooqExceptionTranslator
和SpringTransactionProvider
可以被重用于提供类似于自动配置对单个DataSource
所做的功能。
17.8. 数据库初始化
一个SQL数据库可以以不同的方式初始化,取决于你的技术栈是什么。当然,你也可以手工初始化,前提是数据库是单独的进程。推荐使用单一机制来模式生成。
17.8.1. 使用JPA初始化数据库
JPA有用于DDL生成的功能,并且针对数据库这些可以被设置为启动时运行。通过两个外部化属性控制:
spring.jpa.generater-ddl
(boolean)打开/关闭功能并且供应商独立的。spring.jpa.hibernate.ddl-auto
(枚举)是一个Hibernate功能,可以以更细粒度的方式控制行为。本指南后面详情描述此特性。
17.8.2. 使用Hibernate初始化一个数据库
你可以显示地设置spring.jpa.hibernate.ddl-auto
并且标准的Hibernate属性值为none
,validate
,update
,create
和create-drop
。Spring Boot为你选择一个默认值,取决于它认为你的数据库是否是内嵌的。如果没有检测出模式管理器或者在所有情况下为none
,它默认为create-drop
。通过查看Connection
类型和JDBC url检测内嵌的数据库。hsqldb
,h2
和derby
是候选的,其他的则不是。注意从内存数据库转换为真正数据库时,你不要假设在新的平台存在表和数据。你要么显示地设置ddl-auto
要么使用其他机制的一种来初始化数据库。
你可以通过启用
org.hibernate.SQL
日志记录器输出模式创建。如果你启用debug模式自动为你这样做。
除此之外,如果Hibernate从头开始创建模式,启动时,执行在根类路径中名称为import.sql
的文件,(也就是说,如果ddl-auto
属性被设置为create
或者create-drop
)。如果你仔细的话,对于演示和测试这个是非常有用的,但是在生产环境中不希望出现在类路径中。它是Hibernate特性(与Spring无关)。
17.8.3. 使用基本的SQL脚本初始化数据库
Spring Boot可以自动化创建JDBC的DataSource
或者R2DBC的ConnectionFactory
的模式(DDL脚本)并初始化它(DML脚本)。它分别从标准的根类路径位置载入SQL:schema.sql
和data.sql
。除此之外,Spring Boot处理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 Boot启用他的基于脚本数据库初始化器快速失败特性。这表示,如果这个脚本导致异常,这个应用程序启动失败。你可以通过设置spring.sql.init.continue-on-error
调整这个行为。
在任何JPA EntityManagerFactory
被创建之前,默认情况下,执行基于脚本的DataSource
初始化。schema.sql
可以用来为JPA管理的实体创建模式,data.sql
可以用来填充它。尽管我们不建议使用多数据源初始化技术,如果你想基于脚本的DataSource
初始化能够构建在Hibernate提供的模式创作上,设置spring.jpa.defer-datasource-initialization
为true
。这个将推迟数据源初始化直到任何EntityManagerFactory
bean已经被创建和初始化之后。然后,schema.sql
可以被用来向Hibernate提供的任何模式创作添加内容,data.sql
用来填充它。
如果你正在使用高级别数据库迁移工具,像Flyway或者Liquibase,你应该单独使用他们来创建和初始化模式。不建议在Flyway或Liquibase的同时使用基本的schema.sql
和data.sql
脚本。将在未来的版本移除支持。
17.8.4. 初始化一个Spring Batch数据库
如果你使用Spring Batch,它预先打包了用于大多数知名的数据库平台的SQL初始化脚本。在启动时,Spring Boot可以检测你的数据库类型并执行这些脚本。如果你使用一个内嵌的数据库,默认情况就会出现这种情况。你也可以启用用于任何数据库类型,如下示例所示:
spring:
batch:
jdbc:
initialize-schema: "always"
你也可以显示地设置spring.batch.jdbc.initialize-schema
为never
关闭初始化。
17.8.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
目录查找脚本:
spring:
flyway:
locations: "classpath:db/migration,filesystem:/opt/migration"
你也可以添加一个特定的{vendor}
占位符来使用特定的供应商脚本。假设如下:
spring:
flyway:
locations: "classpath:db/migration/{vendor}"
前面的配置设置目录来使用相对应的数据库类型(例如,db/migration/mysql
用于MYSQL),而不是使用db/migration
。在DatabaseDriver
中的支持的数据库列表是可用的。
迁移也可以使用Java书写。使用实现了JavaMigration
的任何bean自动配置Flyway。
FlywayProperties
提供大多数Flyway的设置和可以用来禁用迁移或者关闭位置检查的一小组额外属性。如果你需要在配置之上更多控制,考虑注册一个FlywayConfigurationCustomizer
bean。
Spring Boot调用Flyway.migrate()
来执行数据库迁移。如果你喜欢更多控制,提供实现FlywayMigrationStrategy
的@Bean
。
Flyway支持SQL和Java回调。要使用基于SQL的回调,将回调脚本放入到classpath:db/migration
目录。要使用基于Java回调,创建一个或者多个实现Callback
的bean。使用Flyway
自动化注册任何这样的bean。他们可以通过@Order
或者实现@Ordered
对他们进行排序。实现废弃的FlywayCallback
接口bean也可以被检测。然而,他们不能和Callback
bean一起使用。
默认情况下,在你的上下文中,Flyway自动装配(@Primary
)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
中,你可以指定以下设置:
spring:
flyway:
locations: "classpath:/db/migration,classpath:/dev/db/migration"
使用这个设置,只有当dev
配置文件激活时在dev/db/migration
中的迁移运行。
在启动时执行Liquibase数据库迁移
在启动时,要自动化运行Liquibase数据库迁移,添加org.liquibase:liquibase-core
到你的类路径。
当你将
org.liquibase:liquibase-core
添加到类路径,默认情况下,数据库迁移运行在应用程序启动期间和你测试运行之前。可以使用spring.liquibase.enabled
定制这个行为,在main
和test
配置中设置不同的值。他不能使用两个不同的方式来初始化数据库(例如Liquibase用于应用程序启动,JPA用于测试运行)。
默认情况下,主修改日志从db/chargelog/db.changelog-master.yaml
读取,但是你可以通过设置spring.liquibase.change-log
修改位置。除了YAML之外,Liquibase也支持JSON,XML,SQL修改日志格式。
默认情况下,Liquibase自动注入上下文中的(@Primary
)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
了解关于可用的设置详情,比如上下文,默认的模式和其他。
17.8.6. 依赖于初始化的数据库
当应用程序启动时,数据库初始化作为应用程序刷新的一部分则执行它。在启动期间,为允许访问一个初始化的数据库,将自动检测充当数据库初始化器的bean和需要对数据库进行初始化的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
(除非spring.jpa.defer-datasource-initialization
设置为true
)DSLContext
(jOOQ)EntityManagerFactory
(除非spring.jpa.defer-datasource-initialization
设置为true
)JdbcOperations
NamedParameterJdbcOperations
如果你使用第三方类库启动器数据访问类库,它可能提供一个检测器以便自动检测其他类型的bean。要检测其他bean,在META-INF/spring.factories
注册一个DependsOnDatabaseInitializationDetector
实现。或者,@DependsOnDatabaseInitialization
注解注解这个bean的类或者它的@Bean
方法。
17.9. 消息
Spring Boot提供多个启动器来支持消息。这章节解答由使用Spring Boot使用消息产生的问题。
17.9.1. 禁用事务JMS会话
如果JMS borker不支持事务会话,你必须完全禁用事务的支持。如果你创建你自己的JmsListenerContainerFactory
,无需做任何事情,因为默认情况下,它不能被事务。如果你想使用DefaultJmsListenerContainerFactoryConfigurer
来重用Spring Boot的默认设置,你可以禁用事务会话,如下:
import jakarta.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;
}
}
前面的示例重写了默认的工厂,并且它应该被应用到应用程序定义的任何其他的工厂(如果有的话)。
17.10. 批处理应用程序
当人们从Spring Boot应用程序内使用Spring Batch时,多个问题经常发生。这章节解决这些问题。
17.10.1. 指定批处理数据源
默认情况下,批处理应用程序需要DataSource
来存储作业明细。默认情况下,Spring Batch期望使用一个单独的DataSource
。要用它使用一个DataSource
不同于应用程序的主DataSource
,声明一个DataSource
bean,使用@BatchDataSource
注解它的@Bean
。如果你这样做并想要两个数据源,记得标记另一个为@Promary
。为了获得更大的控制权,实现BatchConfigurer
。请查看@EnableBatchProcessing
的Javadoc了解更多详情。
要了解关于Spring Batch更多信息,请查看Spring Batch项目页面。
17.10.2. 启动时运行Spring Batch 作业
通过添加@EnableBatchProcessing
到@Configuration
类其中一个来启动Spring Batch自动配置。
如果在应用程序上下文中发现单独的Job
,在启动时执行它(请查看JobLauncherApplicationRunner
了解更多详情)。如果发现多个‘Job’,应该被执行的job必须使用spring.batch.job.name
指定。
要在应用程序上下文中禁止发现运行一个Job
,设置spring.batch.job.enabled
为false
。
请查看BatchAutoConfiguration
和@EnableBatchProcessing
了解更多详情。
17.10.3. 在命令行运行
Spring Boot转换任何以--
开头的命令行参数为属性来添加到Environment
,请查看访问命令行属性。不应该使用这个传入参数到批处理作业。要在命令行指定批次参数,使用常规格式(也就是没有--
),如下示例所示:
$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue
如果在命令行你指定了一个Environment
的属性,它会被Job忽略。考虑以下命令行:
$ java -jar myapp.jar --server.port=7070 someParameter=someValue
这个只提供了一个参数到批处理作业:someParameter=someValue
。
17.10.4. 存储Job仓库
Spring Batch需要一个数据库来存储Job
仓库。如果你使用Spring Boot,你必须使用一个真正的数据库。注意,它可以是内存数据库,请查看配置一个Job仓库。
17.11. Actuator
Spring Boot包括Spring Boot Actuator。这章节解答来自它的使用引起的问题。
17.11.1. 修改HTTP端口或者Actuator端点地址
在独立的应用程序中,Actuator HTTP端口默认与主要的HTTP端口相同。要使应用程序监听不同的端口,设置外部属性:management.server.port
。要监听在完全不同的网络地址(例如当你有一个内部网络用于管理并且外部的网络用于用户应用程序),你也可以设置management.server.address
为一个服务器可以绑定的有效的IP地址。
要了解更多细节,请查看ManagementServerProperties
原代码和在生产就绪特性章节中的“定制Management Server Port”。
17.11.2. 定制‘白板’错误页面
如果你遇到一个服务器错误(机器客户端消耗JSON和其他媒体类型应该看到合理的带有错误码的返回),Spring Boot安装一个‘白板’错误页面,可以在浏览器中看到。
设置
server.error.whitelabel.enabled=false
来关闭默认的错误页面。这样做恢复了你正在使用的servlet容器的默认值。注意Spring Boot仍尝试解析错误视图,所以你应该添加你自己的错误页面而不是完全禁用它。
使用自己的覆盖错误页面取决于你使用的模板技术。例如,如果你使用Thymeleaf,你可以添加error.html
模板。如果你使用FreeMarker,你可以添加error.ftlh
模板。通常,你需要一个使用error
名字的解析的View
或者处理/error
路径的@Controller
。除非你替换一些默认配置,你应该在ApplicationContext
中查找BeanNameViewResolver
,所以名称为error
的@Bean
是这样做的一种方式。请查看ErrorMvcAutoConfiguration
了解更多选项。
也可以查看在“错误处理”章节了解如何在servlet容器中注册处理器的细节。
17.11.3. 清洗敏感值
通过env
和configprops
端点返回的信息可能稍微敏感,所以默认情况下,key匹配某些规则是被清洗的(也就是他们的值被*******
替换)。Spring Boot使用合理的默认值用于这些key:以“password”,“secret”,“key”,“token”,“vcap_services”,“sun.java.command”结尾的任何key是全部清洗的。除此之外,持有单词“credentials”做为秘钥部分的任何key也是完全清洗的。
而且,Spring Boot清洗用于key类似URL值的敏感部分,使用以下之一结尾的:
address
addresses
uri
uris
url
urls
使用格式<scheme>://<username>:<password>@<host>:<port>
识别URI的敏感部分 。例如,对应属性myclient.uri=http://user1:password1@localhost: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
配置额外的格式。
要完全控制清洗,定义一个SanitizingFunction
bean。调用该函数的SanitizableData
提供了对键和值以及他们来自的PropertySource的访问。例如,这允许你清洗每一个来自特别的属性源的值。按顺序调用每一个SanitizingFunction
直到一个函数修改敏感数据的值。如果没有函数修改他的值,内嵌的基于key的清洗被执行。
17.11.4. 将监控指标映射到测量仪指标
Spring Boot监控指标返回Status
类型表明全面的系统监控状态。如果你想对特别的应用程序在健康级别进行监控或者提示,你可以导出这些状态作为Micrometer的指标。默认情况下,状态编码“UP”,“DOWN”,"OUT_OF_SERVICE"和“UNKNOWN”被Spring Boot使用。要导出这些,你将需要转换这些状态为几组数字以便他们用于Micrometer 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;
}
}
17.12. 安全
这部分解决关于当使用Spring Boot时安全的问题,包括使用Spring Boot与Spring Security引起的问题。
要了解更多关于Spring Security,请查看Spring Security 项目页面。
17.12.1. 关闭Spring Boot Security配置
如果在应用程序中,你使用WebSecurityConfigurerAdapter
或者SecurityFilterChain
bean定义@Configuration
,它关闭在Spring Boot默认的webapp安全设置。
17.12.2. 修改UserDetailService和添加用户账户
如果你提供类型为AuthenticationManager
,AuthenticationProvider
,UserDetailsService
的@Bean
,对于InMemoryUserDetailsManager
默认的@Bean
不会被创建。这表示你拥有Spring Security的完整特性集(例如各种身份认证选项)。
添加用户最容易的方式是提供你自己的UserDetailsService
bean。
17.12.3. 当运行在代理服务器后时启用HTTPS
对于任何应用程序来说,确保所有的你的主要的端点只在HTTPS上可用是非常重要的累活。如果你使用Tomcat作为servlet容器,如果它检测到一些环境设置,然后Spring Boot自动添加Tomcat自己的RemoteIpValue
,你应该可以依赖HttpServletRequest
来报告他是否安全(甚至处理真正SSL终止的代理服务器的下游)。标准的行为是由请求头(x-forwarded-for
和x-forwarded-proto
)的存在和不存在决定的,他们的名称是常规的,所以它适用于大部分前端代理。你可以通过添加一些条目到application.properties
打开值,如下示例所示:
server:
tomcat:
remoteip:
remote-ip-header: "x-forwarded-for"
protocol-header: "x-forwarded-proto"
(这两种特性中的任何一种都会打开值。或者,你可以通过使用WebServerFactoryCustomizer
bean定制TomcatServletWebServerFactory
添加RemoteIpValue
。)
要配置需要一个对于所有(或者一些)请求安全的频道的Spring Security,考虑添加你自己的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((channel) -> channel.anyRequest().requiresSecure());
return http.build();
}
}
17.13. 热插拔
Spring Boot支持热插拔。这章节解答关于它如何工作的问题。
17.13.1. 重载静态内容
有多个选项用于热重载。推荐方式是使用spring-boot-devtools
,因为它提供了额外的开发时特性,例如支持应用程序快速重启和LiveReload以及合理的开发时配置(例如模板缓存)。Devtools通过监控类路径的变化来工作。这表示必须“构建”静态资源修改以便更改生效。默认情况下,当你保存你的修改时,使用Ecplise自动发生。使用IntelliJ IDEA,Make Project命令触发必须的构建。由于默认的重启排除,修改静态资源不会触发应用程序的重启。然而,他们确实会触发实时重载。
或者,运行在一个IDE(特别是在调试的时候)是比较好的方式开发(所有的现代IDE允许静态资源重载,通常也允许Java类修改的热插拔)。
最终,可以配置(请查看addResources
属性)Maven和Gradle插件来支持命令行运行,并直接从源文件重新加载静态文件。如果你正在使用更高级工具写代码,你可以使用一个外部 css/js编译进程。
17.13.2. 无需重启容器来重载模板
大多数Spring Boot支持的模板技术包含禁用缓存的配置选项(这个文档后面所描述的)。如果你使用spring-boot-devtools
模块,这些属性在开发时为你自动配置。
Thymeleaf模板
如果你使用Thymeleaf,设置spring.thymeleaf.cache
为false
。请查看ThymeleafAutoConfiguration
了解其他Thymeleaf定制选项。
FreeMarker模板
如果你使用FreeMarker,设置spring.freemarker.cache
为false
。请查看FreeMarkerAutoConfiguration
了解其他FreeMarker定制选项。
Groovy模板
如果你使用Groovy模板,设置spring.groovy.template.cache
为false
。请查看GroovyTemplateAutoConfiguration
了解其他Groovy定制选项。
17.13.3. 快速应用程序重启
spring-boot-devtools
模块包含对自动化应用程序重启的支持。虽然没有像JRebel这样的技术那么快,但是它通常比“冷启动”快得多。在研究稍后在本文档讨论的一些更复杂的重载选项之前,你可能应该尝试一下。
要了解更多详情,请查看开发者工具章节。
17.13.4. 无需重启重启重载Java Class
许多现代的IDE(Ecplise,IDEA和其他)通过字节编码支持热插拔。因此,如果你创建了一个不影响类或者方法签名的更改,他应该安静的重载并没有副作用。
17.14. 测试
Spring Boot包含多个测试工具和支持类以及专用的启动器,它提供常用测试依赖项。这章节解答关于测试的常见问题。
17.14.1. 使用Spring Security测试
Spring Security提供对以特定的用户运行测试的支持。例如,在以下片段的测试将使用一个身份授权的用户执行,此用户有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 Security提供与Spring MVC测试全面的测试并且当测试controller使用@WebMvcTest
分片和MockMvc
时也可以使用它。
要了解关于Spring Security的测试支持的额外的详情,请查看Spring Security的参考文档。
17.14.2. 使用Testcontainers用于集成测试
Testcontainers类库提供一种管理运行在Docker容器内的服务的方法。它与JUnit集成,允许你写一个测试类,在任何测试类运行之前,它可以启动一个容器。对于写与真正后端服务(例如 MySQL,MongoDB,Cassandra和其他)交流的集成测试,Testcontainers是特别有用。Testcontainers可以在Spring Boot测试中被使用,如下:
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() {
// ...
}
}
在任何测试运行之前,这将启动一个docker容器运行Neo4j(如果Docker在本地运行)。在大多数情况,你将需要使用来自运行容器的详细信息来配置应用程序,例如容器IP或者端口。
这可以使用静态的@DynamicPropertySource
方法完成,允许添加动态的属性值到Spring环境:
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沟通。
17.14.3. 构造包含在分片测试中的@Configuration
类
分片测试通过限制Spring Framework的组件扫描为基于他们类型的一组受限制组件来工作。对于不是通过组件扫描创建的任何bean,例如,使用@Bean
注解创建的bean,分片测试将不能从应用程序上下文包含/排除他们。考虑这个示例:
import org.apache.commons.dbcp2.BasicDataSource;
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.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@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,以便你可以测试你的controller端点是否是完全安全的。然而,通过@WebMvcTest
的组件扫描过滤器不会获取MyConfiguration
,因为它没有匹配任何通过过滤器指定的类型。你可以通过使用@Import(MyConfiguration.class)
注解测试类显式地包含配置。这将加载MyConfiguraiton
中的所有的bean,包括当测试web层时不需要的BasicDataSource
bean。将配置类分为两个,可以只启用引入安全的配置:
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.authorizeRequests((requests) -> requests.anyRequest().authenticated());
return http.build();
}
}
import org.apache.commons.dbcp2.BasicDataSource;
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.second")
public BasicDataSource secondDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
}
当某个域的bean需要包含在分片测试中时,使用单独的配置类是效率低下。反而,将应用程序的配置结构为多个带有特定域的bean的颗粒类可以仅为特定的分片测试引入他们。
17.15. 构建
Spring Boot包含用于Maven和Gradle的构建插件。这章节解答关于这些插件的常见问题。
17.15.1. 生成构建信息
Maven插件和Gradle插件允许生成包括坐标,名称和项目版本的构建信息。还可以配置插件来通过配置添加额外的属性。当这样的文件存在时,Spring Boot自动配置BuildProperties
bean.
要使用Maven生成构建信息,添加一个build-info
目标的执行,如下示例所示:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.0.0-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
请查看Spring Boot Maven插件文档了解更多信息。
下面的示例使用Gradle做相同的事情:
springBoot {
buildInfo()
}
请查看Spring Boot Gradle插件文档了解更多信息。
17.15.2. 生成Git信息
当构建项目时,Maven和Gradle允许生成一个包含关于git
源代码仓库状态信息的git.properties
文件。
对于Maven用户,spring-boot-starter-parent
POM包含一个预配置的插件来生成一个git.properties
文件。要使用它,添加以下用于Git Commit Id Plugin
的声明到你的POM:
<build>
<plugins>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Gradle用户可以使用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
。这个是默认格式用于上面所列的插件。使用这个格式允许时间被解析为Date
和当被序列化为JSON时,他的格式可以通过Jackson的时间序列化配置设置控制。
17.15.3. 定制依赖项版本
spring-boot-dependencies
POM管理常用的依赖项版本。用于Maven和Gradle的Spring Boot插件允许使用构造属性定制这些托管版本依赖。
每一个Spring Boot发行都会针对特性第三方依赖集设计和测试。覆盖版本可能导致兼容性问题。
要使用Maven覆盖依赖版本,请查看Maven插件文档部分。
要使用Gradle覆盖依赖版本,请查看Gradle插件文档部分。
17.15.4. 使用Maven创建可执行JAR
spring-boot-maven-plugin
可以用来创建可执行"fat" JAR。如果你使用spring-boot-starter-parent
POM, 你可以声明这个插件和你要打包的jar,如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果你不使用parent POM,你仍可以使用这个插件。然而,你必须额外添加<executions>
部分,如下:
<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.15.5. 使用Spring Boot应用程序作为一个依赖项
像是war文件,Spring Boot应用程序不打算用来作为一个依赖。如果你的应用程序包含你想要和其他项目共享的类,推荐的方法是异动该代码到一个单独的模块。你的应用程序和其他项目可以依赖单独的模块。
如果你不能按照上面的建议重新整理你的代码,Spring Boot的Maven和Gradle插件就必须配置为生成适用作为依赖的单独的artifact。可执行的归档不能用作一个依赖项因为可执行jar格式的包应用程序类在BOOT-INF/classes
中。这表示当可执行jar被用来当做一个依赖项时,不能找到他们。
要生产两个artifact,一个可以用来当做依赖项,另一个可执行,必须指定一个分类器。这个分类器被应用到可执行归档的名称,保留默认的归档作为一个依赖项使用:
要使用Maven配置exec
的分类器,你可以使用以下配置:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
17.15.6. 当可执行jar运行时提取特定的类库
在可执行jar中大多数内嵌的类库不需要为了执行被解压。然而,某个类库可能有问题。例如,JRuby包含它自己的内嵌jar支持,它假设jruby-complete.jar
总是作为一个文件直接可用。
要处理任何有问题的类库,当可执行jar第一次运行,你可以标志特定的内嵌的jar应该自动解压。这样的内嵌jar写入到通过jar.io.temdir
系统属性识别临时目录之下。
应该小心保证你的操作系统被配置,以便当应用程序仍在运行时,它不会删除已经解压到临时目录的jar。
例如,要表示JRuby应该被标记为通过使用Maven Plugin来解压,你应该添加以下配置:
<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.15.7. 使用exclusion创建一个非可执行的JAR
通常,如果你有一个可执行的和一个非可执行的jar作为两个单独的构建产品,可执行版本有额外的配置文件,这些配置文件在类库jar中是不需要的。例如,application.yml
配置文件可能从非可执行jar中排除。
在Maven中, 可执行jar必须是主actifact并且你可以为类库添加一个归类jar,如下:
<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.15.8. 使用Maven远程调试已启动的Spring Boot应用程序
为将一个远程的调试器附属到使用Maven启动的Spring Boot应用程序,你可以使用maven插件的jvmArguments
属性。
请查看这个示例了解更多详情。
17.15.9. 从不使用spring-boot-antlib的Ant构建一个可执行的归档
要使用Ant构建,你需要抓取依赖项,编译,然后创建一个jar或者war归档。要使它可执行,你可以使用spring-boot-antlib
模块或者你可以遵循这些指令:
- 如果你构建一个jar,打包应用程序类和资源到内嵌的
BOOT-INF/classes
目录。如果你在构建一个war,通常打包应用程序的类到内嵌的WEB-INF/classes
目录。 - 添加运行时依赖项到在一个jar内嵌的
BOOT-INF/lib
目录或者一个jar内嵌的WEB-INF/lib
。记住不要压缩条目到归档中。 - 添加
provided
(内嵌容器)依赖项到一个jar内嵌的BOOT-INF/lib
目录或者war中WEB-INF/lib-provided
。记住不要压缩条目到归档中。 - 在归档根目录添加
spring-boot-loader
类(以便Main-Class
可用)。 - 使用合适的启动器(例如用于jar文件的
JarLauncher
)做为在清单中的Main-Class
属性,并指定它需要的其他属性作为清单条目–主要是,通过设置Start-Class
属性。
以下示例展示使用Ant如何构建一个可执行归档:
<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.16. 传统部署
Spring Boot支持传统部署以及更现代的部署形式。这章节解答关于传统部署的常见问题
17.16.1. 创建一个可部署的war文件
因为Spring WebFlux不是严格依赖servlet API并且应用程序默认情况下被部署到内嵌的Reactor Netty服务器上,War部署不支持WebFlux应用程序。
生成一个可部署的war文件第一步是提供一个SpringBootServletInitializer
子类并重写configure
方法。这样做当通过servlet容器启动时,利用Spring Framework的 servlet 3.0 支持并允许你配置你的应用程序。通常,你应该修改你的应用程序的main类为继续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
文件修改parckaging为war,如下:
<packaging>war</packaging>
如果你使用Gradle,你需要修改build.gradle
为应该war插件到这个项目,如下:
apply plugin: 'war'
过程的最后一步是确保内嵌的servlet容器不干扰war文件部署到servlet容器。为此,你需要内嵌servlet容器依赖项标记为已提供。
如果你使用Maven,以下示例将servlet容器(Tomcat,在这个情况)标记为已提供:
<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'
// ...
}
provideRuntime
优先于Gradle的compileOnly
配置。在其他限制中,compileOnly
依赖项不在测试类路径下,所以任何基于web的集成测试将失败。
如果你使用Spring Boot构建工具,将内嵌的servlet容器依赖项标记为已提供,将生成一个可执行的war文件,其中所提供的依赖项打包在lib-provided
目录中。这表示,除了可发布到servlet容器,你也可以在命令行中使用java -jar
运行你的应用程序。
17.16.2. 转换已存在的应用程序为Spring Boot
要转换已存在的非webSpring 应用程序为Spring Boot应用程序,替换创建你的ApplicationContext
代码,并将其替换为调用SpringApplication
或者SpringApplicationBuilder
。Spring MVC web应用程序通常有责任优先创建一个可部署的war应用程序,然后在将他迁移到一个可执行的war或者jar。请查看关于转换一个jar为war的入门指南。
通过继承SpringBootServletInitializer
(例如,在一个称为Application
的类中)要创建一个可部署的war并添加Spring Boot @SpringBootApplication
注解,使用类似于以下示例展示的代码:
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为他们提供它自己的默认值,但是在你需要这样做之前,应该可以让某些东西工作起来。
静态资源可以被移动到根目录下的/public
(或者/static
或者/resources
或者/META-INF/resources
)。这同样使用于messages.properties
(在根类路径Spring Boot自动检测)。
Spring DispatcherServlet
的原始用法和Spring Security不需要进一步修改。如果在应用程序中你有其他特性(例如,使用其他servlet或者过滤器),你可能需要添加其他配置到你的Application
上下文,通过替换来自web.xml
的这些元素,如下:
- 类型
Servlet
或者SrvletRegistrationBean
的@Bean
安装将该bean安装到容器中,就像它是在web.xml
中的一个<serlvet/>
和<servlet-mapping/>
。 - 类型
Filter
或者FilterRegistrationBean
的@Bean
表现类似于<filter />
和<filter-mapping />
。 - 在XML文件中的
ApplicationContext
可以通过@ImportResource
被添加到你的Application
。或者,对于已经大量使用注解配置的地方,可以在几行中作为@Bean
定义重新创建。
一旦war文件已经工作,你可以通过添加main
方法到你的Application
使他可执行,如下示例所示:
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
如果你打算你的应用程序作为war或者可执行的应用程序启动,你需要在一个方法中共享构造器的定制,这个方法对于
SpringBootServletInitializer
回调和在一个类的中的main
方法同时可用,类似于以下:import org.springframework.boot.Banner; 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 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
的应用程序 - 使用上下文分层的应用程序
- 没有上下文分层的应用程序
所有的这些都应该便于转化,但是每一个可能需要略有不同的技术。
如果他们已经使用Spring Servlet 3.0+ 初始化器支持类,Servlet 3.0+ 应用程序可能非常容易转化。平常,来自已存在的WebApplicationInitializer
所有的代码可以移动到SpringBootServletInitializer
。如果已存在的应用程序存在超过一个ApplicationContext
(例如,如果它使用AbstractDispatcherServletInitializer
)然后你可能可以将所有你的上下文的源组合到一个单独的SpringApplication
。你可能遭遇的主要复杂的情况是如果组合不起作用,你需要维护上下层次结构。请查看构建层次结构条目的示例。已存在的包含特定的web特性的父上下文通常需要分解,以便所有的ServletContextAware
组件在子上下文中。
还不是Spring应用程序可以转换为Spring Boot的应用程序,之前提到的指引可能有帮助。然而,你也可能遇到问题。在这种情况,我们建议在Stack Overflow问一个带有spring-boot
标签的问题。
17.16.3. 将一个WAR部署到WebLogic
要将一个Spring Boot应用程序部署到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 {
}
如果你使用Logback,你也需要告诉WebLogic选择打包的版本,而不是使用服务器预安装的版本。你可以通过添加WEB-INF/weblogic.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>