本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
本节为一些在使用Spring Boot时经常出现的 "如何做…… " 问题提供答案。 它的覆盖面并不全面,但确实涵盖了相当多的内容。
如果你有一个特定的问题,而我们在这里没有涉及,你可能想检查一下 stackoverflow.com ,看看是否有人已经提供了答案。这也是一个提出新问题的好地方(请使用 spring-boot
标签)。
我们也非常乐意扩展这个部分。如果你想添加一个 ‘how-to’,请给我们发送一个 pull request。
1. Spring Boot 应用
本节包括与Spring Boot应用程序直接相关的主题。
1.1. 创建你自己的故障分析器(FailureAnalyzer)
FailureAnalyzer
是一种很好的方法,可以在启动时拦截异常,并将其转化为人类可读的消息,并将其包裹在 FailureAnalysis
中。Spring Boot为应用 application context 相关的异常、JSR-303验证等提供了这样一个analyzer。你也可以创建你自己的。
AbstractFailureAnalyzer
是 FailureAnalyzer
的一个方便的扩展,它可以检查要处理的异常中是否有指定的异常类型。
你可以在此基础上进行扩展,这样你的实现只有在实际存在的情况下才有机会处理该异常。
如果由于某种原因,你不能处理这个异常,那么返回 null
,让其他实现有机会处理这个异常。
FailureAnalyzer
的实现必须在 META-INF/spring.factories
中注册。
下面的例子注册了 ProjectConstraintViolationFailureAnalyzer
。
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
如果你需要访问 BeanFactory 或 Environment ,你的 FailureAnalyzer 可以分别实现 BeanFactoryAware 或 EnvironmentAware 。
|
1.2. 排除“自动配置”的故障
Spring Boot的自动配置尽力 "做正确的事",但有时会失败,而且很难说清原因。
在任何Spring Boot ApplicationContext
中都有一个非常有用的 ConditionEvaluationReport
。如果你启用 DEBUG日
志输出,你就可以看到它。如果你使用 spring-boot-actuator
(见Actuator章节),也有一个 conditions
端点,可以将报告以JSON格式呈现出来。使用该端点来调试应用程序,看看Spring Boot在运行时添加了哪些功能(以及哪些没有添加)。
还有很多问题可以通过查看源代码和Javadoc来回答。 在阅读代码时,请记住以下经验法则。
-
寻找名为
*AutoConfiguration
的类,并阅读其来源。 特别注意@Conditional*
注解,找出它们启用的功能和时间。 在命令行中添加--debug
或系统属性(System property)-Ddebug
,以便在控制台中获得所有在你的应用程序中做出的自动配置决定的日志。 在一个启用了执行器的运行中的应用程序中,查看conditions
端点(/actuator/conditions
或相当于JMX)以获得相同的信息。 -
寻找属于
@ConfigurationProperties
的类(ServerProperties
),并从那里读取可用的外部配置选项。@ConfigurationProperties
注解有一个name
属性,作为外部属性的前缀。 因此,ServerProperties
有prefix="server"
,它的配置属性是server.port
,server.address
,以及其他。 在一个启用了actuator的运行中的应用程序中,查看configprops
端点。 -
寻找
Binder
上的bind
方法的用途,以轻松的方式从Environment
中明确提取配置值。 它经常与前缀一起使用。 -
寻找直接与
Environment
绑定的@Value
注解。 -
寻找
@ConditionalOnExpression
注解,该注解在响应SpEL表达式时打开或关闭功能,通常用从Environment
中解析的占位符进行评估。
1.3. 在启动前定制环境(Environment)或应用上下文(ApplicationContext)
一个 SpringApplication
有 ApplicationListeners
和 ApplicationContextInitializers
,用于对上下文或环境进行定制。
Spring Boot从 META-INF/spring.factories
中加载了许多这样的定制,供内部使用。
有不止一种方法可以注册额外的定制。
-
在运行之前,通过调用
SpringApplication
上的addListeners
和addInitializers
方法,以编程的方式对每个application进行操作。 -
通过设置
context.initializer.classes
或context.listener.classes
属性,以声明的方式,在每个application中。 -
通过添加
META-INF/spring.factories
和打包一个jar文件,为所有的application声明性地提供一个库。
SpringApplication
向监听器发送一些特殊的 ApplicationEvents
(有些甚至在context创建之前),然后为 ApplicationContext
发布的事件也注册监听器。完整的列表见 "Spring Boot特性" 部分的 “Application Event 和监听器”。
也可以通过使用 EnvironmentPostProcessor
在应用上下文刷新之前定制 Environment
。
每个实现都应该在 META-INF/spring.factories
中注册,如以下例子所示。
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor
实现可以加载任意文件并将其添加到 Environment
中。
例如,下面的例子从classpath加载一个YAML配置文件。
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);
}
}
}
class MyEnvironmentPostProcessor : EnvironmentPostProcessor {
private val loader = YamlPropertySourceLoader()
override fun postProcessEnvironment(environment: ConfigurableEnvironment, application: SpringApplication) {
val path: Resource = ClassPathResource("com/example/myapp/config.yml")
val propertySource = loadYaml(path)
environment.propertySources.addLast(propertySource)
}
private fun loadYaml(path: Resource): PropertySource<*> {
Assert.isTrue(path.exists()) { "Resource $path does not exist" }
return try {
loader.load("custom-resource", path)[0]
} catch (ex: IOException) {
throw IllegalStateException("Failed to load yaml configuration from $path", ex)
}
}
}
Environment 已经准备好了Spring Boot默认加载的所有常用属性源。
因此,可以从environment中获取文件的位置。
前面的例子将 custom-resource 属性源加在了列表的最后,这样一来,在任何一个通常的其他位置定义的键就会被优先考虑。
一个自定义的实现可以定义另一个顺序。
|
虽然在你的 @SpringBootApplication 上使用 @PropertySource 似乎是在 Environment 中加载自定义资源的一种便捷方式,但我们并不推荐这样做。
这样的属性源直到应用上下文被刷新时才会被添加到 Environment 中。
这对于配置某些属性来说已经太晚了,例如 logging.* 和 spring.main.* ,这些属性在刷新开始前就已经被读取。
|
1.4. 建立一个有层次结构的ApplicationContext(添加一个父级或根级Context)
你可以使用 ApplicationBuilder
类来创建父/子 ApplicationContext
层次结构。参见 "Spring Boot特性 "部分的 “features.html” 以了解更多信息。
1.5. 创建一个非WEB应用程序
并非所有的Spring应用都必须是WEB应用(或WEB服务)。如果你想在一个 main
方法中执行一些代码,但同时也要启动一个Spring应用来设置使用的基础设施,你可以使用Spring Boot的 SpringApplication
功能。一个SpringApplication会改变它的 ApplicationContext
类,这取决于它是否认为它需要一个Web应用程序。你可以做的第一件事是帮助它把与服务器相关的依赖(如servlet API)从classpath中移除。如果你不能这样做(例如,你从同一个代码库中运行两个应用程序),那么你可以明确地在 SpringApplication
实例上调用 setWebApplicationType(WebApplicationType.NONE)
或者设置 applicationContextClass
属性(通过Java API或者使用外部属性)。你想作为业务逻辑运行的应用程序代码可以作为 CommandLineRunner
实现,并作为 @Bean
定义丢入上Context。
2. 属性(Properties)和配置(Configuration)
本节包括关于设置和读取属性和配置设置以及它们与Spring Boot应用程序的互动的主题。
2.1. 在构建时自动扩展属性
与其硬编码一些在项目构建配置中也被指定的属性,不如使用现有的构建配置来自动扩展它们。 这在Maven和Gradle中都可以实现。
2.1.1. 使用Maven的自动属性扩展
你可以通过使用 resource filtering 从Maven项目中自动扩展属性。
如果你使用 spring-boot-starter-parent
,你就可以用 @..@
占位符来引用你的Maven ‘project properties’,如下例所示。
app.encoding=@project.build.sourceEncoding@
app.java.version=@java.version@
app:
encoding: "@project.build.sourceEncoding@"
java:
version: "@java.version@"
只有生产配置是这样被过滤的(换句话说,在 src/test/resources 上没有应用过滤)。
|
如果你启用 addResources 标志,spring-boot:run 目标可以直接将 src/main/resources 添加到classpath(用于热重载)。
这样做规避了 resource filtering 和这个功能。
相反,你可以使用 exec:java goal或自定义插件的配置。
更多细节请参见 插件使用页面。
|
如果你不使用 parent starter,你需要在你的 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 ,这些可能会被构建时扩展。
|
2.1.2. 使用Gradle的自动属性扩展
你可以通过配置Java插件的 processResources
task 来自动展开Gradle项目的属性,如下例所示。
tasks.named('processResources') {
expand(project.properties)
}
然后你可以通过使用占位符来引用你的Gradle项目的属性,如下面的例子所示。
app.name=${name}
app.description=${description}
app:
name: "${name}"
description: "${description}"
Gradle的 expand 方法使用Groovy的 SimpleTemplateEngine ,它可以转换 ${..} 的标记。
${..} 风格与Spring自己的属性占位符机制相冲突。
要将Spring的属性占位符与自动扩展一起使用,请将Spring的属性占位符转义如下。\${..} .
|
2.2. 将SpringApplication的配置外部化
SpringApplication
有bean属性setter,所以你可以在创建应用程序时使用其Java API来修改其行为。
另外,你也可以通过在 spring.main.*
中设置属性来外部化配置。
例如,在 application.properties
中,你可以有以下设置。
spring.main.web-application-type=none
spring.main.banner-mode=off
spring:
main:
web-application-type: "none"
banner-mode: "off"
那么Spring Boot的Banner就不会在启动时被打印出来,而且应用程序也不会启动嵌入式Web服务器。
外部配置中定义的属性覆盖并取代了用Java API指定的值,但 primary sources 是个明显的例外。
primary sources 是提供给 SpringApplication
构造函数的来源。
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
@SpringBootApplication
object MyApplication {
@JvmStatic
fun main(args: Array<String>) {
val application = SpringApplication(MyApplication::class.java)
application.setBannerMode(Banner.Mode.OFF)
application.run(*args)
}
}
或者 SpringApplicationBuilder
的 sources(…)
方法。
public class MyApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.sources(MyApplication.class)
.run(args);
}
}
object MyApplication {
@JvmStatic
fun main(args: Array<String>) {
SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.sources(MyApplication::class.java)
.run(*args)
}
}
鉴于上述例子,如果我们有以下配置。
spring.main.sources=com.example.MyDatabaseConfig,com.example.MyJmsConfig
spring.main.banner-mode=console
spring:
main:
sources: "com.example.MyDatabaseConfig,com.example.MyJmsConfig"
banner-mode: "console"
实际 application 将显示 banner(如配置所覆盖),并使用三个来源的 ApplicationContext
。
应用程序的来源是。
-
MyApplication
(来自代码) -
MyDatabaseConfig
(来自扩展配置) -
MyJmsConfig
(来自扩展配置)
2.3. 改变应用程序的外部属性的位置
默认情况下,来自不同来源的属性会按照定义的顺序添加到Spring Environment
中(具体顺序见 “Spring Boot特性的” 中的 “features.html” 部分)。
你也可以提供以下System properties(或环境变量)来改变行为。
-
spring.config.name
(SPRING_CONFIG_NAME
)。默认为应用程序作为文件名的root。 -
spring.config.location
(SPRING_CONFIG_LOCATION
)。要加载的文件(如classpath资源或URL)。 为该文件设置了一个单独的Environment
属性源,它可以被system properties、环境变量或命令行覆盖。
无论你在environment中设置了什么,Spring Boot总是按照上述方法加载 application.properties
。
默认情况下,如果使用YAML,那么扩展名为 ‘.yml’,‘.yaml’ 的文件也会被添加到列表中。
如果你想了解正在加载的文件的详细信息,你可以将 org.springframework.boot.context.config 的日志级别设置为 trace 。
|
2.4. 使用 "简短" 的命令行参数
有些人喜欢使用(例如)--port=9000
而不是 --server.port=9000
来在命令行上设置配置属性。你可以通过在 application.properties
中使用占位符来启用这种行为,如下面的例子所示。
server.port=${port:8080}
server:
port: "${port:8080}"
如果你继承了 spring-boot-starter-parent POM, maven-resources-plugins 的默认过滤标记已从 ${*} 改为 @ (即 @maven.token@ 而不是 ${maven.token} ),以防止与Spring风格的占位符冲突。如果你直接为 application.properties 启用了 Maven filtering 功能,你可能还想改变默认的过滤 token,使用 其他分隔符。
|
在这种特定情况下,端口绑定在 Heroku 或 Cloud Foundry 这样的PaaS环境中工作。
在这两个平台中,PORT 环境变量是自动设置的,Spring可以与 Environment 属性的大写同义词进行绑定。
|
2.5. 对外部属性使用YAML
YAML是JSON的超集,因此,它是一种方便的语法,用于以分层格式存储外部属性,如下例所示。
spring:
application:
name: "cruncher"
datasource:
driver-class-name: "com.mysql.jdbc.Driver"
url: "jdbc:mysql://localhost/test"
server:
port: 9000
创建一个名为 application.yaml
的文件,并将其放在classpath的根目录中。
然后将 snakeyaml
添加到你的依赖项中(Maven 座标 org.yaml:snakeyaml
,如果你使用 spring-boot-starter
就已经包含了)。
YAML文件被解析为Java的 Map<String,Object>
(就像JSON对象),Spring Boot会将该Map扁平化,使其只有一层,并且有句点分隔的键,就像许多人习惯于使用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特性" 部分的 “features.html” ,了解更多YAML信息。
2.6. 设置活动的 Spring Profile
Spring Environment
有这方面的API,但你通常会设置一个系统属性(spring.profiles.active
)或一个操作系统环境变量(SPRING_PROFILES_ACTIVE
)。另外,你可以用 -D
参数来启动你的应用程序(记得把它放在main类或jar存档之前),如下所示。
$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
在Spring Boot中,你也可以在 application.properties
中设置活动配置文件,如下例所示。
spring.profiles.active=production
spring:
profiles:
active: "production"
以这种方式设置的值会被系统属性或环境变量设置取代,但不会被 SpringApplicationBuilder.profiles()
方法取代。
因此,后者的Java API可以用来增加配置文件而不改变默认值。
更多信息见 "Spring Boot特性" 部分的 “features.html” 。
2.7. 设置默认配置文件(Profile)名称
默认配置文件是在没有激活配置文件的情况下启用的配置文件。
默认情况下,默认配置文件的名称是 default
,但可以使用系统属性(spring.profiles.default
)或操作系统环境变量(SPRING_PROFILES_DEFAULT
)来改变它。
在Spring Boot中,你也可以在 application.properties
中设置默认的配置文件名称,如下例所示。
spring.profiles.default=dev
spring:
profiles:
default: "dev"
更多信息见 "Spring Boot特性" 部分的 “features.html”。
2.8. 根据环境改变配置
Spring Boot支持多文档YAML和Properties文件(详见 features.html),可以根据活动配置文件有条件地激活。
如果一个文档包含 spring.config.activate.on-profile
key,那么 profiles
值(逗号分隔的profile列表或profile表达式)被送入 Environment.acceptsProfiles()
方法。如果配置文件表达式匹配,那么该文档就会被包含在最终的合并中(否则就不包含),如下面的例子所示。
server.port=9000
#---
spring.config.activate.on-profile=development
server.port=9001
#---
spring.config.activate.on-profile=production
server.port=0
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 处于活动状态,那么端口是9001。 如果 ‘production’ 处于活动状态,那么端口就是0。
文件是按照遇到的顺序合并的。 后面的值覆盖前面的值。 |
2.9. 发现外部属性的内置选项
Spring Boot在运行时将来自 application.properties
(或 YAML 文件和其他地方)的外部属性绑定到应用程序中。
这里没有(技术上也不可能有)一个单一位置的所有支持属性的详尽列表,因为贡献可能来自你classpath上的其他jar文件。
一个运行中的具有Actuator功能的应用程序有一个 configprops
端点,显示所有通过 @ConfigurationProperties
可用的绑定和可绑定的属性。
附录中包括一个 application.properties
的例子,列出了Spring Boot支持的最常见的属性。确定的列表来自于搜索源代码中的 @ConfigurationProperties
和 @Value
注解,以及偶尔使用的Binder。关于加载属性的确切顺序,请参阅 "features.html"。
3. 嵌入式WEB服务器
每个Spring Boot Web应用程序都包括一个嵌入式Web服务器。 这一特性导致了一些操作问题,包括如何改变嵌入式服务器以及如何配置嵌入式服务器。 本节将回答这些问题。
3.1. 使用其他的Web服务器
许多Spring Boot starter 包括默认的嵌入式容器。
-
对于 servlet stack 应用程序,
spring-boot-starter-web
通过包含spring-boot-starter-tomcat
来包含Tomcat,但是你可以使用spring-boot-starter-jetty
或spring-boot-starter-undertow
来代替。 -
对于 reactive stack 应用程序,
spring-boot-starter-webflux
通过包括spring-boot-starter-reactor-netty
包括Reactor Netty,但你可以使用spring-boot-starter-tomcat
,spring-boot-starter-jetty
,或spring-boot-starter-undertow
代替。
当切换到不同的HTTP服务器时,你需要把默认的依赖关系换成你需要的那些。 为了帮助完成这一过程,Spring Boot为每个支持的HTTP服务器提供了一个单独的starter。
下面的Maven示例展示了如何为Spring MVC排除Tomcat并包含Jetty。
<properties>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除对Tomcat依赖 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用 Jetty 代替 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
servlet API的版本已被覆盖,因为与Tomcat 9和Undertow 2不同,Jetty 9.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 starter之外,还需要排除一些Jetty9特有的依赖关系。
下面的Gradle例子配置了必要的依赖关系和 模块替换,以便在Spring WebFlux中使用Undertow代替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")
}
}
}
spring-boot-starter-reactor-netty 需要使用 WebClient 类,所以你可能需要保持对Netty的依赖,即使你需要包含一个不同的HTTP服务器。
|
3.2. 禁用Web服务器
如果你的classpath包含启动Web服务器的必要位,Spring Boot将自动启动它。
要禁用这种行为,请在你的 application.properties
中配置 WebApplicationType
,如以下例子所示。
spring.main.web-application-type=none
spring:
main:
web-application-type: "none"
3.3. 修改HTTP端口
在一个独立的应用程序中,主要的HTTP端口默认为 8080
,但可以用 server.port
来设置(例如,在 application.properties
中或作为一个系统属性)。
由于放松了对 Environment
值的绑定,你也可以使用 SERVER_PORT
(例如,作为一个操作系统环境变量)。
要完全关闭HTTP端点,但仍然创建一个 WebApplicationContext
,使用 server.port=-1
(这样做有时对测试很有用)。
更多细节,请参阅 "Spring Boot特性" 部分的 “web.html”,或 ServerProperties
源代码。
3.5. 在运行时发现HTTP端口
你可以从日志输出或从 WebServerApplicationContext
中通过其 WebServer
访问服务器正在运行的端口。
获得该信息并确定其已被初始化的最好方法是添加一个 @Bean
类型的 ApplicationListener<WebServerInitializedEvent>
并在事件发布时将容器拉出。
使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
的测试也可以通过使用 @LocalServerPort
注解将实际端口注入一个字段,如下例所示。
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {
@LocalServerPort
int port;
// ...
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class MyWebIntegrationTests {
@LocalServerPort
var port = 0
// ...
}
|
3.6. 启用HTTP响应压缩
Jetty、Tomcat、Reactor Netty和Undertow都支持HTTP响应压缩。
它可以在 application.properties
中启用,如下所示。
server.compression.enabled=true
server:
compression:
enabled: true
默认情况下,响应必须至少有2048字节的长度才能进行压缩。
你可以通过设置 server.compression.min-response-size
属性来配置这种行为。
默认情况下,只有当响应的内容类型是下列之一时才会被压缩。
-
text/html
-
text/xml
-
text/plain
-
text/css
-
text/javascript
-
application/javascript
-
application/json
-
application/xml
你可以通过设置 server.compression.mime-types
属性来配置这种行为。
3.7. 配置 SSL
SSL可以通过设置各种 server.ssl.*
属性来声明性地配置,通常在 application.properties
或 application.yaml
中。
下面的例子显示了使用Java KeyStore文件设置SSL属性。
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret
server:
port: 8443
ssl:
key-store: "classpath:keystore.jks"
key-store-password: "secret"
key-password: "another-secret"
下面的例子显示了使用PEM编码的证书和私钥文件设置SSL属性。
server.port=8443
server.ssl.certificate=classpath:my-cert.crt
server.ssl.certificate-private-key=classpath:my-cert.key
server.ssl.trust-certificate=classpath:ca-cert.crt
server:
port: 8443
ssl:
certificate: "classpath:my-cert.crt"
certificate-private-key: "classpath:my-cert.key"
trust-certificate: "classpath:ca-cert.crt"
另外,SSL信任材料可以配置在一个 SSL bundle 中,并应用到Web服务器,如本例所示:
server.port=8443
server.ssl.bundle=example
server:
port: 8443
ssl:
bundle: "example"
关于所有支持的属性的细节,请参见 Ssl
。
使用像前面例子那样的配置意味着应用程序不再支持8080端口的普通HTTP连接器。
Spring Boot不支持通过 application.properties
同时配置HTTP连接器和HTTPS连接器。
如果你想同时拥有,你需要以编程方式配置其中一个。
我们建议使用 application.properties
来配置HTTPS,因为HTTP连接器是两者中比较容易编程配置的。
3.8. 配置 HTTP/2
你可以通过 server.http2.enabled
配置属性在你的Spring Boot应用程序中启用HTTP/2支持。h2
(HTTP/2 over TLS)和 h2c
(HTTP/2 over TCP)都被支持。要使用 h2
,必须同时启用SSL。当SSL未被启用时,将使用 h2c
。例如,当你的应用程序在一个执行 TLS termination的代理服务器后面运行时,你可能想使用 h2c
。
3.8.1. Tomcat 和 HTTP/2
Spring Boot 默认使用 Tomcat 10.1.x,它支持 h2c
和 h2
。
另外,如果主机操作系统上安装了库和其依赖项,你可以使用 libtcnative
来支持 h2
。
库目录必须被提供给JVM的库路径,如果还没有的话。你可以通过JVM参数来做到这一点,如 -Djava.library.path=/usr/local/opt/tomcat-native/lib
。更多信息请见 官方Tomcat文档。
3.8.2. Jetty 和 HTTP/2
为了支持HTTP/2,Jetty需要额外的 org.eclipse.jetty.http2:http2-server
依赖项。
要使用 h2c
不需要其他依赖。
要使用 h2
,你还需要根据你的部署选择以下依赖项之一。
-
org.eclipse.jetty:jetty-alpn-java-server
以使用JDK内置支持。 -
org.eclipse.jetty:jetty-alpn-conscrypt-server
和 Conscrypt库
3.8.3. Reactor Netty 和 HTTP/2
spring-boot-webflux-starter
默认使用 Reactor Netty 作为服务器。
Reactor Netty支持 h2c
和 h2
,开箱即用。
为了获得最佳的运行性能,该服务器还支持带有本地库的 h2
。
Spring Boot为 io.netty:netty-tcnative-boringssl-static
"uber jar" 管理版本,包含所有平台的本地库。开发者可以选择使用 classifier 只导入所需的依赖项(见 Netty官方文档)。
3.9. 配置Web服务器
一般来说,你应该首先考虑使用许多可用的配置key之一,并通过在 application.properties
或 application.yaml
文件中添加新条目来定制你的Web服务器。(见 “发现外部属性的内置选项”)。server.*
命名空间在这里相当有用,它包括 server.tomcat.*
、server.jetty.*
等命名空间,用于服务器的特定功能。参见 application-properties.html 的列表。
前面的章节已经涵盖了许多常见的用例,如压缩、SSL或HTTP/2。
然而,如果你的用例不存在配置密钥,那么你应该看看 WebServerFactoryCustomizer
。
你可以声明这样一个组件,并获得与你的选择相关的服务器工厂的访问权:你应该为所选择的服务器(Tomcat, Jetty, Reactor Netty, Undertow)和所选择的Web栈(servlet或reactive)选择变量。
下面的例子是针对Tomcat的 spring-boot-starter-web
(servlet stack)。
@Component
public class MyTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}
@Component
class MyTomcatWebServerCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory?> {
override fun customize(factory: TomcatServletWebServerFactory?) {
// customize the factory here
}
}
Spring Boot在内部使用该基础设施来自动配置服务器。
自动配置的 WebServerFactoryCustomizer Bean的顺序为 0 ,将在任何用户定义的自定义器之前被处理,除非它有一个明确的顺序说明。
|
一旦你使用定制器获得了对 WebServerFactory
的访问权,你就可以用它来配置特定的部分,如连接器、服务器资源或服务器本身—所有这些都使用服务器特定的API。
此外,Spring Boot还提供了。
Server | Servlet stack | Reactive stack |
---|---|---|
Tomcat |
|
|
Jetty |
|
|
Undertow |
|
|
Reactor |
N/A |
|
作为最后的手段,你也可以声明你自己的 WebServerFactory
bean,它将覆盖Spring Boot提供的那个。
当你这样做的时候,自动配置的自定义器仍然会应用在你的自定义工厂上,所以要小心使用这个选项。
3.10. 添加 Servlet、 Filter、 Listener 到应用
在servlet stack应用程序中,也就是使用 spring-boot-starter-web
,有两种方法可以将 Servlet
、Filter
、ServletContextListener
以及Servlet API支持的其他监听器添加到你的应用程序。
3.10.1. 使用 Spring Bean 添加 Servlet、Filter、Listener
要通过使用Spring Bean来添加 Servlet
、Filter
或Servlet *Listener
,你必须为它提供一个 @Bean
定义。当你想注入配置或依赖时,这样做会非常有用。然而,你必须非常小心,不要让它们导致太多其他Bean的急切初始化,因为它们必须在应用生命周期的早期安装在容器中。(例如,让它们依赖你的 DataSource
或 JPA configuration 并不是一个好主意)。你可以通过在第一次使用时懒加载地初始化Bean而不是在初始化时初始化来解决这些限制。
在 Filter 和 Servlet 的情况下,你也可以通过添加 FilterRegistrationBean
或 ServletRegistrationBean
来添加映射和初始参数,代替或补充底层组件。
如果在filter注册中没有指定 |
像其他Spring Bean一样,你可以定义servlet过滤器bean的顺序;请确保检查 “web.html” 部分。
禁用注册一个Servlet或Filter
如 前所述,任何 Servlet
或 Filter
Bean都会自动在Servlet容器中注册。要禁用某个 Filter
或 Servlet
Bean的注册,请为其创建一个注册Bean,并将其标记为禁用,如下面的例子中所示。
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
@Bean
public FilterRegistrationBean<MyFilter> registration(MyFilter filter) {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(filter);
registration.setEnabled(false);
return registration;
}
}
@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {
@Bean
fun registration(filter: MyFilter): FilterRegistrationBean<MyFilter> {
val registration = FilterRegistrationBean(filter)
registration.isEnabled = false
return registration
}
}
3.11. 配置访问日志
可以通过各自的命名空间为Tomcat、Undertow和Jetty配置访问日志。
例如,以下设置在Tomcat上用 自定义 pattern 记录访问。
server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a %r %s (%D ms)
server:
tomcat:
basedir: "my-tomcat"
accesslog:
enabled: true
pattern: "%t %a %r %s (%D ms)"
NOTE:日志的默认位置是相对于Tomcat基础目录的 logs
目录。
默认情况下,logs
目录是一个临时目录,所以你可能想固定Tomcat的基本目录,或者为日志使用一个绝对路径。
在前面的例子中,相对于应用程序的工作目录,日志在 my-tomcat/logs
中可用。
Undertow的访问日志可以用类似的方式配置,如下面的例子所示。
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a %r %s (%D ms)
server.undertow.options.server.record-request-start-time=true
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的访问日志也可以按以下方式配置。
server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log
server:
jetty:
accesslog:
enabled: true
filename: "/var/log/jetty-access.log"
默认情况下,日志被重定向到 System.err
。
更多细节,请参见Jetty文档。
3.12. 运行在代理服务器后面
如果你的应用程序在代理、负载均衡器或云端运行,请求信息(如主机、端口、scheme……)可能会沿途改变。
你的应用程序可能在 10.10.10.10:8080
上运行,但HTTP客户应该只看到 example.org
。
RFC7239 "Forwarded Headers" 定义了转发的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 filter。
如果你使用Tomcat并在代理处终止SSL, server.tomcat.redirect-context-root 应该设置为 false 。
这允许在执行任何重定向之前尊重 X-Forwarded-Proto 头。
|
如果你的应用程序在 Cloud Foundry 或 Heroku 中运行, server.forward-headers-strategy 属性默认为 NATIVE 。
在所有其他情况下,它默认为 NONE 。
|
3.12.1. 自定义Tomcat的代理配置
如果你使用Tomcat,你可以额外配置用于携带 “forwarded” 信息的 header 名称,如下例所示。
server.tomcat.remoteip.remote-ip-header=x-your-remote-ip-header
server.tomcat.remoteip.protocol-header=x-your-protocol-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.properties
中添加一个条目来定制valve的配置,如下面的例子所示。
server.tomcat.remoteip.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
server:
tomcat:
remoteip:
internal-proxies: "192\\.168\\.\\d{1,3}\\.\\d{1,3}"
你可以通过设置 internal-proxies 为empty来信任所有代理(但在生产中不要这样做)。
|
你可以完全控制Tomcat的 RemoteIpValve
的配置,方法是关闭自动的(要这样做,设置 server.forward-headers-strategy=NONE
),并使用 WebServerFactoryCustomizer
bean添加一个新的valve实例。
3.13. Tomcat 启用多个连接器(Connector)
你可以在 TomcatServletWebServerFactory
中添加 org.apache.catalina.connector.Connector
,它可以允许多个连接器,包括HTTP和HTTPS连接器,如下例所示。
@Configuration(proxyBeanMethods = false)
public class MyTomcatConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> connectorCustomizer() {
return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createConnector());
}
private Connector createConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(8081);
return connector;
}
}
@Configuration(proxyBeanMethods = false)
class MyTomcatConfiguration {
@Bean
fun connectorCustomizer(): WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
return WebServerFactoryCustomizer { tomcat: TomcatServletWebServerFactory ->
tomcat.addAdditionalTomcatConnectors(
createConnector()
)
}
}
private fun createConnector(): Connector {
val connector = Connector("org.apache.coyote.http11.Http11NioProtocol")
connector.port = 8081
return connector
}
}
3.14. 启用 Tomcat 的 MBean Registry
嵌入式Tomcat的MBean registry默认是禁用的。
这样可以最大限度地减少Tomcat的内存占用。
如果你想使用Tomcat的MBeans,例如让Micrometer使用它们来暴露指标,你必须使用 server.tomcat.mbeanregistry.enabled
属性来做到这一点,如下例所示。
server.tomcat.mbeanregistry.enabled=true
server:
tomcat:
mbeanregistry:
enabled: true
3.15. Undertow 启用多个 Listener
在 UndertowServletWebServerFactory
中添加一个 UndertowBuilderCustomizer
,并在 Builder
中添加一个 listener,如下例所示。
@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");
}
}
@Configuration(proxyBeanMethods = false)
class MyUndertowConfiguration {
@Bean
fun undertowListenerCustomizer(): WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
return WebServerFactoryCustomizer { factory: UndertowServletWebServerFactory ->
factory.addBuilderCustomizers(
UndertowBuilderCustomizer { builder: Undertow.Builder -> addHttpListener(builder) })
}
}
private fun addHttpListener(builder: Undertow.Builder): Undertow.Builder {
return builder.addHttpListener(8080, "0.0.0.0")
}
}
3.16. 使用@ServerEndpoint创建WebSocket端点
如果你想在使用嵌入式容器的Spring Boot应用程序中使用 @ServerEndpoint
,你必须声明一个 ServerEndpointExporter
@Bean
,如下例所示。
@Configuration(proxyBeanMethods = false)
public class MyWebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
@Configuration(proxyBeanMethods = false)
class MyWebSocketConfiguration {
@Bean
fun serverEndpointExporter(): ServerEndpointExporter {
return ServerEndpointExporter()
}
}
前面例子中显示的Bean将任何 @ServerEndpoint
注解的Bean与底层WebSocket容器注册。
当部署到独立的servlet容器时,这个角色由servlet容器的初始化器执行,而不需要 ServerEndpointExporter
bean。
4. Spring MVC
Spring Boot有许多包含Spring MVC的starter。 请注意,有些启动器包括对Spring MVC的依赖,而不是直接包括它。 本节回答关于Spring MVC和Spring Boot的常见问题。
4.1. 编写一个JSON REST服务
只要Jackson2在classpath上,Spring Boot应用程序中的任何Spring @RestController
都应该默认渲染JSON响应,如以下例子所示。
@RestController
public class MyController {
@RequestMapping("/thing")
public MyThing thing() {
return new MyThing();
}
}
@RestController
class MyController {
@RequestMapping("/thing")
fun thing(): MyThing {
return MyThing()
}
}
只要 MyThing
能够被Jackson2序列化(对于普通的POJO或Groovy对象来说是真的),那么 localhost:8080/thing
就会默认提供它的JSON表示。
请注意,在浏览器中,你有时可能会看到XML响应,因为浏览器倾向于发送accept头信息,更倾向于XML。
4.2. 编写一个XML REST服务
如果你在classpath上有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
,如下面的例子所示。
@XmlRootElement
public class MyThing {
private String name;
}
@XmlRootElement
class MyThing {
var name: String? = null
}
你将需要确保JAXB库是你项目的一部分,例如添加如下依赖。
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
为了让服务器渲染XML而不是JSON,你可能需要发送一个 Accept: text/xml 头(或使用浏览器)。
|
4.3. 自定义 Jackson ObjectMapper
Spring MVC(客户端和服务器端)使用 HttpMessageConverters
来协商HTTP交换中的内容转换。
如果Jackson在classpath上,你已经得到了由 Jackson2ObjectMapperBuilder
提供的默认converter,它的一个实例是为你自动配置的。
ObjectMapper
(或Jackson XML converter 的 XmlMapper
)实例(默认创建)有以下自定义属性。
-
MapperFeature.DEFAULT_VIEW_INCLUSION
已被禁用 -
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
已被禁用 -
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
已被禁用
Spring Boot也有一些功能,使其更容易定制这种行为。
你可以通过使用environment来配置 ObjectMapper
和 XmlMapper
实例。
Jackson提供了一套广泛的开/关特性,可用于配置其处理的各个方面。
这些特性在六个枚举中描述(在Jackson中),它们映射到environment中的properties。
Enum | Property | Values |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
例如,要启用pretty print,请设置 spring.jackson.serialization.indent_output=true
。注意,由于使用了宽松绑定,indent_output
的情况不一定要与相应的枚举常量的情况相匹配,即 INDENT_OUTPUT
。
这个基于environment的配置被应用于自动配置的 Jackson2ObjectMapperBuilder
Bean,并应用于任何通过使用 builder 创建的 mapper,包括自动配置的 ObjectMapper
Bean。
context的 Jackson2ObjectMapperBuilder
可以由一个或多个 Jackson2ObjectMapperBuilderCustomizer
Bean 来定制。
这种定制器Bean可以被排序(Boot自己的定制器的排序为0),让额外的定制在Boot的定制之前和之后都可以应用。
任何 com.fasterxml.jackson.databind.Module
类型的bean都会自动注册到自动配置的 Jackson2ObjectMapperBuilder
上,并应用到它所创建的任何 ObjectMapper
实例上。
这提供了一个全局机制,当你为你的应用程序添加新功能时,可以贡献自定义模块。
如果你想完全取代默认的 ObjectMapper
,可以定义一个该类型的 @Bean
并将其标记为 @Primary
,或者,如果你喜欢基于builder的方法,可以定义一个 Jackson2ObjectMapperBuilder
@Bean
。
请注意,无论哪种情况,这样做都会使 ObjectMapper
的所有自动配置失效。
如果你提供了任何 MappingJackson2HttpMessageConverter
类型的 @Beans
,它们将取代MVC配置中的默认值。另外,还提供了一个 HttpMessageConverters
类型的便利 Bean(如果你使用默认的MVC配置,它总是可用的)。它有一些有用的方法来访问默认和用户增强的message converter。
更多细节请参见 “自定义@ResponseBody的渲染方式” 部分和 WebMvcAutoConfiguration
源代码。
4.4. 自定义@ResponseBody的渲染方式
Spring使用 HttpMessageConverters
来渲染 @ResponseBody
(或来自 @RestController
的响应)。
你可以通过在Spring Boot上下文中添加适当类型的Bean来贡献额外的converter。
如果你添加的Bean是默认包含的类型(例如用于JSON转换的 MappingJackson2HttpMessageConverter
),它将取代默认值。
一个 HttpMessageConverters
类型的便利Bean被提供,如果你使用默认的MVC配置,它总是可用的。
它有一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果你想手动注入它们到一个自定义的 RestTemplate
中,它可能很有用)。
与正常的MVC使用一样,你提供的任何 WebMvcConfigurer
bean也可以通过覆盖 configureMessageConverters
方法来贡献converter。
然而,与普通的MVC不同,你只能提供你需要的额外converter(因为Spring Boot使用相同的机制来贡献其默认值)。
最后,如果你通过提供自己的 @EnableWebMvc
配置来选择退出Spring Boot默认的MVC配置,你可以完全控制并通过使用 WebMvcConfigurationSupport
的 getMessageConverters
手动完成一切。
更多细节请参见 WebMvcAutoConfiguration
源代码。
4.5. 处理 Multipart File 文件上传
Spring Boot接纳了servlet 5 jakarta.servlet.http.Part
API来支持上传文件。默认情况下,Spring Boot为Spring MVC配置了每个文件的最大尺寸为1MB,单个请求中的文件数据最大为10MB。你可以通过使用 MultipartProperties
类中暴露的属性来覆盖这些值、中间数据的存储位置(例如,存储到 /tmp
目录),以及超过数据被刷入磁盘的阈值。例如,如果你想指定文件不受限制,请将 spring.servlet.multipart.max-file-size
属性设为 -1
。
当你想在Spring MVC控制器处理方法中接收作为 @RequestParam
注解的 MultipartFile
类型参数的multipart编码文件数据时,multipart支持很有帮助。
更多细节参见 MultipartAutoConfiguration
源代码。
建议使用容器内置的对multipart上传的支持,而不是引入一个额外的依赖,如Apache Commons File Upload。 |
4.6. 关闭 Spring MVC DispatcherServlet
默认情况下,所有内容都是从你的应用程序的根(/
)提供的。
如果你想映射到一个不同的路径,你可以按以下方式配置。
spring.mvc.servlet.path=/mypath
spring:
mvc:
servlet:
path: "/mypath"
如果你有额外的Servlet,你可以为每个Servlet声明一个 @Bean
类型的 Servlet
或 ServletRegistrationBean
,Spring Boot将以透明的方式向容器注册它们。
因为servlet是以这种方式注册的,所以它们可以被映射到 DispatcherServlet
的子context中,而无需调用它。
自己配置 DispatcherServlet
是不寻常的,但如果你真的需要这样做,也必须提供一个 DispatcherServletPath
类型的 @Bean
,以提供你自定义 DispatcherServlet
的路径。
4.7. 关闭默认的的 MVC 配置(Configuration)
完全控制MVC配置的最简单的方法是提供你自己的 @Configuration
与 @EnableWebMvc
注解。
这样做让所有的MVC配置都掌握在你手中。
4.8. 自定义 ViewResolver
ViewResolver
是Spring MVC的一个核心组件,它将 @Controller
中的视图(view)名称转换为实际的 View
实现。
请注意,ViewResolvers
主要用于UI应用程序,而不是REST风格的服务(View
不用于渲染 @ResponseBody
)。
有很多 ViewResolver
的实现可以选择,Spring本身对你应该使用哪些实现没有意见。
另一方面,Spring Boot会根据它在classpath和application context中发现的情况,为你安装一个或两个。
DispatcherServlet
使用它在应用上下文中发现的所有解析器,依次尝试每个解析器,直到得到一个结果。
如果你添加你自己的,你必须注意你的解析器被添加的顺序和位置。
WebMvcAutoConfiguration
将以下 ViewResolvers
添加到你的上下文。
-
一个名为 ‘defaultViewResolver’' 的
InternalResourceViewResolver
。 这个定位可以通过使用DefaultServlet
渲染的物理资源(包括静态资源和JSP页面,如果你使用这些)。 它对视图名称应用前缀和后缀,然后在servlet上下文中寻找具有该路径的物理资源(默认值都是空的,但可以通过spring.mvc.view.prefix
和spring.mvc.view.suffix
访问外部配置)。 你可以通过提供一个相同类型的bean来覆盖它。 -
一个名为 ‘beanNameViewResolver’ 的
BeanNameViewResolver。
这是视图解析器链中的一个有用的成员,它接收任何与正在解析的View
同名的Bean。 应该没有必要覆盖或替换它。 -
一个名为 ‘viewResolver’ 的
ContentNegotiatingViewResolver
只有在有View
类型的Bean存在时才会被添加。 这是一个复合的解析器,委托给所有其他的解析器,并试图找到一个与客户端发送的 ‘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
在你的classpath上),你也有一个GroovyMarkupViewResolver
,名为 ‘groovyMarkupViewResolver’。 它通过用前缀和后缀(外化为spring.groovy.template.prefix
和spring.groovy.template.suffix
)包围视图名称来寻找加载器路径中的资源。 前缀和后缀的默认值分别为 ‘classpath:/templates/’ 和 ‘.tpl’。 你可以通过提供一个同名的bean来覆盖GroovyMarkupViewResolver
。 -
如果你使用Mustache,你也有一个名为 ‘mustacheViewResolver’ 的
MustacheViewResolver
。它通过在视图名称周围加上前缀和后缀来寻找资源。前缀是spring.mustache.prefix
,后缀是spring.mustache.suffix
。前缀和后缀的值分别默认为 ‘classpath:/templates/’ 和 ‘.mustache’。你可以通过提供一个同名的bean来覆盖MustacheViewResolver
。
更多细节请见以下章节。
5. Jersey
5.1. 用Spring Security保护Jersey端点
Spring Security可以用来保护基于Jersey的Web应用,其方式与保护基于Spring MVC的Web应用的方式基本相同。
然而,如果你想在Jersey中使用Spring Security的方法级安全,你必须将Jersey配置为使用 setStatus(int)
而不是 sendError(int)
。
这可以防止Jersey在Spring Security有机会向客户端报告认证或授权失败之前提交响应。
jersey.config.server.response.setStatusOverSendError
属性必须在应用程序的 ResourceConfig
bean上设置为 true
,如下例所示。
@Component
public class JerseySetStatusOverSendErrorConfig extends ResourceConfig {
public JerseySetStatusOverSendErrorConfig() {
register(Endpoint.class);
setProperties(Collections.singletonMap("jersey.config.server.response.setStatusOverSendError", true));
}
}
5.2. 和另一个 web 框架一起使用 Jersey
要将Jersey与另一个Web框架(如Spring MVC)一起使用,应该对它进行配置,使其允许其他框架处理它无法处理的请求。
首先,通过配置 spring.jersey.type
application property 的值为 filter
,将Jersey配置为使用filter而不是servlet。
其次,配置你的 ResourceConfig
来转发那些会导致404的请求,如下面的例子所示。
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Endpoint.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
6. HTTP 客户端
Spring Boot提供了一些与HTTP客户端一起工作的starter。 本节回答与使用它们有关的问题。
6.1. 配置RestTemplate以使用代理
正如 io.html 中所描述的,你可以使用 RestTemplateCustomizer
与 RestTemplateBuilder
来建立一个定制的 RestTemplate
。这是创建配置为使用代理的 RestTemplate
的推荐方法。
6.2. 配置基于Reactor Netty的WebClient所使用的TcpClient
当Reactor Netty在classpath上时,基于Reactor Netty的 WebClient
会被自动配置。
为了定制客户端对网络连接的处理,可以提供一个 ClientHttpConnector
bean。
下面的例子配置了60秒的连接超时并添加了一个 ReadTimeoutHandler
。
@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);
}
}
@Configuration(proxyBeanMethods = false)
class MyReactorNettyClientConfiguration {
@Bean
fun clientHttpConnector(resourceFactory: ReactorResourceFactory): ClientHttpConnector {
val httpClient = HttpClient.create(resourceFactory.connectionProvider)
.runOn(resourceFactory.loopResources)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 60000)
.doOnConnected { connection ->
connection.addHandlerLast(ReadTimeoutHandler(60))
}
return ReactorClientHttpConnector(httpClient)
}
}
注意连接提供者和事件循环资源使用了 ReactorResourceFactory 。
这确保了接收请求的服务器和发出请求的客户端有效地共享资源。
|
7. 日志(Logging)
除了通常由Spring Framework的 spring-jcl
模块提供的Commons Logging API,Spring Boot没有强制性的日志依赖。要使用 Logback,你需要在classpath上包含它和 spring-jcl
。推荐的方法是通过starter,它们都依赖于 spring-boot-starter-logging
。对于Web应用,你只需要 spring-boot-starter-web
,因为它间接依赖于logging starter。如果你使用Maven,下面的依赖可以为你添加日志。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot有一个 LoggingSystem
抽象,试图根据classpath的内容来配置日志。
如果Logback是可用的,它是第一选择。
如果你需要对日志进行的唯一改变是设置各种日志的级别,你可以在 application.properties
中通过使用 "logging.level" 前缀来实现,如以下例子所示。
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
logging:
level:
org.springframework.web: "debug"
org.hibernate: "error"
你也可以通过使用 logging.file.name
来设置写入日志的文件的位置(除了控制台)。
要配置日志系统的更精细的设置,你需要使用相关的 LoggingSystem
支持的本地配置格式。
默认情况下,Spring Boot会从系统的默认位置获取本地配置(例如Logback的 classpath:logback.xml
),但你可以通过使用 logging.config
属性设置配置文件的位置。
7.1. 配置 Logback 用于日志记录
如果你需要对logback进行定制,而不是用 application.properties
来实现,你将需要添加一个标准的logback配置文件。你可以在你的classpath的根部添加一个 logback.xml
文件,让logback找到。如果你想使用 Spring Boot Logback扩展,你也可以使用 logback-spring.xml
。
Logback文档有一个 专门的章节,涉及配置的一些细节。 |
Spring Boot提供了一些可以 included
在你自己的配置中的logback配置。
这些 included
旨在允许重用某些常见的Spring Boot惯例。
以下文件在 org/springframework/boot/logging/logback/
下提供。
-
defaults.xml
- 提供转换规则, pattern properties 和常见的 logger配置. -
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配置文件也可以利用系统属性(System properties),LoggingSystem
负责为你创建。
-
${PID}
: 当前进程的ID。 -
${LOG_FILE}
: 无论 Boot的外部配置中是否设置了logging.file.name
。 -
${LOG_PATH}
: 无论logging.file.path
(代表日志文件所在的目录)是否在Boot的外部配置中被设置。 -
${LOG_EXCEPTION_CONVERSION_WORD}
: 无论 Boot的外部配置中是否设置了logging.exception-conversion-word
。 -
${ROLLING_FILE_NAME_PATTERN}
: 无论 Boot的外部配置中是否设置了logging.pattern.rolling-file-name
。
通过使用自定义的Logback converter,Spring Boot还在控制台提供了一些漂亮的ANSI彩色终端输出(但不是在日志文件中)。
请看 defaults.xml
配置中的 CONSOLE_LOG_PATTERN
的例子。
如果Groovy在classpath上,你应该也能用 logback.groovy
来配置Logback。
如果存在,这个设置会被优先考虑。
Spring extensions are not supported with Groovy configuration.
Any logback-spring.groovy files will not be detected.
|
7.1.1. 配置只用于文件输出的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>
你还需要在你的 application.properties
或 application.yaml
中添加 logging.file.name
,如下例所示。
logging.file.name=myapplication.log
logging:
file:
name: "myapplication.log"
7.2. 配置 Log4j 用于日志记录
如果 Log4j 2 在classpath上,Spring Boot支持Log4j 2进行日志配置。如果你使用starter来组装依赖关系,你必须排除Logback,然后包括Log4j 2。如果你不使用starter,除了Log4j 2,你还需要提供(至少) spring-jcl
。
推荐的路径是通过starter,尽管这需要一些调整。 下面的例子展示了如何在Maven中设置starter。
<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提供了几种不同的方法来设置starter。 一种方法是使用 模块替换。 要做到这一点,需要声明对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")
}
}
}
Log4j starter将常见的日志要求的依赖聚集在一起(例如让Tomcat使用 java.util.logging ,但使用Log4j 2配置输出)。
|
为了确保使用 java.util.logging 执行的调试日志被路由到Log4j 2,通过将 java.util.logging.manager 系统属性设置为 org.apache.logging.log4j.jul.LogManager 来配置其 JDK日志适配器。
|
7.2.1. 使用YAML或JSON来配置Log4j 2
除了默认的XML配置格式外,Log4j 2还支持YAML和JSON配置文件。 要配置Log4j 2使用其他的配置文件格式,请在classpath中添加适当的依赖项,并命名你的配置文件以匹配你选择的文件格式,如下面的例子所示。
格式 | 依赖 | 文件名称 |
---|---|---|
YAML |
|
|
JSON |
|
|
8. 数据访问
Spring Boot包括一些用于处理数据源的starter。 本节回答与此相关的问题。
8.1. 配置自定义数据源
要配置你自己的 DataSource
,请在你的配置中定义一个该类型的 @Bean
。
Spring Boot会在任何需要 DataSource
的地方重用它,包括数据库初始化。
如果你需要外部化一些设置,你可以将你的 DataSource
绑定到environment中(见 “features.html”)。
下面的例子显示了如何在Bean中定义一个数据源。
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "app.datasource")
public SomeDataSource dataSource() {
return new SomeDataSource();
}
}
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "app.datasource")
fun dataSource(): SomeDataSource {
return SomeDataSource()
}
}
下面的例子显示了如何通过设置属性来定义一个数据源。
app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:h2:mem:mydb"
username: "sa"
pool-size: 30
假设 SomeDataSource
有常规的JavaBean属性,用于URL、用户名和池大小,在 DataSource
被提供给其他组件之前,这些设置被自动绑定。
Spring Boot还提供了一个名为 DataSourceBuilder
的实用builder,可用于创建标准数据源之一(如果它在classpath上)。
builder可以根据classpath上的可用内容检测要使用的数据源。
它还可以根据JDBC的URL自动检测驱动程序。
下面的例子显示了如何通过使用 DataSourceBuilder
来创建一个数据源。
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
fun dataSource(): DataSource {
return DataSourceBuilder.create().build()
}
}
要用该 DataSource
运行一个应用程序,你所需要的只是连接信息。
也可以提供池的特定设置。
检查运行时要使用的实现以了解更多细节。
下面的例子显示了如何通过设置属性来定义一个JDBC数据源。
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
然而,有一个问题。
因为连接池的实际类型没有暴露,在你的自定义 DataSource
的元数据中没有生成key,在你的IDE中也没有完成度(因为 DataSource
接口没有暴露属性)。
另外,如果你碰巧在classpath上有 Hikari,这个基本设置就不工作了,因为Hikari没有 url
属性(但有 jdbcUrl
属性)。
在这种情况下,你必须重写你的配置,如下所示。
app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30
app:
datasource:
jdbc-url: "jdbc:mysql://localhost/test"
username: "dbuser"
password: "dbpass"
pool-size: 30
你可以通过强制连接池使用并返回一个专门的实现而不是 DataSource
来解决这个问题。
你不能在运行时改变实现,但选项列表将是明确的。
下面的例子显示了如何用 DataSourceBuilder
创建一个 HikariDataSource
。
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
}
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource")
fun dataSource(): HikariDataSource {
return DataSourceBuilder.create().type(HikariDataSource::class.java).build()
}
}
你甚至可以更进一步,利用 DataSourceProperties
为你做的事情—也就是说,如果没有提供URL,就提供一个默认的嵌入式数据库,并提供合理的用户名和密码。
你可以从任何 DataSourceProperties
对象的状态中轻松地初始化 DataSourceBuilder
,所以你也可以注入Spring Boot自动创建的DataSource。
然而,这将把你的配置分成两个命名空间:url
、username
、password
、type
和 driver
在 spring.datasource
上,其余的在你的自定义命名空间( app.datasource
)。
为了避免这种情况,你可以在你的自定义命名空间上重新定义一个自定义的 DataSourceProperties
,如下面的例子所示。
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyDataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource")
fun dataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean
@ConfigurationProperties("app.datasource.configuration")
fun dataSource(properties: DataSourceProperties): HikariDataSource {
return properties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
}
这种设置使你与Spring Boot的默认做法同步,只是(在代码中)选择了一个专用的连接池,其设置在 app.datasource.configuration
子命名空间中公开。
因为 DataSourceProperties
为你处理了 url
/jdbcUrl
的转换,你可以按以下方式配置它。
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.configuration.maximum-pool-size=30
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 没有影响。
在实践中,builder被初始化为你可能设置的任何值,然后被对 .type() 的调用所重写。
|
详情见 "Spring Boot特性" 部分的 “data.html” 和 DataSourceAutoConfiguration
类。
8.2. 配置两个数据源
如果你需要配置多个数据源,你可以应用上一节所述的相同技巧。
然而,你必须将其中一个 DataSource
实例标记为 @Primary
,因为接下来的各种自动配置都希望能够按类型获得一个。
如果你创建了你自己的 DataSource
,自动配置就会退缩。
在下面的例子中,我们提供了与自动配置在主数据源上提供完全相同的功能集。
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyDataSourcesConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource.first")
fun firstDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
@Bean
@ConfigurationProperties("app.datasource.second")
fun secondDataSource(): BasicDataSource {
return DataSourceBuilder.create().type(BasicDataSource::class.java).build()
}
}
firstDataSourceProperties 必须被标记为 @Primary ,以便数据库初initializer功能使用你的副本(如果你使用初initializer)。
|
这两个数据源也都是为高级定制而绑定的。 例如,你可以对它们进行如下配置。
app.datasource.first.url=jdbc:mysql://localhost/first
app.datasource.first.username=dbuser
app.datasource.first.password=dbpass
app.datasource.first.configuration.maximum-pool-size=30
app.datasource.second.url=jdbc:mysql://localhost/second
app.datasource.second.username=dbuser
app.datasource.second.password=dbpass
app.datasource.second.max-total=30
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
,如下面的例子所示。
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyCompleteDataSourcesConfiguration {
@Bean
@Primary
@ConfigurationProperties("app.datasource.first")
fun firstDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean
@Primary
@ConfigurationProperties("app.datasource.first.configuration")
fun firstDataSource(firstDataSourceProperties: DataSourceProperties): HikariDataSource {
return firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource::class.java).build()
}
@Bean
@ConfigurationProperties("app.datasource.second")
fun secondDataSourceProperties(): DataSourceProperties {
return DataSourceProperties()
}
@Bean
@ConfigurationProperties("app.datasource.second.configuration")
fun secondDataSource(secondDataSourceProperties: DataSourceProperties): BasicDataSource {
return secondDataSourceProperties.initializeDataSourceBuilder().type(BasicDataSource::class.java).build()
}
}
前面的例子在自定义命名空间上配置了两个数据源,其逻辑与Spring Boot在自动配置中使用的一样。
请注意,每个 configuration
子命名空间都根据所选择的实现提供高级设置。
8.3. 使用 Spring Data Repository
Spring Data可以创建各种类型的 @Repository
接口的实现。
只要这些 @Repositories
包含在你的 @EnableAutoConfiguration
类的同一个包(或一个子包)中,Spring Boot就会为你处理所有这些。
对于许多应用程序来说,你所需要的只是将正确的Spring Data依赖关系放在你的classpath上。
有一个 spring-boot-starter-data-jpa
用于JPA,spring-boot-starter-data-mongodb
用于Mongodb,还有其他各种支持技术的starter。
为了开始工作,创建一些 Repository 接口来处理你的 @Entity
对象。
Spring Boot试图根据它发现的 @EnableAutoConfiguration
来猜测你的 @Repository
定义的位置。
要获得更多的控制,可以使用 @EnableJpaRepositories
注解(来自Spring Data JPA)。
关于Spring Data的更多信息,请参阅 Spring Data项目页面。
8.4. 将@Entity定义从Spring配置中分离出来
Spring Boot会根据它发现的 @EnableAutoConfiguration
,试图猜测你的 @Entity
定义的位置。
为了获得更多控制,你可以使用 @EntityScan
注解,如下例所示。
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = City.class)
public class MyApplication {
// ...
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EntityScan(basePackageClasses = [City::class])
class MyApplication {
// ...
}
8.5. 配置JPA属性
Spring Data JPA已经提供了一些独立于供应商的配置选项(例如用于SQL日志的配置),Spring Boot将这些选项和Hibernate的一些其他选项作为外部配置属性公开。 其中一些是根据上下文自动检测出来的,所以你不应该设置它们。
spring.jpa.hibernate.ddl-auto
是一个特例,因为根据运行时条件,它有不同的默认值。
如果使用嵌入式数据库,并且没有 schema manager(如Liquibase或Flyway)处理 DataSource
,它默认为 create-drop
。
在所有其他情况下,它默认为 none
。
使用的方言是由JPA提供者检测的。
如果你喜欢自己设置方言,请设置 spring.jpa.database-platform
属性。
要设置的最常见的选项如下例所示。
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true
spring:
jpa:
hibernate:
naming:
physical-strategy: "com.example.MyPhysicalNamingStrategy"
show-sql: true
此外,在创建本地的 EntityManagerFactory
时,spring.jpa.properties.*
中的所有属性都会作为正常的JPA属性传递(剥离前缀)。
你需要确保在 例如,如果你想配置Hibernate的 batch size,你必须使用 |
如果你需要对Hibernate属性进行高级定制,可以考虑注册一个 HibernatePropertiesCustomizer bean,它将在创建 EntityManagerFactory 之前被调用。
这将优先于任何由自动配置应用的东西。
|
8.6. 配置Hibernate的命名策略
Hibernate使用 两种不同的命名策略,将对象模型中的名称映射到相应的数据库名称。
物理策略和隐含策略实现的全限定类名可以通过设置 spring.jpa.hibernate.naming.physical-strategy
和 spring.jpa.hibernate.naming.implicit-strategy
属性分别配置。
另外,如果 ImplicitNamingStrategy
或 PhysicalNamingStrategy
Bean在应用程序上下文中可用,Hibernate将被自动配置为使用它们。
默认情况下,Spring Boot用 CamelCaseToUnderscoresNamingStrategy
配置 physical 命名策略。
使用这种策略,所有的点都被下划线所取代,驼峰大小写也被下划线所取代。
此外,默认情况下,所有的表名都以小写生成。
例如,一个 TelephoneNumber
实体被映射到 telephone_number
表。
如果你的模式需要混合大小写的标识符,请定义一个自定义的 CamelCaseToUnderscoresNamingStrategy
bean,如下例所示。
@Configuration(proxyBeanMethods = false)
public class MyHibernateConfiguration {
@Bean
public CamelCaseToUnderscoresNamingStrategy caseSensitivePhysicalNamingStrategy() {
return new CamelCaseToUnderscoresNamingStrategy() {
@Override
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return false;
}
};
}
}
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
fun caseSensitivePhysicalNamingStrategy(): CamelCaseToUnderscoresNamingStrategy {
return object : CamelCaseToUnderscoresNamingStrategy() {
override fun isCaseInsensitive(jdbcEnvironment: JdbcEnvironment): Boolean {
return false
}
}
}
}
如果你喜欢用Hibernate的默认值来代替,请设置以下属性。
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
或者,你可以配置以下Bean。
@Configuration(proxyBeanMethods = false)
class MyHibernateConfiguration {
@Bean
PhysicalNamingStrategyStandardImpl caseSensitivePhysicalNamingStrategy() {
return new PhysicalNamingStrategyStandardImpl();
}
}
@Configuration(proxyBeanMethods = false)
internal class MyHibernateConfiguration {
@Bean
fun caseSensitivePhysicalNamingStrategy(): PhysicalNamingStrategyStandardImpl {
return PhysicalNamingStrategyStandardImpl()
}
}
8.7. 配置Hibernate 二级缓存
Hibernate 二级缓存 可以为一系列的缓存提供者进行配置。 与其配置Hibernate再次查找缓存提供者,不如尽可能地提供上下文中可用的那个缓存提供者。
要使用JCache,首先要确保 org.hibernate.orm:hibernate-jcache
在classpath上可用。
然后,添加一个 HibernatePropertiesCustomizer
bean,如下例所示。
@Configuration(proxyBeanMethods = false)
public class MyHibernateSecondLevelCacheConfiguration {
@Bean
public HibernatePropertiesCustomizer hibernateSecondLevelCacheCustomizer(JCacheCacheManager cacheManager) {
return (properties) -> properties.put(ConfigSettings.CACHE_MANAGER, cacheManager.getCacheManager());
}
}
@Configuration(proxyBeanMethods = false)
class MyHibernateSecondLevelCacheConfiguration {
@Bean
fun hibernateSecondLevelCacheCustomizer(cacheManager: JCacheCacheManager): HibernatePropertiesCustomizer {
return HibernatePropertiesCustomizer { properties ->
properties[ConfigSettings.CACHE_MANAGER] = cacheManager.cacheManager
}
}
}
这个自定义器将配置Hibernate使用与应用程序使用的相同的 CacheManager
。
也可以使用单独的 CacheManager
实例。
详情请见 Hibernate用户指南。
8.8. 在Hibernate组件中使用依赖注入
默认情况下,Spring Boot注册了一个使用 BeanFactory
的 BeanContainer
实现,以便 converter 和 entity listener 可以使用常规的依赖注入。
你可以通过注册一个 HibernatePropertiesCustomizer
来禁用或调整这种行为,它可以删除或改变 hibernate.resource.beans.container
属性。
8.9. 使用一个自定义的 EntityManagerFactory
为了完全控制 EntityManagerFactory
的配置,你需要添加一个名为 entityManagerFactory
的 @Bean
。
Spring Boot的自动配置会在出现该类型的Bean时关闭其 entity manager。
8.10. 使用多个 EntityManagerFactory
如果你需要针对多个数据源使用JPA,你可能需要每个数据源有一个 EntityManagerFactory
。
Spring ORM的 LocalContainerEntityManagerFactoryBean
允许你为你的需求配置一个 EntityManagerFactory
。
你也可以重复使用 JpaProperties
来为每个 EntityManagerFactory
绑定设置,如下例所示。
@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();
}
}
@Configuration(proxyBeanMethods = false)
class MyEntityManagerFactoryConfiguration {
@Bean
@ConfigurationProperties("app.jpa.first")
fun firstJpaProperties(): JpaProperties {
return JpaProperties()
}
@Bean
fun firstEntityManagerFactory(
firstDataSource: DataSource?,
firstJpaProperties: JpaProperties
): LocalContainerEntityManagerFactoryBean {
val builder = createEntityManagerFactoryBuilder(firstJpaProperties)
return builder.dataSource(firstDataSource).packages(Order::class.java).persistenceUnit("firstDs").build()
}
private fun createEntityManagerFactoryBuilder(jpaProperties: JpaProperties): EntityManagerFactoryBuilder {
val jpaVendorAdapter = createJpaVendorAdapter(jpaProperties)
return EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.properties, null)
}
private fun createJpaVendorAdapter(jpaProperties: JpaProperties): JpaVendorAdapter {
// ... map JPA properties as needed
return HibernateJpaVendorAdapter()
}
}
上面的例子使用一个名为 firstDataSource
的bean创建了一个 EntityManagerFactory
。
它扫描了与 Order
相同的包中的实体。
可以使用 app.first.jpa
命名空间来映射其他JPA属性。
当你为 LocalContainerEntityManagerFactoryBean 自己创建一个Bean时,在创建自动配置的 LocalContainerEntityManagerFactoryBean 时应用的任何定制都会丢失。
例如,在Hibernate的情况下, spring.jpa.hibernate 前缀下的任何属性都不会自动应用到你的 LocalContainerEntityManagerFactoryBean 。
如果你依赖这些属性来配置诸如命名策略或DDL模式,你将需要在创建 LocalContainerEntityManagerFactoryBean bean时明确配置。
|
你应该为你需要JPA访问的任何其他数据源提供类似的配置。
为了使画面完整,你需要为每个 EntityManagerFactory
配置一个 JpaTransactionManager
。
或者,你也可以使用一个跨越两者的JTA事务管理器(transaction manager)。
如果你使用Spring Data,你需要相应地配置 @EnableJpaRepositories
,如以下例子所示。
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "firstEntityManagerFactory")
public class OrderConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Order::class], entityManagerFactoryRef = "firstEntityManagerFactory")
class OrderConfiguration
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "secondEntityManagerFactory")
public class CustomerConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableJpaRepositories(basePackageClasses = [Customer::class], entityManagerFactoryRef = "secondEntityManagerFactory")
class CustomerConfiguration
8.11. 使用传统的 persistence.xml 文件
Spring Boot默认不会搜索或使用 META-INF/persistence.xml
。
如果你喜欢使用传统的 persistence.xml
,你需要定义你自己的 @Bean
,类型为 LocalEntityManagerFactoryBean
(ID为 entityManagerFactory
),并在那里设置 persistence unit name。
默认设置见 JpaBaseConfiguration
。
8.12. 使用 Spring Data JPA 和 Mongo Repository
Spring Data JPA和Spring Data Mongo都可以为你自动创建 Repository
实现。
如果它们都在classpath上,你可能需要做一些额外的配置,告诉Spring Boot要创建哪些repository。
最明确的方法是使用标准的 Spring Data @EnableJpaRepositories
和 @EnableMongoRepositories
注解,并提供你的 Repository
接口的位置。
还有一些标志(spring.data.*.repositories.enabled
和 spring.data.*.repositories.type
),你可以用来在外部配置中打开和关闭自动配置的repository。
这样做是有用的,例如,如果你想关闭Mongo repository,但仍然使用自动配置的 `MongoTemplate'。
其他自动配置的Spring Data资源库类型(Elasticsearch、Redis和其他)也存在同样的障碍和同样的功能。 要与它们一起工作,要相应地改变注解和标志(flag)的名称。
8.13. 自定义 Spring Data 的 Web 支持
Spring Data提供了WEB支持,简化了Spring Data Repository在WEB应用中的使用。
Spring Boot在 spring.data.web
命名空间中提供了用于定制其配置的属性。
注意,如果你使用Spring Data REST,你必须使用 spring.data.rest
命名空间中的属性来代替。
8.14. 将 Spring Data Repository 作为REST端点进行公开
Spring Data REST可以将 Repository
的实现作为REST端点公开给你。
只要应用程序启用了Spring MVC。
Spring Boot公开了一组有用的属性(来自 spring.data.rest
命名空间),用于定制 RepositoryRestConfiguration
。
如果你需要提供额外的定制,你应该使用 RepositoryRestConfigurer
bean。
如果你不在你的自定义 RepositoryRestConfigurer 上指定任何顺序,它就会在Spring Boot内部使用的顺序之后运行。
如果你需要指定一个顺序,请确保它高于0。
|
8.15. 配置一个被JPA使用的组件
如果你想配置JPA使用的组件,那么你需要确保该组件在JPA之前被初始化。 当组件被自动配置时,Spring Boot会帮你解决这个问题。 例如,当Flyway被自动配置时,Hibernate被配置为依赖Flyway,这样Flyway就有机会在Hibernate试图使用它之前初始化数据库。
如果你自己配置一个组件,你可以使用 EntityManagerFactoryDependsOnPostProcessor
子类作为设置必要的依赖关系的方便方法。
例如,如果你使用Hibernate Search与Elasticsearch作为其索引管理器,任何 EntityManagerFactory
Bean 必须被配置为依赖 elasticsearchClient
Bean,如以下例子所示。
/**
* {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
* {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
*/
@Component
public class ElasticsearchEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
public ElasticsearchEntityManagerFactoryDependsOnPostProcessor() {
super("elasticsearchClient");
}
}
@Component
class ElasticsearchEntityManagerFactoryDependsOnPostProcessor :
EntityManagerFactoryDependsOnPostProcessor("elasticsearchClient")
8.16. 用两个数据源配置jOOQ
如果你需要用多个数据源来使用jOOQ,你应该为每个数据源创建你自己的 `DSLContext'。 更多细节请参见 JooqAutoConfiguration。
特别是,JooqExceptionTranslator 和 SpringTransactionProvider 可以被重用,以提供类似于自动配置对单一 DataSource 所做的功能。
|
9. 数据库初始化
一个SQL数据库可以通过不同的方式进行初始化,这取决于你的堆栈是什么。 当然,你也可以手动进行,前提是数据库是一个独立的进程。 建议使用单一的机制来生成schema。
9.1. 使用JPA初始化一个数据库
JPA具有生成DDL的功能,这些功能可以被设置为在启动时针对数据库运行。 这是由两个外部属性控制的。
-
spring.jpa.generate-ddl
(boolean)可以打开和关闭该功能,并且与供应商无关。 -
spring.jpa.hibernate.ddl-auto
(enum) 是Hibernate的一个功能,它以更精细的方式控制行为。本指南后面将详细介绍这一特性。
9.2. 使用Hibernate初始化数据库
你可以明确设置 spring.jpa.hibernate.ddl-auto
,标准的Hibernate属性值是 none
、validate
、update
、create
和 create-drop
。
Spring Boot根据它认为你的数据库是否被嵌入,为你选择了一个默认值。
如果没有检测到schema manager,它默认为 create-drop
,在所有其他情况下为 none
。
嵌入式数据库是通过查看 Connection
类型和JDBC网址来检测的。
hsqldb
、h2
和 derby
是候选的,其他的不是。
当从内存中切换到 "真实" 数据库时要小心,不要对新平台中的表和数据的存在做出假设。
你必须明确设置 ddl-auto
,或者使用其他机制来初始化数据库。
你可以通过启用 org.hibernate.SQL 日志器来输出schema的创建。
如果你启用 debug 模式,这将自动为你完成。
|
此外,如果Hibernate从头开始创建schema(也就是说,如果 ddl-auto
属性被设置为 create
或 creat-drop
),在classpath根部的一个名为 import.sql
的文件在启动时被执行。
这对于演示和测试来说是很有用的,如果你很小心的话,但在生产中你可能不希望出现在classpath上。
这是一个Hibernate的特性(与Spring无关)。
9.3. 使用基本SQL脚本初始化数据库
Spring Boot可以自动创建你的JDBC DataSource
或R2DBC ConnectionFactory
的schema(DDL脚本)并初始化它(DML脚本)。
它从标准的根classpath位置加载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
来调整这一行为。
基于脚本的 DataSource
初始化默认在创建任何JPA EntityManagerFactory
Bean之前进行。
schema.sql
可以用来为JPA管理的实体创建schema,data.sql
可以用来填充它。
虽然我们不推荐使用多种数据源初始化技术,但如果你希望基于脚本的 DataSource
初始化能够建立在Hibernate执行的schema创建之上,请将 spring.jpa.defer-datasource-initialization
设置为 true
。
这将推迟数据源初始化,直到任何 EntityManagerFactory
Bean 被创建和初始化之后。
然后,schema.sql
可以用来对Hibernate执行的任何schema创建进行补充,data.sql
可以用来填充它。
如果你正在使用 高级数据库迁移工具,如Flyway或Liquibase,你应该单独使用它们来创建和初始化schema。不建议在Flyway或Liquibase旁边使用基本的 schema.sql
和 data.sql
脚本,在未来的版本中会取消支持。
9.4. 初始化Spring Batch数据库
如果你使用Spring Batch,它预装了大多数流行数据库平台的SQL初始化脚本。 Spring Boot可以检测你的数据库类型并在启动时执行这些脚本。 如果你使用的是嵌入式数据库,默认情况下是这样的。 你也可以为任何数据库类型启用它,如下面的例子所示。
spring.batch.jdbc.initialize-schema=always
spring:
batch:
jdbc:
initialize-schema: "always"
你也可以通过设置 spring.batch.jdbc.initialize-schema
为 never
来明确关闭初始化。
9.5. 使用高级数据库迁移工具
9.5.1. 在启动时执行Flyway数据库迁移
为了在启动时自动运行Flyway数据库迁移,请将 org.flywaydb:flyway-core
添加到你的classpath。
通常,迁移是以 V<VERSION>__<NAME>.sql
的形式出现的脚本(其中 VERSION>
是用下划线分隔的版本,例如 ‘1’ 或 ‘2_1’ )。
默认情况下,它们在一个名为 classpath:db/migration
的目录中,但你可以通过设置 spring.flyway.locations
来修改这个位置。
这是一个逗号分隔的列表,包含一个或多个 classpath:
或 filesystem:
位置。
例如,下面的配置将搜索默认classpath位置和 /opt/migration
目录下的脚本。
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
spring:
flyway:
locations: "classpath:db/migration,filesystem:/opt/migration"
你也可以添加一个特殊的 {vendor}
占位符来使用特定供应商的脚本。
假设如下。
spring.flyway.locations=classpath:db/migration/{vendor}
spring:
flyway:
locations: "classpath:db/migration/{vendor}"
前面的配置不是使用 db/migration
,而是根据数据库的类型设置要使用的目录(比如对于MySQL来说是 db/migration/mysql
)。
支持的数据库列表可以在 DatabaseDriver
中找到。
迁移也可以用Java编写。
Flyway将自动配置任何实现 JavaMigration
的bean。
FlywayProperties
提供了Flyway的大部分设置和一小部分额外的属性,可以用来禁用迁移或关闭位置检查。
如果你需要对配置进行更多的控制,可以考虑注册一个 FlywayConfigurationCustomizer
bean。
Spring Boot调用 Flyway.migrate()
来执行数据库迁移。
如果你想获得更多的控制权,可以提供一个实现 FlywayMigrationStrategy
的 @Bean
。
Flyway支持SQL和Java callback(回调)。
要使用基于SQL的回调,将回调脚本放在 classpath:db/migration
目录下。
要使用基于Java的回调,需要创建一个或多个实现 Callback
的bean。
任何这样的Bean都会自动在 Flyway
注册。
它们可以通过使用 @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
spring:
flyway:
locations: "classpath:/db/migration,classpath:/dev/db/migration"
在这样的设置下,dev/db/migration
中的迁移只在 dev
配置文件处于活动状态时运行。
9.5.2. 在启动时执行Liquibase数据库迁移
为了在启动时自动运行Liquibase数据库迁移,请将 org.liquibase:liquibase-core
添加到你的classpath。
当你把 |
默认情况下,主变更日志是从 db/changelog/db.changelog-master.yaml
中读取的,但你可以通过设置 spring.liquibase.change-log
来改变位置。
除了YAML之外,Liquibase还支持JSON、XML和SQL change日志格式。
默认情况下,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
以了解可用的设置,如上下文、默认schema和其他。
9.6. 依赖于一个初始化的数据库
数据库初始化是在应用程序启动时作为应用程序上下文刷新的一部分进行的。 为了允许在启动期间访问初始化的数据库,自动检测作为数据库初始化器的Bean和需要数据库被初始化的Bean。 那些初始化依赖于数据库已经被初始化的Bean被配置为依赖于那些初始化数据库的Bean。 如果在启动过程中,你的应用程序试图访问数据库,但它还没有被初始化,你可以配置额外的检测那些初始化数据库和要求数据库被初始化的bean。
9.6.1. 检测数据库初始化程序(Initializer)
Spring Boot会自动检测到初始化SQL数据库的以下类型的bean。
-
DataSourceScriptDatabaseInitializer
-
EntityManagerFactory
-
Flyway
-
FlywayMigrationInitializer
-
R2dbcScriptDatabaseInitializer
-
SpringLiquibase
如果你使用的是数据库初始化库的第三方启动器,它可能会提供一个检测器,这样其他类型的Bean也会被自动检测。
要让其他Bean被检测到,请在 META-INF/spring.factories
中注册一个 DatabaseInitializerDetector
的实现。
9.6.2. 检测依赖数据库初始化的 Bean
Spring Boot将自动检测以下类型的Bean,这取决于数据库的初始化。
-
AbstractEntityManagerFactoryBean
(除非spring.jpa.defer-datasource-initialization
被设置为true
。) -
DSLContext
(jOOQ) -
EntityManagerFactory
(除非spring.jpa.defer-datasource-initialization
被设置为true
。) -
JdbcOperations
-
NamedParameterJdbcOperations
如果你使用的是第三方starter数据访问库,它可能会提供一个检测器,这样其他类型的Bean也会被自动检测到。
要想检测其他Bean,请在 META-INF/spring.factories
中注册 DependsOnDatabaseInitializationDetector
的实现。
或者,用 @DependsOnDatabaseInitialization
来注释Bean的类或其 @Bean
方法。
10. NoSQL
Spring Boot提供了许多支持NoSQL技术的 starter。本节将回答在Spring Boot中使用NoSQL时出现的问题。
10.1. 用 Jedis 代替 Lettuce
默认情况下,Spring Boot starter(spring-boot-starter-data-redis
)使用 Lettuce。你需要排除这个依赖,而导入 Jedis 的依赖。Spring Boot 管理着这两个依赖,所以你可以切换到Jedis,而不需要指定一个版本。
下面的例子说明了如何在Maven中这样做:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
下面的例子展示了如何在Gradle中这样做:
dependencies {
implementation('org.springframework.boot:spring-boot-starter-data-redis') {
exclude group: 'io.lettuce', module: 'lettuce-core'
}
implementation 'redis.clients:jedis'
// ...
}
11. Messaging
Spring Boot提供了许多支持消息传递的starter。 本节将回答与Spring Boot一起使用消息传递时出现的问题。
11.1. 禁用事务 JMS Session
如果你的JMS broker不支持事务session,你必须完全禁用对事务的支持。
如果你创建了自己的 JmsListenerContainerFactory
,那就没什么可做的了,因为,默认情况下,它不能进行事务。
如果你想使用 DefaultJmsListenerContainerFactoryConfigurer
来重新使用Spring Boot的默认,你可以禁用事务会话,如下所示。
@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;
}
}
@Configuration(proxyBeanMethods = false)
class MyJmsConfiguration {
@Bean
fun jmsListenerContainerFactory(connectionFactory: ConnectionFactory?,
configurer: DefaultJmsListenerContainerFactoryConfigurer): DefaultJmsListenerContainerFactory {
val listenerFactory = DefaultJmsListenerContainerFactory()
configurer.configure(listenerFactory, connectionFactory)
listenerFactory.setTransactionManager(null)
listenerFactory.setSessionTransacted(false)
return listenerFactory
}
}
前面的例子覆盖了默认的工厂,它应该适用于你的应用程序定义的任何其他工厂,如果有的话。
12. 批(Batch)处理应用
当人们在Spring Boot应用程序中使用Spring Batch时,经常会出现一些问题。 本节将讨论这些问题。
12.1. 指定一个批处理数据源
默认情况下,批处理应用程序需要一个 DataSource
来存储 job detail。
Spring Batch默认期望有一个单一的 DataSource
。
要让它使用除应用程序的主 DataSource
以外的 DataSource
,请声明一个 DataSource
Bean,用`@BatchDataSource` 注解其 @Bean
方法。
如果你这样做了,并且想要两个数据源,记得要把另一个标记为 @Primary
。
要想获得更大的控制权,请将 @EnableBatchProcessing
添加到你的 @Configuration
类中或继承 DefaultBatchConfiguration
。参见 @EnableBatchProcessing
和 DefaultBatchConfiguration
的Javadoc获取更多细节。
关于Spring Batch的更多信息,请参阅 Spring Batch项目页面。
12.2. 在启动时运行Spring批处理作业
通过在应用程序的classpath中添加 spring-boot-starter-batch
,可以启用Spring Batch自动配置。
如果在 application context 中发现单个 Job
,它将在启动时被执行(详情见 JobLauncherApplicationRunner
)。
如果发现多个 Job
,必须使用 spring.batch.job.name
指定应该执行的Job。
要禁止运行在应用程序上下文中发现的 Job
,请将 spring.batch.job.enabled
设置为 false
。
12.3. 从命令行运行
Spring Boot将任何以 --
开头的命令行参数转换为属性,添加到 Environment
中,见访问命令行属性。
这不应该被用来传递参数给批处理作业。
要在命令行上指定批处理参数,请使用常规格式(即没有 --
),如下面的例子所示。
$ java -jar myapp.jar someParameter=someValue anotherParameter=anotherValue
如果你在命令行中指定了 Environment
的属性,它就会被Job所忽略。
考虑一下下面的命令。
$ java -jar myapp.jar --server.port=7070 someParameter=someValue
这只为 batch job 提供一个参数:someParameter=someValue
。
12.4. 储存 Job Repository
Spring Batch需要为 Job
repository 建立一个 data store。
如果你使用Spring Boot,你必须使用一个实际的数据库。
注意,它可以是一个内存数据库,见 配置Job Repository。
13. Actuator
Spring Boot包括Spring Boot Actuator。 本节回答了在使用它时经常出现的问题。
13.1. 改变Actuator端点的HTTP端口或地址
在一个独立的应用程序中,执行器的HTTP端口默认为与主HTTP端口相同。
要使应用程序监听一个不同的端口,可以设置外部属性: management.server.port
。
要在一个完全不同的网络地址上进行监听(例如,当你有一个内部网络用于管理,而一个外部网络用于用户应用程序),你也可以将 management.server.address
设置为一个有效的IP地址,服务器能够与之绑定。
更多细节,请参见 ManagementServerProperties
源代码和 “生产就绪特性” 中的 “actuator.html” 部分。
13.2. 自定义 ‘whitelabel’ 异常页面
Spring Boot安装了一个 ‘whitelabel’ 错误页面,如果遇到服务器错误,你会在浏览器客户端看到这个页面(消费JSON和其他媒体类型的机器客户端应该看到一个带有正确错误代码的合理响应)。
设置 server.error.whitelabel.enabled=false 来关闭默认错误页面。
这样做可以恢复你所使用的servlet容器的默认值。
注意,Spring Boot仍然试图解决错误视图,所以你可能应该添加你自己的错误页面,而不是完全禁用它。
|
用你自己的错误页面覆盖取决于你使用的模板技术。
例如,如果你使用Thymeleaf,你可以添加一个 error.html
模板。
如果你使用FreeMarker,你可以添加一个 error.ftlh
模板。
一般来说,你需要一个以 error
为名称的 View
或一个处理 /error
路径的 @Controller
来解析。
除非你替换了一些默认配置,否则你应该在你的 ApplicationContext
中找到一个 BeanNameViewResolver
,所以一个名为 error
的 @Bean
是一种方法。
参见 ErrorMvcAutoConfiguration
获取更多选项。
关于如何在Servlet容器中注册处理程序的细节,也请参见 “异常处理” 一节。
13.3. 对敏感数据进行脱敏
由 /env
、/configprops
和 /quartz
端点返回的信息可能有些敏感。
默认情况下,所有的值都经过脱敏处理(即用 ******
代替)。
查看未经脱敏的原始值可以使用每个端点的 showValues
属性进行配置。
这个属性可以被配置为有以下值。
-
ALWAYS
- 所有的值都以其未脱敏的形式显示给所有的用户 -
NEVER
- 所有的值都是经过脱敏处理的(即用******
代替)。 -
WHEN_AUTHORIZED
- 所有的值都以其未脱敏的形式显示给授权用户。
对于HTTP端点来说,如果用户已经认证并拥有端点的角色属性所配置的角色,则被认为是授权的。 默认情况下,任何经过认证的用户都是授权的。 对于JMX端点,所有用户总是被授权。
management.endpoint.env.show-values=WHEN_AUTHORIZED
management.endpoint.env.roles=admin
management:
endpoint:
env:
show-values: WHEN_AUTHORIZED
roles: "admin"
上面的配置使所有具有 admin
角色的用户能够从 /env
端点查看所有原始形式的值。
当 show-values 被设置为 ALWAYS 或 WHEN_AUTHORIZED 时,任何由 SanitizingFunction 应用的脱敏处理仍将被应用。
|
13.4. 将健康指标(Health Indicator)映射到 Micrometer Metric
Spring Boot的健康指标返回一个 Status
类型,以显示整个系统的健康状况。
如果你想监测或警告某个特定应用程序的健康水平,你可以用 Micrometer 把这些状态导出为指标。
默认情况下,Spring Boot使用状态代码 “UP”、“DOWN”、“OUT_OF_SERVICE” 和 “UNKNOWN”。
要导出这些,你需要将这些状态转换为一些数字集,以便它们可以被 Micrometer Gauge
使用。
下面的例子显示了编写这种exporter的一种方法。
@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;
}
}
@Configuration(proxyBeanMethods = false)
class MyHealthMetricsExportConfiguration(registry: MeterRegistry, healthEndpoint: HealthEndpoint) {
init {
// This example presumes common tags (such as the app) are applied elsewhere
Gauge.builder("health", healthEndpoint) { health ->
getStatusCode(health).toDouble()
}.strongReference(true).register(registry)
}
private fun getStatusCode(health: HealthEndpoint): Int {
val status = health.health().status
if (Status.UP == status) {
return 3
}
if (Status.OUT_OF_SERVICE == status) {
return 2
}
if (Status.DOWN == status) {
return 1
}
return 0
}
}
14. Security
本节讨论使用Spring Boot时的安全问题,包括在Spring Boot中使用Spring Security产生的问题。
关于Spring Security的更多信息,请参阅 Spring Security项目页面。
14.1. 关闭 Spring Boot Security 配置
如果你在应用程序中定义了一个带有 SecurityFilterChain
bean 的 @Configuration
,它将关闭Spring Boot中默认的webapp安全设置。
14.2. 修改 UserDetailsService 和添加用户账户
如果你提供了一个 @Bean
类型的 AuthenticationManager
、AuthenticationProvider
或 UserDetailsService
,那么 InMemoryUserDetailsManager
的默认 @Bean
不会被创建。
这意味着你拥有Spring Security的全部功能集(如 各种认证选项)。
添加用户账户的最简单方法是提供你自己的 UserDetailsService
bean。
14.3. 运行在代理服务器后面时启用HTTPS
确保你的所有主要端点都只能通过HTTPS来使用,这对任何应用程序来说都是一项重要的工作。
如果你使用Tomcat作为servlet容器,那么Spring Boot如果检测到一些环境设置,就会自动添加Tomcat自己的 RemoteIpValve
,而且你应该能够依靠 HttpServletRequest
来报告它是否安全(甚至在处理真正的SSL termination的代理服务器的下游)。
标准行为是由某些请求头(x-forwarded-for
和 x-forwarded-proto
)的存在与否决定的,其名称是常规的,所以它应该与大多数前端代理一起工作。
你可以通过在 application.properties
中添加一些条目来打开这个valve,如下例所示。
server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto
server:
tomcat:
remoteip:
remote-ip-header: "x-forwarded-for"
protocol-header: "x-forwarded-proto"
(这两个属性中的任何一个的存在,都会开启valve。
另外,你可以通过使用 WebServerFactoryCustomizer
Bean 来定制 TomcatServletWebServerFactory
来添加 RemoteIpValve
。)
要配置Spring Security,使所有(或部分)请求都需要安全通道,可以考虑添加你自己的 SecurityFilterChain
bean,该bean添加了以下 HttpSecurity
配置。
@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();
}
}
@Configuration
class MySecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
// Customize the application security ...
http.requiresChannel { requests -> requests.anyRequest().requiresSecure() }
return http.build()
}
}
15. 热交换
Spring Boot支持热交换。 本节回答关于它如何工作的问题。
15.1. 重新加载静态内容
有几种热重载的选择。推荐的方法是使用 spring-boot-devtools
,因为它提供了额外的开发时功能,如支持快速应用重启和LiveReload,以及合理的开发时配置(如模板缓存)。Devtools通过监控classpath的变化而工作。这意味着静态资源的变化必须被 "built" 以使其生效。默认情况下,在Eclipse中,当你保存你的更改时,这将自动发生。在IntelliJ IDEA中,Make Project命令会触发必要的构建。由于默认的重启排除,对静态资源的更改不会触发应用程序的重启。然而,它们确实会触发实时重载。
另外,在IDE中运行(尤其是在调试开启的情况下)也是一种很好的开发方式(所有现代的IDE都允许重新加载静态资源,通常也允许热交换Java类的变化)。
最后,可以对 Maven和Gradle插件 进行配置(见 addResources
属性),以支持从命令行运行,直接从源码重新加载静态文件。如果你用更高级别的工具编写该代码,你可以用外部css/js编译器进程来使用。
15.2. 在不重启容器的情况下重新加载模板
Spring Boot支持的大多数模板技术都包括一个禁用缓存的配置选项(本文稍后描述)。如果你使用 spring-boot-devtools
模块,这些属性会在开发时自动为你配置。
15.2.1. Thymeleaf 模板
如果你使用Thymeleaf,将 spring.thymeleaf.cache
设置为 false
。
参见 ThymeleafAutoConfiguration
了解其他Thymeleaf定制选项。
15.2.2. FreeMarker 模板
如果你使用FreeMarker,将 spring.freemarker.cache
设置为 false
。
参见 FreeMarkerAutoConfiguration
,了解其他FreeMarker定制选项。
15.2.3. Groovy 模板
如果你使用Groovy模板,请将 spring.groovy.template.cache
设置为 false
。
参见 GroovyTemplateAutoConfiguration
了解其他Groovy定制选项。
15.3. 快速重启应用程序
spring-boot-devtools
模块包括对自动重新启动应用程序的支持。虽然没有像 JRebel 这样的技术那么快,但通常比 "冷启动" 快得多。在研究本文档后面讨论的一些更复杂的重载选项之前,你也许应该先试一下它。
更多细节,见 using.html 部分。
16. 测试
Spring Boot包括一些测试工具和支持类,以及一个专门的starter,提供常见的测试依赖。 本节回答关于测试的常见问题。
16.1. 用Spring Security进行测试
Spring Security提供了对作为特定用户运行测试的支持。
例如,下面的代码段中的测试将以拥有 ADMIN
角色的认证用户运行。
@WebMvcTest(UserController.class)
class MySecurityTests {
@Autowired
private MockMvc mvc;
@Test
@WithMockUser(roles = "ADMIN")
void requestProtectedUrlWithUser() throws Exception {
this.mvc.perform(get("/"));
}
}
@WebMvcTest(UserController::class)
class MySecurityTests(@Autowired val mvc: MockMvc) {
@Test
@WithMockUser(roles = ["ADMIN"])
fun requestProtectedUrlWithUser() {
mvc.perform(MockMvcRequestBuilders.get("/"))
}
}
Spring Security提供了与Spring MVC Test的全面集成,在使用 @WebMvcTest
片段和 MockMvc
测试controller时也可以使用。
关于Spring Security的测试支持的其他细节,请参阅Spring Security的 参考文档。
16.2. 结构化 "@Configuration" 类,以纳入片段测试中
片段测试通过限制Spring框架的组件扫描,使其在有限的组件类型的基础上工作。
对于任何不是通过组件扫描创建的Bean,例如,使用 @Bean
注解创建的Bean,切片测试将无法从应用上下文中包含/排除它们。
考虑一下这个例子。
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((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,这样你就可以测试你的控制器端点是否被正确保护。
然而,MyConfiguration
没有被 @WebMvcTest
的组件扫描过滤器接收,因为它不符合过滤器所指定的任何类型。
你可以通过在测试类中注解 @Import(MyConfiguration.class)
来明确包含配置。
这将加载 MyConfiguration
中的所有Bean,包括 BasicDataSource
Bean,在测试Web层时不需要。
将配置类分成两个,就可以只导入security configuration。
@Configuration(proxyBeanMethods = false)
public class MySecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated());
return http.build();
}
}
@Configuration(proxyBeanMethods = false)
public class MyDatasourceConfiguration {
@Bean
@ConfigurationProperties("app.datasource.second")
public BasicDataSource secondDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}
}
当某个领域的Bean需要包含在片段测试中时,拥有一个单一的配置类可能是低效的。 相反,将应用程序的配置结构化为多个带有特定领域的Bean的细化类,可以只为特定的片断测试导入这些Bean。
17. 构建
Spring Boot包括Maven和Gradle的构建插件。 本节回答有关这些插件的常见问题。
17.1. 生成构建信息
Maven插件和Gradle插件都允许生成包含项目的坐标、名称和版本的构建信息。
这些插件还可以通过配置来增加额外的属性。
当有这样的文件时,Spring Boot会自动配置一个 BuildProperties
bean。
要用Maven生成构建信息,请为 build-info
goal 添加一个execution,如下例所示。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
下面的例子也是用Gradle做的。
springBoot {
buildInfo()
}
更多细节请参见 Spring Boot Gradle插件文档。 |
17.2. 生成Git信息
Maven和Gradle都允许生成 git.properties
文件,其中包含项目构建时 git
源代码库的状态信息。
对于Maven用户,spring-boot-starter-parent
POM包含一个预先配置的插件,用于生成 git.properties
文件。要使用它,请在你的POM中为 Git Commit Id Plugin
添加以下声明。
<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.4.1"
}
Maven和Gradle插件都允许配置 git.properties
中包含的属性。
git.properties 中的提交时间应符合以下格式。yyyy-MM-dd’T’HH:mm:ssZ 。
这是上面列出的两个插件的默认格式。
使用这种格式可以将时间解析为 Date ,当序列化为JSON时,其格式由Jackson的日期序列化配置设置控制。
|
17.3. 自定义依赖版本
spring-boot-dependencies
POM管理常见依赖的版本。
Maven和Gradle的Spring Boot插件允许使用构建属性来定制这些管理的依赖版本。
每个Spring Boot版本都是针对这组特定的第三方依赖关系设计和测试的。 覆盖版本可能会导致兼容性问题。 |
要用Maven覆盖依赖版本,请参见Maven插件文档的 这一部分。
要在Gradle中覆盖依赖版本,请看Gradle插件的 这部分 文档。
17.4. 用Maven创建可执行的JAR
spring-boot-maven-plugin
可以用来创建一个可执行的 “fat” JAR。
如果你使用 spring-boot-starter-parent
POM,你可以声明该插件,你的jar被repackaged,如下所示。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果你不使用父POM,你仍然可以使用该插件。
但是,你必须另外添加一个 <executions>
部分,如下所示。
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.0-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
有关完整的使用细节,请参见 插件文档。
17.5. 将Spring Boot应用作为依赖(Dependency)使用
与war文件一样,Spring Boot应用程序不打算作为一个依赖关系使用。 如果你的应用程序包含你想与其他项目共享的类,建议的方法是将这些代码移到一个单独的模块中。 然后,这个单独的模块就可以被你的应用程序和其他项目所依赖了。
如果你不能按照上面的建议重新安排你的代码,必须对Spring Boot的Maven和Gradle插件进行配置,以产生一个适合作为依赖的独立工件。可执行归档文件不能作为依赖使用,因为 可执行jar格式 将 application 类打包在 BOOT-INF/classes
中。这意味着,当可执行jar被用作依赖时,它们无法被找到。
为了产生两个工件,一个可以作为依赖,一个是可执行的,必须指定一个 classifier(分类器)。 这个分类器被应用于可执行归档文件的名称,留下默认的归档文件作为依赖使用。
要在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.6. 当一个可执行的Jar运行时提取特定的库
大多数嵌套在可执行jar中的库不需要解压就可以运行。
然而,某些库会有问题。
例如,JRuby包括它自己的嵌套jar支持,它假定 jruby-complete.jar
总是作为一个文件直接可用。
为了处理任何有问题的库,你可以标记特定的嵌套jar在可执行jar第一次运行时应自动解包。
这些嵌套的jar被写在由 java.io.tmpdir
系统属性(system property)确定的临时目录下面。
应注意确保你的操作系统被配置成在应用程序仍在运行时不会删除已解压到临时目录的jar。 |
例如,为了表明JRuby应被标记为使用Maven插件进行解包,你可以添加以下配置。
<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.7. 用 Exclusions 创建一个不可执行的JAR
通常,如果你有一个可执行的和一个不可执行的jar作为两个独立的构建产品,可执行的版本有额外的配置文件,这些文件在库jar中是不需要的。
例如,application.yaml
配置文件可能被排除在非可执行JAR中。
在Maven中,可执行jar必须是主要工件,你可以为库添加一个 classified (分类) 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.yaml</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
17.8. 用Maven开始远程调试Spring Boot应用程序
要在用Maven启动的Spring Boot应用程序上附加一个远程调试器,可以使用 maven插件 的 jvmArguments
属性。
更多细节见 本例。
17.9. 在不使用spring-boot-antlib的情况下从Ant构建可执行文件
要用Ant进行构建,你需要抓取依赖项,进行编译,然后创建一个jar或war归档文件。
为了使其可执行,你可以使用 spring-boot-antlib
模块,或者你可以按照这些说明。
-
如果你正在构建一个jar,将应用程序的类和资源打包在一个嵌套的
BOOT-INF/classes
目录下。 如果你正在构建一个war,像往常一样将应用程序的类打包在一个嵌套的WEB-INF/classes
目录下。 -
对于jar来说,在一个嵌套的
BOOT-INF/lib
目录下添加运行时依赖项,对于war来说,在WEB-INF/lib
目录下添加运行时依赖项。 记住 不要 压缩存档中的条目。 -
在嵌套的
BOOT-INF/lib
目录中添加provided
(嵌入式容器)依赖,如果是jar,则添加WEB-INF/lib-provided
目录,如果是war,则添加WEB-INF/lib-provided
。 记住 不要 压缩存档中的条目。 -
在存档的根部添加
spring-boot-loader
类(以便Main-Class
可用)。 -
使用适当的 launcher(比如jar文件的
JarLauncher
)作为manifest中的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>
18. 超前(Ahead-of-time)处理
人们在使用Spring Boot应用程序的超前处理时,经常会出现一些问题。本节讨论这些问题。
18.1. Condition
超前处理可以优化应用程序,并在构建时根据环境评估 conditions。Profiles 是通过 condition 实现的,因此也会受到影响。
如果你想在一个超前优化的应用程序中根据条件创建Bean,你必须在构建应用程序时设置好环境。在构建时超前处理时创建的Bean在运行应用程序时总是被创建的,并且不能被关闭。要做到这一点,你可以设置在构建应用程序时应该使用的 profiles。
对于Maven来说,这可以通过设置 spring-boot-maven-plugin:process-aot
execution 的 profiles
配置来实现:
<profile>
<id>native</id>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-aot</id>
<configuration>
<profiles>profile-a,profile-b</profiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
对于 Gradle,你需要配置 ProcessAot
任务:
tasks.withType(org.springframework.boot.gradle.tasks.aot.ProcessAot).configureEach {
args('--spring.profiles.active=profile-a,profile-b')
}
在运行超前优化的应用程序时,支持只改变不影响条件的配置属性的配置文件,不受限制。
19. 传统的部署方式
Spring Boot支持传统的部署,也支持更现代的部署形式。 本节回答关于传统部署的常见问题。
19.1. 创建一个可部署的WAR文件
由于Spring WebFlux并不严格依赖servlet API,而且应用程序默认部署在嵌入式Reactor Netty服务器上,因此WebFlux应用程序不支持War部署。 |
制作可部署的war文件的第一步是提供一个 SpringBootServletInitializer
子类并重写其 configure
方法。
这样做可以利用Spring框架对servlet 3.0的支持,让你在Servlet容器启动时配置你的应用程序。
通常,你应该更新你的应用程序的main类来扩展 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);
}
}
@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {
override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
return application.sources(MyApplication::class.java)
}
}
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
下一步是更新你的构建配置,使你的项目产生一个war文件而不是jar文件。
如果你使用Maven和 spring-boot-starter-parent
(它为你配置了Maven的war插件),你只需修改 pom.xml
,将打包改为war,如下所示。
<packaging>war</packaging>
如果你使用Gradle,你需要修改 build.gradle
以将war插件应用到项目中,如下所示。
apply plugin: 'war'
这个过程的最后一步是确保嵌入式servlet容器不会干扰war文件所部署的servlet容器。 要做到这一点,你需要把嵌入式servlet容器的依赖标记为 provided。
如果你使用Maven,下面的例子将servlet容器(本例中为Tomcat)标记为 provided。
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- ... -->
</dependencies>
如果你使用Gradle,下面的例子将servlet容器(本例中为Tomcat)标记为 provided。
dependencies {
// ...
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// ...
}
providedRuntime 比Gradle的 compileOnly 配置要好。
在其他限制中,compileOnly 的依赖不在测试的classpath上,所以任何基于web的集成测试都会失败。
|
如果你使用 Spring Boot构建工具,将嵌入式servlet容器的依赖性标记为 provided,会产生一个可执行的war文件,并将提供的依赖打包在 lib-provided
目录下。这意味着,除了可以部署到servlet容器外,你还可以通过在命令行上使用 java -jar
来运行你的应用程序。
19.2. 将现有的应用程序转换为Spring Boot
要将现有的非web Spring应用转换为Spring Boot应用,请替换创建 ApplicationContext
的代码,并将其替换为对 SpringApplication
或 SpringApplicationBuilder
的调用。Spring MVC Web应用程序通常可以先创建一个可部署的war应用程序,之后再将其迁移到可执行的war或jar中。请参阅 《入门指南》中关于将jar转换为war的内容。
要通过扩展 SpringBootServletInitializer
(例如,在一个名为 Application
的类中)并添加Spring Boot @SpringBootApplication
注解来创建一个可部署的war,请使用与下面例子中所示类似的代码。
@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 (through
// @SpringBootApplication)
// we actually do not need to override this method.
return application;
}
}
@SpringBootApplication
class MyApplication : SpringBootServletInitializer() {
override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder {
// Customize the application or call application.sources(...) to add sources
// Since our example is itself a @Configuration class (through @SpringBootApplication)
// we actually do not need to override this method.
return application
}
}
请记住,无论你在 sources
中放了什么,都只是一个Spring的 ApplicationContext
。
通常情况下,任何已经工作的东西都应该在这里工作。
可能有一些Bean你可以稍后删除,让Spring Boot为它们提供自己的默认值,但在你需要这么做之前,应该可以得到一些工作。
静态资源可以移到classpath根中的 /public
(或 /static
或 /resources
或 /META-INF/resources
)。
这同样适用于 messages.properties
(Spring Boot自动检测到classpath根部)。
对Spring DispatcherServlet
和Spring Security的简单使用应该不需要进一步修改。
如果你的应用程序有其他功能(例如,使用其他Servlet或过滤器),你可能需要在 Application
上下文中添加一些配置,替换 web.xml
中的那些元素,如下所示。
-
一个
@Bean
类型的Servlet
或ServletRegistrationBean
在容器中安装该Bean,就像在web.xml
中的<servlet/>
和<servlet-mapping/>
一样。 -
一个
@Bean
类型的Filter
或FilterRegistrationBean
的行为类似(作为一个<filter/>
和<filter-mapping/>
)。 -
一个XML文件中的
ApplicationContext
可以通过@ImportResource
添加到你的Application
中。 另外,在注解配置(configuration)已经被大量使用的情况下,可以在几行中重新创建@Bean
定义。
一旦战war件开始工作,你可以通过在你的 Application
中添加一个 main
方法使其可执行,如下面的例子所示。
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
如果你打算以war或可执行程序的形式启动你的应用程序,你需要在一个方法中分享构建器的定制,该方法既可用于 Java
Kotlin
|
应用程序可以归入一个以上的类别。
-
Servlet 3.0以上的应用程序,没有
web.xml
。 -
有
web.xml
的应用程序。 -
有context层次结构的应用程序。
-
没有context层次结构的应用程序。
所有这些都应该是可以转换的,但每个人可能需要稍微不同的技术。
如果Servlet 3.0+应用程序已经使用了Spring Servlet 3.0+初始化器支持类,那么它们可能会很容易转化。通常情况下,现有 WebApplicationInitializer
的所有代码都可以移到 SpringBootServletInitializer
中。如果你现有的应用程序有一个以上的 ApplicationContext
(例如,如果它使用 AbstractDispatcherServletInitializer
),那么你也许可以将所有的上下文源合并到一个 SpringApplication
中。你可能遇到的主要复杂情况是,如果合并不成功,你需要维护上下文的层次结构。请参阅关于构建层次结构的条目,以了解实例。一个包含Web特定功能的现有父级上下文通常需要被拆分,以便所有 ServletContextAware
组件都在子级上下文中。
还不是 Spring application 的应用程序可能会被转换为 Spring Boot application,前面提到的指导可能会有所帮助。但是,你可能还会遇到问题。在这种情况下,我们建议 在Stack Overflow上提问,标签为 spring-boot
。
19.3. 将一个WAR部署到WebLogic上
要将Spring Boot应用程序部署到WebLogic,你必须确保你的servlet initializer 直接 实现 WebApplicationInitializer
(即使你从已经实现它的基类中扩展)。
一个典型的 WebLogic 的 initializer 应该类似于下面的例子。
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
}
@SpringBootApplication
class MyApplication : SpringBootServletInitializer(), 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>
20. Docker Compose
本节包括与Spring Boot中的Docker Compose支持有关的主题。
20.1. 自定义JDBC URL
在 Docker Compose 中使用 JdbcConnectionDetails
时,JDBC URL的参数可以通过对服务应用 org.springframework.boot.jdbc.parameters
label 来定制。比如说:
services:
postgres:
image: 'postgres:15.3'
environment:
- 'POSTGRES_USER=myuser'
- 'POSTGRES_PASSWORD=secret'
- 'POSTGRES_DB=mydb'
ports:
- '5432:5432'
labels:
org.springframework.boot.jdbc.parameters: 'ssl=true&sslmode=require'
有了这个 Docker Compose 文件,使用的JDBC URL是 jdbc:postgresql://127.0.0.1:5432/mydb?ssl=true&sslmode=require
。