本部分将介绍 Spring Boot 相关的细节内容. 在这里,您可以学习到可能需要使用和自定义的主要功能. 您如果还没有做好充分准备,可能需要阅读 "入门" 和 "使用 Spring Boot" ,以便打下前期基础.
1. SpringApplication
SpringApplication
类提供了一种可通过运行 main()
方法来启动 Spring 应用的简单方式. 多数情况下,您只需要委托给静态的 SpringApplication.run
方法:
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}
当应用启动时,您应该会看到类似以下的内容输出:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: v2.2.13.RELEASE 2019-04-31 13:09:54.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb) 2019-04-31 13:09:54.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy 2019-04-01 13:09:56.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080 2019-04-01 13:09:57.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)
默认情况下,将显示 INFO
级别的日志信息,包括一些应用启动相关信息. 如果您需要修改 INFO
日志级别,请参考日志等级.
使用主应用程序类包中的实现版本来确定应用程序版本. 可以通过将 spring.main.log-startup-info
设置为 false
来关闭启动信息记录. 这还将关闭对应用程序 active 配置文件的日志记录.
要在启动期间添加其他日志记录,可以在 SpringApplication 的子类中重写 logStartupInfo(boolean) .
|
1.1. 启动失败
如果您的应用无法启动,注册的 FailureAnalyzers
可能会提供有相关的错误信息和解决问题的具体方法. 例如,如果您在已经被占用的 8080
端口上启动了一个 web 应用,会看到类似以下的错误信息:
*************************** APPLICATION FAILED TO START *************************** Description: Embedded servlet container failed to start. Port 8080 was already in use. Action: Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
Spring Boot 提供了许多的 FailureAnalyzer 实现,您也可以添加自己的实现.
|
如果没有失败分析器能够处理的异常,您仍然可以显示完整的条件报告以便更好地了解出现的问题. 为此,您需要针对 org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
启用 debug
属性 或者开启 DEBUG
日志.
例如,如果您使用 java -jar
运行应用,可以按以下方式启用 debug
属性:
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug
1.2. 延迟初始化
SpringApplication
允许延迟地初始化应用程序. 启用延迟初始化后,将根据需要创建bean,而不是在应用程序启动期间创建bean. 因此,启用延迟初始化可以减少应用程序启动所需的时间. 在Web应用程序中,启用延迟初始化将导致许多与Web相关的Bean直到收到HTTP请求后才被初始化.
延迟初始化的缺点是,它可能会延迟发现应用程序问题的时间. 如果错误配置的Bean延迟初始化,则启动期间将不再发生故障,并且只有在初始化Bean时问题才会变得明显. 还必须注意确保JVM有足够的内存来容纳所有应用程序的bean,而不仅仅是启动期间初始化的bean. 由于这些原因,默认情况下不会启用延迟初始化,因此建议在启用延迟初始化之前先对JVM的堆大小进行微调.
可以使用 SpringApplicationBuilder
上的 lazyInitialization
方法或 SpringApplication
上的 setLazyInitialization
方法以编程方式启用延迟初始化. 另外,可以使用 spring.main.lazy-initialization
属性启用它,如以下示例所示:
spring.main.lazy-initialization=true
如果要在对应用程序其余部分使用延迟初始化时禁用某些bean的延迟初始化,则可以使用 @Lazy(false) 注解将它们的延迟属性显式设置为 false .
|
1.3. 自定义 banner
可以通过在 classpath 下添加一个 banner.txt
文件,或者将 spring.banner.location
属性指向该文件的位置来更改启动时打印的 banner. 如果文件采用了非 UTF-8 编码,您可以设置 spring.banner.charset
来解决. 除了文本文件,
您还可以将 banner.gif
、banner.jpg
或者 banner.png
图片文件添加到 classpath 下,或者设置 spring.banner.image.location
属性. 指定的图片将会被转换成 ASCII 形式并打印在 banner 文本上方.
您可以在 banner.txt
文件中使用以下占位符:
变量 | 描述 |
---|---|
|
您的应用版本号,声明在 |
|
您的应用版本号,声明在 |
|
您使用的 Spring Boot 版本. 例如 |
|
您使用的 Spring Boot 版本格式化之后显示 (用括号括起来,以 |
|
其中 |
|
您的应用标题,声明在 |
如果您想以编程的方式生成 banner,可以使用 SpringApplication.setBanner(…) 方法. 使用 org.springframework.boot.Banner 接口并实现自己的 printBanner() 方法.
|
您还可以使用 spring.main.banner-mode
属性来确定是否必须在 System.out
(console
) 上打印 banner,还是使用日志记录器 (log
)或者都不打印(off
).
打印的 banner 被注册名为 springBootBanner
的单例 bean.
1.4. 自定义 SpringApplication
如果 SpringApplication
的默认设置不符合您的想法,您可以创建本地实例进行定制化. 例如,要关闭 banner,您可以这样:
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringConfiguration.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
传入 SpringApplication 的构造参数是 spring bean 的配置源. 大多情况下是引用 @Configuration 类,但您也可以引用 XML 配置或者被扫描的包.
|
也可以使用 application.properties
文件配置 SpringApplication
. 有关详细信息,请参见_外部化配置_.
关于配置选项的完整列表,请参阅 SpringApplication
Javadoc.
1.5. Fluent Builder API
如果您需要构建一个有层级关系的 ApplicationContext
(具有父/子关系的多上下文) ,或者偏向使用 fluent (流式) 构建器 API,可以使用 SpringApplicationBuilder
.
SpringApplicationBuilder
允许您链式调用多个方法,包括能创建出具有层次结构的 parent
和 child
方法.
例如:
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
创建层级的 ApplicationContext 时有部分限制,比如 Web 组件必须包含在子上下文中,并且相同的 Environment 将作用于父子上下文. 有关详细信息,请参阅 SpringApplicationBuilder Javadoc .
|
1.6. 应用程序事件与监听器
除了常见的 Spring Framework 事件,比如 ContextRefreshedEvent
,SpringApplication
还会发送其他应用程序事件.
在 org.springframework.context.ApplicationListener=com.example.project.MyListener |
当您运行应用时,应用程序事件将按照以下顺序发送:
-
在开始应用开始运行但还没有进行任何处理时 (除了注册监听器和初始化器[initializer]) ,将发送
ApplicationStartingEvent
. -
当 Environment 被上下文使用,但是在上下文创建之前,将发送
ApplicationEnvironmentPreparedEvent
. -
准备
ApplicationContext
并调用ApplicationContextInitializers
之后但在加载任何bean定义之前,将发送ApplicationContextInitializedEvent
. -
开始刷新之前,bean 定义被加载之后发送
ApplicationPreparedEvent
. -
在上下文刷新之后且所有的应用和命令行运行器 (command-line runner) 被调用之前发送
ApplicationStartedEvent
. -
在应用程序和命令行运行器 (command-line runner) 被调用之后,将发出
ApplicationReadyEvent
,该事件用于通知应用已经准备处理请求. -
如果启动时发生异常,将发送
ApplicationFailedEvent
.
上面的列表仅包含绑定到 SpringApplication
的 SpringApplicationEvents
. 除这些以外,以下事件也在 ApplicationPreparedEvent
之后和 ApplicationStartedEvent
之前发布:
-
刷新
ApplicationContext
时,将发送ContextRefreshedEvent
. -
WebServer
准备就绪后,将发送WebServerInitializedEvent
.ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是servlet和reactive变量.
您可能不会经常使用应用程序事件,但了解他们的存在还是很有必要的. 在框架内部,Spring Boot 使用这些事件来处理各种任务. |
应用程序事件发送使用了 Spring Framework 的事件发布机制. 该部分机制确保在子上下文中发布给监听器的事件也会发布给所有祖先上下文中的监听器. 因此,如果您的应用程序使用有层级结构的 SpringApplication
实例,则监听器可能会收到同种类型应用程序事件的多个实例.
为了让监听器能够区分其上下文事件和后代上下文事件,您应该注入其应用程序上下文,然后将注入的上下文与事件的上下文进行比较. 可以通过实现 ApplicationContextAware
来注入上下文,如果监听器是 bean,则使用 @Autowired
注入上下文.
1.7. Web 环境
SpringApplication
试图为您创建正确类型的 ApplicationContext
. 确定 WebApplicationType
的算法非常简单:
-
如果存在 Spring MVC,则使用
AnnotationConfigServletWebServerApplicationContext
-
如果 Spring MVC 不存在且存在 Spring WebFlux,则使用
AnnotationConfigReactiveWebServerApplicationContext
-
否则,使用
AnnotationConfigApplicationContext
这意味着如果您在同一个应用程序中使用了 Spring MVC 和 Spring WebFlux 中的新 WebClient
,默认情况下将使用 Spring MVC. 您可以通过调用 setWebApplicationType(WebApplicationType)
修改默认行为.
也可以调用 setApplicationContextClass(…)
来完全控制 ApplicationContext
类型.
在 JUnit 测试中使用 SpringApplication 时,通常需要调用 setWebApplicationType(WebApplicationType.NONE) .
|
1.8. 访问应用程序参数
如果您需要访问从 SpringApplication.run(…)
传入的应用程序参数,可以注入一个 org.springframework.boot.ApplicationArguments
bean. ApplicationArguments
接口提供了访问原始 String[]
参数以及解析后的 option
和 non-option
参数的方法:
import org.springframework.boot.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
@Component
public class MyBean {
@Autowired
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
// if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
}
}
Spring Boot 还向 Spring Environment 注册了一个 CommandLinePropertySource . 这允许您可以使用 @Value 注解注入单个应用参数.
|
1.9. 使用 ApplicationRunner 或 CommandLineRunner
如果您需要在 SpringApplication
启动时运行一些代码,可以实现 ApplicationRunner
或者 CommandLineRunner
接口. 这两个接口的工作方式是一样的,都提供了一个单独的 run
方法,它将在 SpringApplication.run(…)
完成之前调用.
CommandLineRunner
接口提供了访问应用程序字符串数组形式参数的方法,而 ApplicationRunner
则使用了上述的 ApplicationArguments
接口. 以下示例展示 CommandLineRunner
和 run
方法的使用:
import org.springframework.boot.*;
import org.springframework.stereotype.*;
@Component
public class MyBean implements CommandLineRunner {
public void run(String... args) {
// Do something...
}
}
如果您定义了多个 CommandLineRunner
或者 ApplicationRunner
bean,则必须指定调用顺序,您可以实现 org.springframework.core.Ordered
接口,也可以使用 org.springframework.core.annotation.Order
注解解决顺序问题.
1.10. 应用程序退出
每个 SpringApplication
注册了一个 JVM 关闭钩子,以确保 ApplicationContext
在退出时可以优雅关闭. 所有标准的 Spring 生命周期回调 (比如 DisposableBean
接口,或者 @PreDestroy
注解) 都可以使用.
此外,如果希望在调用 SpringApplication.exit()
时返回特定的退出码,则 bean 可以实现 org.springframework.boot.ExitCodeGenerator
接口. 之后退出码将传递给 System.exit()
以将其作为状态码返回,如示例所示:
@SpringBootApplication
public class ExitCodeApplication {
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}
public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(ExitCodeApplication.class, args)));
}
}
此外,ExitCodeGenerator
接口可以通过异常实现. 遇到这类异常时,Spring Boot 将返回实现的 getExitCode()
方法提供的退出码.
1.11. 管理功能
可以通过指定 spring.application.admin.enabled
属性来为应用程序启用管理相关的功能. 其将在 MBeanServer
平台上暴露 SpringApplicationAdminMXBean
. 您可以使用此功能来远程管理 Spring Boot 应用. 该功能对服务包装器的实现也是非常有用的.
如果您想知道应用程序在哪一个 HTTP 端口上运行,请使用 local.server.port 键获取该属性.
|
2. 外部化配置
Spring Boot 可以让您的配置外部化,以便可以在不同环境中使用相同的应用程序代码. 您可以使用 properties 文件、YAML 文件、环境变量或者命令行参数来外部化配置. 可以使用 @Value
注解将属性值直接注入到 bean 中,可通过 Spring 的 Environment
访问,
或者通过 @ConfigurationProperties
绑定到结构化对象.
Spring Boot 使用了一个非常特别的 PropertySource
指令,用于智能覆盖默认值. 属性将按照以下顺序处理:
-
当 devtools 被激活,
$HOME/.config/spring-boot
文件夹中的 Devtools 全局设置属性. -
在测试中使用到的
@TestPropertySource
注解. -
在测试中使用到的
properties
属性,可以是@SpringBootTest
和 用于测试应用程序某部分的测试注解. -
命令行参数.
-
来自
SPRING_APPLICATION_JSON
的属性 (嵌入在环境变量或者系统属性 [system propert] 中的内联 JSON) . -
ServletConfig
初始化参数. -
ServletContext
初始化参数. -
来自
java:comp/env
的 JNDI 属性. -
Java 系统属性 (
System.getProperties()
) . -
操作系统环境变量.
-
只有
random.*
属性的RandomValuePropertySource
. -
在已打包的 jar 外部的 指定 profile 的应用属性文件 (
application-{profile}.properties
和 YAML 变量). -
在已打包的 jar 内部的指定 profile 的应用属性文件 (
application-{profile}.properties
和 YAML 变量). -
Application properties outside of your packaged jar (
application.properties
and YAML variants). -
Application properties packaged inside your jar (
application.properties
and YAML variants). -
在已打包的 jar 外部的应用属性文件 (
application.properties
和 YAML 变量). -
在已打包的 jar 内部的应用属性文件 (
application.properties
和 YAML 变量). -
在
@Configuration
类上的@PropertySource
注解. 请注意,在刷新应用程序上下文之前,不会将此类属性源添加到环境中. 现在配置某些属性 (如logging.
和spring.main.
) 为时已晚,这些属性在刷新开始之前就已读取. -
默认属性 (使用
SpringApplication.setDefaultProperties
指定) .
举个例子,假设开发的 @Component
使用了 name
属性,可以这样:
import org.springframework.stereotype.*;
import org.springframework.beans.factory.annotation.*;
@Component
public class MyBean {
@Value("${name}")
private String name;
// ...
}
在您的应用程序的 classpath 中 (比如在 jar 中) ,您可以有一个 application.properties
,它为 name 提供了一个合适的默认属性值. 当在新环境中运行时,您可以在 jar 外面提供一个 application.properties
来覆盖 name
. 对于一次性测试,您可以使用命令行指定形式启动 (比如 java -jar app.jar --name="Spring"
) .
$ SPRING_APPLICATION_JSON='{"acme":{"name":"test"}}' java -jar myapp.jar 在此示例中,您可以在 Spring $ java -Dspring.application.json='{"name":"test"}' -jar myapp.jar 或者以命令行参数形式: $ java -jar myapp.jar --spring.application.json='{"name":"test"}' 或者将 JSON 作为一个 JNDI 变量: |
尽管来自 JSON 的 null 值将被添加到最终的属性源中,但是 PropertySourcesPropertyResolver 会将 null 属性视为缺失值。
这意味着 JSON 无法使用 null 值覆盖优先级低的属性源中的属性.
|
2.1. 配置随机值
RandomValuePropertySource
对于随机值注入非常有用 (比如在保密场景或者测试用例中). 它可以产生 integer、long、uuid 和 string. 如下示例:
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}
random.int*
语法为 OPEN value (,max) CLOSE
,OPEN,CLOSE
可为任意字符,value,max
为整数. 如果使用了 max
,value
则为最小值,max
为最大值.
2.2. 访问命令行属性
默认情况下,SpringApplication
将所有命令行选项参数 (即以 --
开头的参数,比如 --server.port=9000
) 转换为属性,并将它们添加到 Spring Environment
中. 如之前所述,命令行属性始终优先于其他属性源.
如果您不希望将命令行属性添加到 Environment
,可以使用 SpringApplication.setAddCommandLineProperties(false)
来禁用它们.
2.3. 应用程序属性文件
SpringApplication
从以下位置的 application.properties
文件中加载属性 (properties) ,并将它们添加到 Spring Environment
中:
SpringApplication
loads properties from application.properties
files in the following locations and adds them to the Spring Environment
:
-
当前目录的
/config
子目录 -
当前目录
-
classpath 上的
/config
包 -
classpath 根路径
列表按序号优先级排序,序号越小,优先级越高.
您还可以 使用 YAML (.yml) 文件来替代 '.properties'. |
如果您不喜欢 application.properties
作为配置文件名,则可以通过指定 spring.config.name
环境属性来切换到另一个文件名. 您还可以使用 spring.config.location
环境属性来引用一个显式位置 (以逗号分隔的目录位置或文件路径列表) .
以下示例展示了如何指定其他文件名:
$ java -jar myproject.jar --spring.config.name=myproject
以下示例展示了如何指定两个位置:
$ java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties
spring.config.name 和 spring.config.location 在程序启动早期就用来确定哪些文件必须加载,因此必须将它们定义为环境属性 (通常是 OS 环境变量、系统属性或命令行参数) .
|
如果 spring.config.location
包含目录 (而不是文件) ,则它们应该以 /
结尾 (并且在运行期间,在加载之前追加从 spring.config.name
生成的名称,包括指定 profile 的文件名) .
spring.config.location
中指定的文件按原样使用,不支持指定 profile 形式,并且可被任何指定 profile 的文件的属性所覆盖.
无论是直接指定还是包含在目录中,配置文件都必须在名称中包含文件扩展名。
开箱即用的典型扩展名是 .properties
,.yaml
和 .yml
。
配置位置以相反的顺序搜索. 默认情况下,配置的位置为 classpath:/,classpath:/config/,file:./,file:./config/
. 生成的搜索顺序如下:
-
file:./config/
-
file:./
-
classpath:/config/
-
classpath:/
使用了 spring.config.location
配置自定义配置位置时,默认位置配置将被替代. 例如,如果 spring.config.location
配置为 classpath:/custom-config/,file:./custom-config/
,搜索顺序将变为以下:
-
file:./custom-config/
-
classpath:custom-config/
或者,当使用 spring.config.additional-location
配置自定义配置位置时,除了使用默认位置外,还会使用它们. 这些其他 (additional) 位置将在默认位置之前搜索. 例如,如果将其他位置配置为 classpath:/custom-config/,file:./custom-config/
,则搜索顺序将变为以下内容:
-
file:./custom-config/
-
classpath:custom-config/
-
file:./config/
-
file:./
-
classpath:/config/
-
classpath:/
该搜索顺序允许您在一个配置文件中指定默认值,然后有选择地覆盖另一个配置文件中的值. 您可以在 application.properties
(或您使用 spring.config.name
指定的其他文件) 中的某个默认位置为应用程序提供默认值. 之后,在运行时,这些默认值将被自定义位置中的某个文件所覆盖.
如果您使用的是环境变量而不是系统属性,大部分操作系统都不允许使用 . 分隔的键名,但您可以使用下划线来代替 (例如,使用 SPRING_CONFIG_NAME 而不是 spring.config.name ) .查看 从环境变量绑定 获取更多细节信息.
|
如果应用程序在容器中运行,则可以使用 JNDI 属性 (java:comp/env ) 或 servlet 上下文初始化参数来代替环境变量或系统属性. (in java:comp/env )
|
2.4. 特定 Profile 的属性文件
除 application.properties
文件外,还可以使用以下命名约定定义特定 profile 的属性文件: application-{profile}.properties. Environment 有一组默认配置文件 (默认情况下为 default
) ,如果未设置激活的 (active) profile,则使用这些配置文件. 换句话说,如果没有显式激活 profile,则会加载 application-default.properties
中的属性.
特定 profile 的属性文件从与标准 application.properties
相同的位置加载,特定 profile 的属性文件无论是否在打包的 jar 内部,都始终覆盖非特定文件.
如果指定了多个配置文件,则应用 last-wins 策略 (优先采取最后一个) . 例如,spring.profiles.active
属性指定的配置文件是在使用 SpringApplication
API 配置的配置文件之后添加的,因此优先应用.
如果在 spring.config.location 中指定了文件,则不考虑这些文件的特定 profile 形式. 如果您还想使用特定 profile 的属性文件,请在 spring.config.location 中使用目录形式.
|
2.5. 属性中的占位符
application.properties
中的值在使用时通过现有的 Environment
进行过滤,因此您可以返回之前定义的值 (例如,从系统属性) .
app.name=MyApp
app.description=${app.name} is a Spring Boot application
您还可以使用此技术创建现有 Spring Boot 属性的简短形式. 有关详细信息,请参见 使用简短命令行参数 . |
2.6. 加密属性
Spring Boot 没有为加密属性值提供任何内置支持,然而,它提供了修改 Spring Environment
包含的值所必需的钩子. EnvironmentPostProcessor
接口允许您在应用程序启动之前操作 Environment.
有关详细信息,请参见 在启动前自定义 Environment 或 ApplicationContext .
如果您正在寻找一种可用于存储凭据和密码的安全方法, Spring Cloud Vault 项目支持在 HashiCorp Vault 中存储外部化配置.
2.7. 使用 YAML 代替属性文件
YAML 是 JSON 的超集,是一个可用于指定层级配置数据的便捷格式. 只要在 classpath 上有 SnakeYAML 库,SpringApplication
类就会自动支持 YAML 作为属性文件 (properties) 的替代.
如果使用 starter ,则 spring-boot-starter 会自动提供 SnakeYAML.
|
2.7.1. 加载 YAML
Spring Framework 提供了两个便捷类,可用于加载 YAML 文档. YamlPropertiesFactoryBean
将 YAML 加载为 Properties
,YamlMapFactoryBean
将 YAML 加载为 Map
.
例如以下 YAML 文档:
environments:
dev:
url: https://dev.example.com
name: Developer Setup
prod:
url: https://another.example.com
name: My Cool App
前面的示例将转换为以下属性 (properties) :
environments.dev.url=https://dev.example.com
environments.dev.name=Developer Setup
environments.prod.url=https://another.example.com
environments.prod.name=My Cool App
YAML 列表表示带有 [index]
下标引用的属性键. 例如以下 YAML:
my:
servers:
- dev.example.com
- another.example.com
以上示例将转成以下属性:
my.servers[0]=dev.example.com
my.servers[1]=another.example.com
要使用 Spring Boot 的 Binder 工具来绑定这样配置到属性 (这是 @ConfigurationProperties
所做的) ,你需要在目标 bean 中有一个 java.util.List
(或 Set
) 类型的属性,你需要为其提供一个 setter 或者使用可变值初始化它. 例如,以下示例展示将上述的配置与属性绑定:
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
2.7.2. 在 Spring Environment 中将 YAML 暴露为属性
YamlPropertySourceLoader
类可用于在 Spring Environment
中将 YAML 暴露为 PropertySource
. 这样做可以让您使用带占位符语法的 @Value
注解来访问 YAML 属性.
2.7.3. 多 profile YAML 文档
您可以使用 spring.profiles
key 在单个文件中指定多个特定 profile 的 YAML 文档,以指示文档何时应用,如下所示:
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production & eu-central
server:
address: 192.168.1.120
在前面示例中,如果 development
profile 处于激活状态,则 server.address
属性得值为 127.0.0.1
. 同样,如果 production
和 eu-central
profile 处于激活状态,则 server.address
属性的值为 192.168.1.120
.
如果未激活 development
、production
或 eu-central
profile,则该属性的值为 192.168.1.100
.
因此, |
如果在应用程序上下文启动时没有显式激活,则激活默认 profile. 因此,在以下 YAML 中,我们为 spring.security.user.password
设置了一个值,该值仅在 default profile 中可用:
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak
然而,在以下示例中,始终设置密码,因为它未附加到任何 profile,如果需要更改,必须在所有其他 profile 中显式重置:
server:
port: 8000
spring:
security:
user:
password: weak
使用 spring.profiles
元素来指定 Spring profile 可以选择通过使用 !
字符来取反 (否定) . 如果为单个文档指定了否定和非否定的 profile,则至少一个非否定的 profile 必须匹配,没有否定的 profile 可以匹配.
2.7.4. YAML 的缺点
无法使用 @PropertySource
注解加载 YAML 文件. 因此,如果您需要以这种方式加载值,请使用属性文件 (properties) .
在特定于配置文件的YAML文件中使用多YAML文档语法可能会导致意外行为. 例如,考虑文件中的以下配置:
server:
port: 8000
---
spring:
profiles: "!test"
security:
user:
password: "secret"
如果使用参数 --spring.profiles.active=dev
运行该应用程序,则可能希望将 security.user.password
设置为 “secret”,但事实并非如此.
嵌套文档将被过滤,因为主文件名为 application-dev.yml
. 它已经被认为是特定于配置文件的,并且嵌套文档将被忽略.
我们建议您不要混用特定于配置文件的YAML文件和多个YAML文档. 坚持只使用其中之一. |
2.8. 类型安全的配置属性
使用 @Value("${property}")
注解来注入配置属性有时会很麻烦,特别是如果您使用了多个属性或者您的数据本质上是分层结构. Spring Boot 提供了另一种使用属性的方法,该方法使用强类型的 bean 来管理和验证应用程序的配置,如下所示:
2.8.1. JavaBean 属性绑定
可以绑定一个声明标准JavaBean属性的bean,如以下示例所示:
package com.example;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("acme")
public class AcmeProperties {
private boolean enabled;
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>(Collections.singleton("USER"));
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
public List<String> getRoles() { ... }
public void setRoles(List<String> roles) { ... }
}
}
前面的 POJO 定义了以下属性:
-
acme.enabled
, 默认值为false
. -
acme.remote-address
, 可以从String
强制转换的类型. -
acme.security.username
, 内嵌一个security
对象,其名称由属性名称决定. 特别是,返回类型根本没有使用,可能是SecurityProperties
. -
acme.security.password
. -
acme.security.roles
,String
集合. 默认为USER
.
Spring Boot自动配置大量使用 @ConfigurationProperties 来轻松配置自动配置的bean. 与自动配置类相似,Spring Boot中可用的 @ConfigurationProperties 类仅供内部使用. 通过属性文件,YAML文件,环境变量等配置的映射到该类的属性是公共API,
但是该类本身的内容并不意味着可以直接使用.
|
getter 和 setter 通常是必需的,因为绑定是通过标准的 Java Bean 属性描述符来完成,就像在 Spring MVC 中一样. 以下情况可以省略 setter:
有些人可能会使用 Project Lombok 来自动生成 getter 和 setter. 请确保 Lombok 不为此类型生成任何特定构造函数,因为容器会自动使用它来实例化对象. 最后,考虑到标准 Java Bean 属性,不支持对静态属性的绑定. |
2.8.2. 构造函数绑定
上一节中的示例可以以不变的方式重写,如下例所示:
package com.example;
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public AcmeProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
public boolean isEnabled() { ... }
public InetAddress getRemoteAddress() { ... }
public Security getSecurity() { ... }
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password,
@DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() { ... }
public String getPassword() { ... }
public List<String> getRoles() { ... }
}
}
在此设置中,@ConstructorBinding
注解用于指示应使用构造函数绑定. 这意味着绑定器将期望找到带有您希望绑定的参数的构造函数.
@ConstructorBinding
类的嵌套成员 (例如上例中的 Security
) 也将通过其构造函数进行绑定.
可以使用 @DefaultValue
指定默认值,并且将应用相同的转换服务将 String
值强制为缺少属性的目标类型.
默认情况下,如果没有属性绑定到 Security
,则 AcmeProperties
实例的 Security
为 null
。
如果您希望即使没有绑定任何属性都返回 Security
的非空实例,则可以使用空的 @DefaultValue
注解来这样做:
package com.example;
import java.net.InetAddress;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConstructorBinding
@ConfigurationProperties("acme")
public class AcmeProperties {
private final boolean enabled;
private final InetAddress remoteAddress;
private final Security security;
public AcmeProperties(boolean enabled, InetAddress remoteAddress, @DefaultValue Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}
}
要使用构造函数绑定,必须使用 @EnableConfigurationProperties 或配置属性扫描来启用该类. 您不能对通过常规Spring机制创建的bean使用构造函数绑定 (例如 @Component bean,通过 @Bean 方法创建的bean或使用 @Import 加载的bean)
|
如果您的类具有多个构造函数,则还可以直接在应绑定的构造函数上使用 @ConstructorBinding .
|
不建议将 java.util.Optional 与 @ConfigurationProperties 一起使用,因为它主要用于返回类型。
因此,它不太适合配置属性注入。为了与其他类型的属性保持一致,如果您确实声明了 Optional 属性并且没有任何值,则将绑定 null 而不是空的 Optional .
|
2.8.3. 启用 @ConfigurationProperties
注解的类型
Spring Boot提供了绑定 @ConfigurationProperties
类型并将其注册为Bean的基础架构. 您可以逐类启用配置属性,也可以启用与组件扫描类似的方式进行配置属性扫描.
有时,用 @ConfigurationProperties
注解的类可能不适用于扫描,例如,如果您正在开发自己的自动配置,或者想要有条件地启用它们. 在这些情况下,请使用 @EnableConfigurationProperties
注解 指定要处理的类型列表. 可以在任何 @Configuration
类上完成此操作,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
要使用配置属性扫描,请将 @ConfigurationPropertiesScan
注解 添加到您的应用程序. 通常,它被添加到使用 @SpringBootApplication
注解的主应用程序类中,但可以将其添加到任何 @Configuration
类中.
默认情况下,将从声明注解的类的包中进行扫描. 如果要定义要扫描的特定程序包,可以按照以下示例所示进行操作:
@SpringBootApplication
@ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
当以这种方式注册 上面示例中的 bean 名称为 |
即使前面的配置为 AcmeProperties
创建了一个 bean,我们也建议 @ConfigurationProperties
只处理环境 (environment) ,特别是不要从上下文中注入其他 bean.
对于极端情况,可以使用setter注入或框架提供的任何 *Aware
接口 (例如,需要访问 Environment
的 EnvironmentAware
) . 如果仍然想使用构造函数注入其他bean,则必须使用 @Component
注解配置属性bean,并使用基于JavaBean的属性绑定.
2.8.4. 使用 @ConfigurationProperties
注解类型
这种配置样式与 SpringApplication
外部YAML配置特别有效,如以下示例所示:
# application.yml
acme:
remote-address: 192.168.1.1
security:
username: admin
roles:
- USER
- ADMIN
# additional configuration as required
要使用 @ConfigurationProperties
bean,您可以使用与其他 bean 相同的方式注入它们,如下所示:
@Service
public class MyService {
private final AcmeProperties properties;
@Autowired
public MyService(AcmeProperties properties) {
this.properties = properties;
}
//...
@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}
}
使用 @ConfigurationProperties 还可以生成元数据文件,IDE 可以通过这些文件来为您自己的 key 提供自动完成功能. 有关详细信息,请参阅附录 B: 配置元数据 .
|
2.8.5. 第三方配置
@ConfigurationProperties
除了可以使用来注解类之外,您还可以在公共的 @Bean
方法上使用. 当您想要将属性绑定到您掌控之外的第三方组件时,这样做特别有用.
要使用 Environment
属性配置 bean,请将 @ConfigurationProperties
添加到 bean 注册上,如下所示:
@ConfigurationProperties(prefix = "another")
@Bean
public AnotherComponent anotherComponent() {
...
}
使用 another
前缀定义的所有属性都使用与前面的 AcmeProperties
示例类似的方式映射到 AnotherComponent
bean.
2.8.6. 宽松绑定
Spring Boot 使用一些宽松的规则将 Environment
属性绑定到 @ConfigurationProperties
bean,因此 Environment
属性名不需要和 bean 属性名精确匹配.
常见的示例包括使用了 -
符号分割的环境属性 (例如,context-path
绑定到 contextPath
) 和大写环境属性 (例如,PORT
绑定到 port
) .
如下 @ConfigurationProperties
类:
@ConfigurationProperties(prefix="acme.my-project.person")
public class OwnerProperties {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
在上述示例中,同样可以使用以下属性名称:
属性 | 描述 |
---|---|
|
Kebab 风格 (短横线命名) ,建议在 |
|
标准驼峰式风格. |
|
下划线表示法, |
|
大写风格,当使用系统环境变量时推荐使用该风格. |
注解的 prefix 值必须是 kebab (短横线命名)风格 (小写并用 - 分隔,例如 acme.my-project.person ) .
|
属性源 | 简单类型 | 列表集合类型 |
---|---|---|
Properties 文件 |
驼峰式、短横线式或下划线式 |
标准列表语法使用 |
YAML 文件 |
驼峰式、短横线式或者下划线式 |
标准 YAML 列表语法或者逗号分隔值 |
环境变量 |
大写并且以下划线作为定界符,(查看 从环境变量绑定). |
数字值两边使用下划线连接,例如 |
系统属性 |
驼峰式、短横线式或者下划线式 |
标准列表语法使用 |
我们建议,属性尽可能以小写的短横线格式存储,比如 my.property-name=acme .
|
绑定 Maps
当绑定到 Map
属性时,如果 key
包含除小写字母数字字符或 -
以外的任何内容,则需要使用括号表示法来保留原始值. 如果 key 没有使用 []
包裹,则里面的任何非字母数字字符或 -
的字符都将被删除. 例如,将以下属性绑定到一个 Map
:
acme:
map:
"[/key1]": value1
"[/key2]": value2
/key3: value3
上面的属性将绑定到一个 Map
上,其中 /key1
,/key2
和 key3
作为 map 的 key.
对于YAML文件,方括号需要用引号引起来,以便正确解析 keys. |
从环境变量绑定
大多数操作系统在对于环境变量有严格规范。 例如,Linux shell 变量只能包含字母(a
to z
or A
to Z
),数字(0
to 9
)或下划线字符(_
)。 按照约定,Unix shell 变量也可以用大写字母命名。
Spring Boot 的宽松绑定规则尽可能设计成与这些命名限制兼容。
要将规范形式的属性名称转换为环境变量名称,可以遵循以下规则:
-
使用下划线 (
_
) 替代 (.
). -
删除所有 (
-
). -
转换为大写.
例如,配置属性 spring.main.log-startup-info
是一个名为 SPRING_MAIN_LOGSTARTUPINFO
的环境变量。
下划线不能用于替换属性名称中的中划线。如果您在上面的示例中尝试使用 SPRING_MAIN_LOG_STARTUP_INFO ,则不会绑定任何值。
|
当绑定到对象列表时,也可以使用环境变量。 要绑定到列表,元素编号应在变量名称中用下划线括起来。
例如,配置属性 my.acme[0].other
使用名为 MY_ACME_0_OTHER
的环境变量。
2.8.7. 合并复杂类型
当列表集合 (list) 在多个地方配置时,整个列表集合将被替换.
例如,假设带有 name
和 description
属性的 MyPojo
对象默认为 null
. 以下示例中,AcmeProperties
暴露了一个 MyPojo
对象列表集合:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final List<MyPojo> list = new ArrayList<>();
public List<MyPojo> getList() {
return this.list;
}
}
配置可以如下:
acme:
list:
- name: my name
description: my description
---
spring:
profiles: dev
acme:
list:
- name: my another name
如果 dev
配置文件未激活,则 AcmeProperties.list
只包含一条 MyPojo
条目,如之前所述. 但是,如果激活了 dev
配置文件,列表集合仍然只包含一个条目 (name 属性值为 my another name
,description 为 null
) .
此配置不会向列表集合中添加第二个 MyPojo
实例,也不会合并条目.
在多个配置文件中指定一个 List
时,最高优先级 (并且只有一个) 的列表集合将被使用. 可做如下配置:
acme:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
acme:
list:
- name: my another name
在前面示例中,如果 dev
配置文件处于 active 状态,则 AcmeProperties.list
包含一个 MyPojo
条目 (name 为 my another name
,description 为 null
) . 对于 YAML 而言,逗号分隔的列表和YAML 列表同样会完全覆盖列表集合的内容.
对于 Map 属性,您可以绑定来自多个源中提取的属性值. 但是,对于多个源中的相同属性,则使用高优先级最高的属性. 以下示例从 AcmeProperties
暴露了一个 Map<String, MyPojo>
:
@ConfigurationProperties("acme")
public class AcmeProperties {
private final Map<String, MyPojo> map = new HashMap<>();
public Map<String, MyPojo> getMap() {
return this.map;
}
}
可以考虑以下配置:
acme:
map:
key1:
name: my name 1
description: my description 1
---
spring:
profiles: dev
acme:
map:
key1:
name: dev name 1
key2:
name: dev name 2
description: dev description 2
如果 dev
配置文件未激活,则 AcmeProperties.map
只包含一个带 key1
key 的条目 (name 为 my name 1
,description 为 my description 1
) .
如果激活了 dev
配置文件,则 map 将包含两个条目, key 分别为 key1
(name 为 dev name 1
和 description 为 my description 1
) 和 key2
(name 为 dev name 2
和 description 为 dev description 2
) .
前面的合并规则适用于所有不同属性源的属性,而不仅仅是 YAML 文件. |
2.8.8. 属性转换
当外部应用程序属性 (application properties) 绑定到 @ConfigurationProperties
bean 时,Spring Boot 会尝试将其属性强制转换为正确的类型. 如果需要自定义类型转换,可以提供 ConversionService
bean (名为 conversionService
的 bean) 或自定义属性编辑器 (通过 CustomEditorConfigurer
bean) 或自定义转换器 (带有注解为 @ConfigurationPropertiesBinding
的 bean 定义) .
由于该 bean 在应用程序生命周期早期就被请求 ,因此请限制 ConversionService 使用的依赖. 您在创建时可能无法完全初始化所需的依赖. 如果配置 key 为非强制需要,您可能希望重命名自定义的 ConversionService ,并仅依赖于使用 @ConfigurationPropertiesBinding 限定的自定义转换器.
|
转换 duration
Spring Boot 支持持续时间 (duration) 表达. 如果您暴露一个 java.time.Duration
属性,则可以在应用程序属性中使用以下格式:
-
常规
long
表示 (除非指定@DurationUnit
,否则使用毫秒作为默认单位) -
used by
java.time.Duration
使用的标准 ISO-8601 格式 -
一种更易读的格式,值和单位在一起 (例如
10s
表示 10 秒)
思考以下示例:
@ConfigurationProperties("app.system")
public class AppSystemProperties {
@DurationUnit(ChronoUnit.SECONDS)
private Duration sessionTimeout = Duration.ofSeconds(30);
private Duration readTimeout = Duration.ofMillis(1000);
public Duration getSessionTimeout() {
return this.sessionTimeout;
}
public void setSessionTimeout(Duration sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Duration getReadTimeout() {
return this.readTimeout;
}
public void setReadTimeout(Duration readTimeout) {
this.readTimeout = readTimeout;
}
}
指定一个会话超时时间为 30
秒,使用 30
、PT30S
和 30s
等形式都是可以的. 读取超时时间设置为 500ms
,可以采用以下任何一种形式: 500
、PT0.5S
和 500ms
.
您也可以使用任何支持的单位来标识:
-
ns
纳秒 -
us
微秒 -
ms
毫秒 -
s
秒 -
m
分 -
h
小时 -
d
天
默认单位是毫秒,可以使用 @DurationUnit
配合上面的单位示例重写.
请注意,只有使用 getter 和 setter 的 JavaBean 的属性绑定才支持 @DurationUnit
。
构造函数绑定不支持此功能。
要从先前仅使用 Long 来表示持续时间的版本进行升级,如果切换到 Duration 时不是毫秒,请定义单位 (使用 @DurationUnit ) . 这样做可以提供透明的升级路径,同时支持更丰富的格式.
|
转换 Data Size
Spring Framework 有一个 DataSize
值类型,允许以字节表示大小. 如果暴露一个 DataSize
属性,则可以在应用程序属性中使用以下格式:
-
常规的
long
表示 (使用字节作为默认单位,除非指定了@DataSizeUnit
) -
更具有可读性的格式,值和单位在一起 (例如
10MB
表示 10 兆字节)
请思考以下示例:
@ConfigurationProperties("app.io")
public class AppIoProperties {
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize bufferSize = DataSize.ofMegabytes(2);
private DataSize sizeThreshold = DataSize.ofBytes(512);
public DataSize getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(DataSize bufferSize) {
this.bufferSize = bufferSize;
}
public DataSize getSizeThreshold() {
return this.sizeThreshold;
}
public void setSizeThreshold(DataSize sizeThreshold) {
this.sizeThreshold = sizeThreshold;
}
}
要指定 10
兆字节的缓冲大小,使用 10
和 10MB
是等效的. 256
字节的大小可以指定为 256
或 256B
.
您也可以使用任何支持的单位:
-
B
字节 -
KB
千字节 -
MB
兆字节 -
GB
千兆字节 -
TB
兆兆字节
默认单位是字节,可以使用 @DataSizeUnit
配合上面的示例单位重写.
要从先前仅使用 Long 来表示大小的版本进行升级,请确保在切换到 DataSize 不是字节的情况下定义单位 (使用 @DataSizeUnit ) . 这样做可以提供透明的升级路径,同时支持更丰富的格式.
|
2.8.9. @ConfigurationProperties 验证
只要使用了 Spring 的 @Validated
注解,Spring Boot 就会尝试验证 @ConfigurationProperties
类. 您可以直接在配置类上使用 JSR-303 javax.validation
约束注解. 为此,请确保 JSR-303 实现在 classpath 上,然后将约束注解添加到字段上,如下所示:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
// ... getters and setters
}
您还可以通过使用 @Validated 注解创建配置属性的 @Bean 方法来触发验证.
|
虽然绑定时也会验证嵌套属性,但最好的做法还是将关联字段注解上 @Valid
. 这可确保即使未找到嵌套属性也会触发验证. 以下示例基于前面的 AcmeProperties
示例:
@ConfigurationProperties(prefix="acme")
@Validated
public class AcmeProperties {
@NotNull
private InetAddress remoteAddress;
@Valid
private final Security security = new Security();
// ... getters and setters
public static class Security {
@NotEmpty
public String username;
// ... getters and setters
}
}
您还可以通过创建一个名为 configurationPropertiesValidator
的 bean 定义来添加自定义 Spring Validator
. 应该将 @Bean 方法声明为 static
.
配置属性验证器在应用程序生命周期的早期创建,将 @Bean
方法声明为 static
可以无需实例化 @Configuration
类来创建 bean. 这样做可以避免早期实例化可能导致的意外问题. 这里有一个属性验证示例,讲解了如何设置.
spring-boot-actuator 模块包括一个暴露所有 @ConfigurationProperties bean 的端点. 可将 Web 浏览器指向 /actuator/configprops 或使用等效的 JMX 端点. 有关详细信息,请参阅 "生产就绪功能" 部分.
|
2.8.10. @ConfigurationProperties
与 @Value
对比
@Value
注解是核心容器功能,它不提供与类型安全配置属性相同的功能. 下表总结了 @ConfigurationProperties
和 @Value
支持的功能:
功能 | @ConfigurationProperties |
@Value |
---|---|---|
Yes |
Limited (see note below) |
|
Yes |
No |
|
|
No |
Yes |
如果您要为自己的组件定义一组配置 key,我们建议您将它们分组到使用 @ConfigurationProperties
注解的 POJO 中.
这样做将为您提供结构化,类型安全的对象,您可以将其注入到自己的 bean 中。
最后,虽然您可以在 @Value
中编写 SpEL 表达式,但来自应用程序属性文件的此类表达式并不会被处理.
3. Profiles
Spring Profile 提供了一种应用程序配置部分隔离并使其仅在特定环境中可用的方法. 可以使用 @Profile
来注解任何 @Component
或 @Configuration
以指定何时加载它,如下所示:
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
如果 @ConfigurationProperties Bean是通过 @EnableConfigurationProperties 而非自动扫描注册的,则需要在 @EnableConfigurationProperties 注解的 @Configuration 类上指定 @Profile 注解.
在扫描 @ConfigurationProperties 的情况下,可以在 @ConfigurationProperties 类本身上指定 @Profile .
|
您可以使用 spring.profiles.active
Environment
属性指定哪些配置文件处于激活状态. 您可以使用本章前面介绍的任何方法指定属性. 例如,您可以将其包含在 application.properties
中,如下所示:
spring.profiles.active=dev,hsqldb
您还可以在命令行上使用以下开关指定它: --spring.profiles.active=dev,hsqldb
.
3.1. 添加激活 Profile
spring.profiles.active
属性遵循与其他属性相同的排序规则: 应用优先级最高的 PropertySource
. 这意味着您可以在 application.properties
中指定激活配置文件,然后使用命令行开关替换它们.
有时,将特定 profile 的属性添加到激活配置文件而不是替换它们,这种方式也是很有用的. spring.profiles.include
属性可无条件地添加激活配置文件. SpringApplication
入口还有一个 Java API,用于设置其他 profile (即,在 spring.profiles.active
属性激活的 profile 之上) . 请参阅 SpringApplication 中的 setAdditionalProfiles()
方法.
例如,当使用开关 --spring.profiles.active=prod
运行有以下属性的应用程序时,proddb
和 prodmq
配置文件也会被激活:
---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include:
- proddb
- prodmq
请记住,可以在 YAML 文档中定义 spring.profiles 属性,以确定此特定文档何时包含在配置中. 有关更多详细信息,请参见根据环境更改配置.
|
3.2. 以编程方式设置 Profile
您可以在应用程序运行之前通过调用 SpringApplication.setAdditionalProfiles(…)
以编程方式设置 active 配置文件. 也可以使用Spring的 ConfigurableEnvironment
接口激活 profile.
3.3. 特定 Profile 的配置文件
特定 profile 的 application.properties
(或 application.yml
) 和通过 @ConfigurationProperties
引用的文件被当做文件并加载. 有关详细信息,请参见 "特定 Profile 的属性文件" .
4. 日志记录
Spring Boot 使用 Commons Logging 记录所有内部日志,但保留底层日志实现. 其为 Java Util Logging, Log4J2 和 Logback 提供了默认配置. 每种日志记录器都预先配置为使用控制台输出,并且还提供可选的文件输出.
默认情况下,如果您使用了 Starter
,则使用 Logback 进行日志记录。还包括合适的 Logback 路由,以确保在使用 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的依赖库都能正常工作.
Java 有很多日志框架可供使用. 如果以上列表让您感到困惑,请不要担心. 通常,您不需要更改日志依赖,并且 Spring Boot 提供的默认配置可以保证日志正常工作. |
将应用程序部署到 Servlet 容器或应用程序服务器时,通过 Java Util Logging API 执行的日志记录不会路由到应用程序的日志中. 这样可以防止容器或其他已部署到容器中的应用程序执行的日志记录出现在应用程序的日志中. |
4.1. 日志格式
Spring Boot 默认日志输出类似于以下示例:
2019-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.52 2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2019-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms 2019-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] 2019-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
输出以下项:
-
日期和时间: 毫秒精度,易于排序.
-
日志级别:
ERROR
、WARN
、INFO
、DEBUG
或TRACE
. -
进程 ID.
-
一个
---
分隔符,用于区分实际日志内容的开始. -
线程名称: 在方括号中 (可能会截断控制台输出) .
-
日志记录器名称: 这通常是源类名称 (通常为缩写) .
-
日志内容.
Logback 没有 FATAL 级别. 该级别映射到 ERROR .
|
4.2. 控制台输出
默认日志配置会在写入时将消息回显到控制台. 默认情况下,会记录 ERROR
、WARN
和 INFO
级别的日志. 您还可以通过使用 --debug
标志启动应用程序来启用调试模式.
$ java -jar myapp.jar --debug
您还可以在 application.properties 中指定 debug=true .
|
启用调试模式后,核心日志记录器 (内嵌容器、Hibernate 和 Spring Boot) 将被配置为输出更多日志信息. 启用调试模式不会将应用程序配置为使用 DEBUG
级别记录所有日志内容.
或者,您可以通过使用 --trace
标志 (或在 application.properties
中的设置 trace=true
) 启动应用程序来启用跟踪模式. 这样做可以为选择的核心日志记录器 (内嵌容器、Hibernate 模式生成和整个 Spring 组合) 启用日志追踪.
4.2.1. 着色输出
如果您的终端支持 ANSI,则可以使用颜色输出来提高可读性. 您可以将 spring.output.ansi.enabled
设置为 受支持的值 以覆盖自动检测.
可使用 %clr
转换字配置颜色编码. 最简单形式是,转换器根据日志级别对输出进行着色,如下所示:
%clr(%5p)
下表描述日志级别与颜色的映射关系:
Level | Color |
---|---|
|
Red |
|
Red |
|
Yellow |
|
Green |
|
Green |
|
Green |
或者,您可以通过将其作为转换选项指定应使用的颜色或样式. 例如,要将文本变为黄色,请使用以下设置:
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}
支持以下颜色和样式:
-
blue
-
cyan
-
faint
-
green
-
magenta
-
red
-
yellow
4.3. 文件输出
默认情况下,Spring Boot 仅记录到控制台,不会写入日志文件. 想除了控制台输出之外还要写入日志文件,则需要设置 logging.file
或 logging.path
属性 (例如,在 application.properties
中) .
下表展示了如何与 logging.*
属性一起使用:
logging.file.name |
logging.file.path |
Example | Description |
---|---|---|---|
(none) |
(none) |
仅在控制台输出 |
|
指定文件 |
(none) |
|
写入指定的日志文件. 名称可以是绝对位置或相对于当前目录. |
(none) |
指定目录 |
|
将 |
日志文件在达到 10MB
时会轮转,并且与控制台输出一样,默认情况下会记录 ERROR
、WARN
和 INFO
级别的内容. 可以使用 logging.file.max-size
属性更改大小限制.
除非已设置 logging.file.max-history
属性,否则默认情况下将保留最近7天的轮转日志文件.
可以使用 logging.file.total-size-cap
限制日志归档文件的总大小. 当日志归档的总大小超过该阈值时,将删除备份. 要在应用程序启动时强制清除日志归档文件,请使用 logging.file.clean-history-on-start
属性.
日日志属性独立于实际的日志底层. 因此,spring Boot 不管理特定的配置 key (例如 Logback 的 logback.configurationFile ) .
|
4.4. 日志等级
所有受支持的日志记录系统都可以使用 logging.level.<logger-name>=<level>
来设置 Spring Environment
中的记录器等级 (例如,在 application.properties
中) . 其中 level 是 TRACE
、DEBUG
、INFO
、WARN
、ERROR
、FATAL
和 OFF
其中之一. 可以使用 logging.level.root
配置 root
记录器.
以下示例展示了 application.properties
中默认的日志记录设置:
logging.level.root=warn
logging.level.org.springframework.web=debug
logging.level.org.hibernate=error
也可以使用环境变量设置日志记录级别. 例如, LOGGING_LEVEL_ORG_SPRINGFRAMEWORK_WEB=DEBUG
会将 org.springframework.web
设置为 DEBUG
.
以上方法仅适用于程序包级别的日志记录. 由于宽松的绑定总是将环境变量转换为小写,因此无法以这种方式为单个类配置日志记录. 如果需要为类配置日志记录,则可以使用 the SPRING_APPLICATION_JSON 变量.
|
4.5. 日志组
将相关记录器组合在一起以便可以同时配置,这通常很有用. 例如,您可以更改所有 Tomcat 相关记录器的日志记录级别,但您无法轻松记住顶层的包名.
为了解决这个问题,Spring Boot 允许您在 Spring Environment
中定义日志记录组. 例如,以下通过将 tomcat 组添加到 application.properties
来定义 tomcat 组:
logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat
定义后,您可以使用一行配置来更改组中所有记录器的级别:
logging.level.tomcat=TRACE
Spring Boot 包含以下预定义的日志记录组,可以直接使用:
名称 | 日志记录器 |
---|---|
web |
|
sql |
|
4.6. 自定义日志配置
可以通过在 classpath 中引入适合的库来激活各种日志记录系统,并且可以通过在 classpath 的根目录中或在以下 Spring Environment 属性指定的位置提供合适的配置文件来进一步自定义: logging.config.
您可以使用 org.springframework.boot.logging.LoggingSystem
系统属性强制 Spring Boot 使用特定的日志记录系统. 该值应该是一个实现了 LoggingSystem
的类的完全限定类名. 您还可以使用 none
值完全禁用 Spring Boot 的日志记录配置.
由于日志记录在创建 ApplicationContext 之前初始化,因此无法在 Spring @Configuration 文件中控制来自 @PropertySources 的日志记录. 更改日志记录系统或完全禁用它的唯一方法是通过系统属性设置.
|
根据您的日志记录系统,将加载以下文件:
日志记录系统 | 文件 |
---|---|
Logback |
|
Log4j2 |
|
JDK (Java Util Logging) |
|
如果可能,我们建议您使用 -spring 的形式来配置日志记录 (比如 logback-spring.xml 而不是 logback.xml ) . 如果使用标准的配置位置,Spring 无法完全控制日志初始化.
|
Java Util Logging 存在已知的类加载问题,这些问题在以 'executable jar' 运行时会触发. 如果可能的话,我们建议您在使用可执行 jar 方式运行时避免使用它. . |
为了进行自定义,部分其他属性会从 Spring Environment
传输到 System
属性,如下表所述:
Spring Environment | 系统属性 | 说明 |
---|---|---|
|
|
记录异常时使用的转换字. |
|
|
是否在启动时清除存档日志文件 (如果启用了LOG_FILE) . (仅默认的Logback设置受支持. ) |
|
|
如果已定义,则在默认日志配置中使用它. |
|
|
最大日志文件大小 (如果启用了 LOG_FILE) . (仅支持默认的 Logback 设置. ) |
|
|
要保留的归档日志文件最大数量 (如果启用了 LOG_FILE) . (仅支持默认的 Logback 设置. ) |
|
|
如果已定义,则在默认日志配置中使用它. |
|
|
要保留的日志备份的总大小 (如果启用了LOG_FILE) . (仅默认的Logback设置受支持. ) |
|
|
要在控制台上使用的日志模式 (stdout) . (仅支持默认的 Logback 设置. ) |
|
|
日志日期格式的 Appender 模式. (仅支持默认的 Logback 设置. ) |
|
|
要在文件中使用的日志模式 (如果启用了 |
|
|
渲染日志级别时使用的格式 (默认值为 |
|
|
过渡日志文件名的模式(默认 |
|
|
当前进程 ID (如果可能,则在未定义为 OS 环境变量时发现) . |
所有受支持的日志记录系统在解析其配置文件时都可以参考系统属性. 有关示例,请参阅 spring-boot.jar
中的默认配置:
如果要在日志记录属性中使用占位符,则应使用 Spring Boot 的语法,而不是使用底层框架的语法. 值得注意的是,如果使用 Logback,则应使用 |
您可以通过仅覆盖 2019-08-30 12:30:04.031 user:someone INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request |
4.7. Logback 扩展
Spring Boot 包含许多 Logback 扩展,可用于进行高级配置. 您可以在 logback-spring.xml
配置文件中使用这些扩展.
由于标准的 logback.xml 配置文件加载过早,因此无法在其中使用扩展. 您需要使用 logback-spring.xml 或定义 logging.config 属性.
|
扩展不能与 Logback 的 配置扫描 一起使用. 如果尝试这样做,更改配置文件会导致发生类似以下错误日志: . |
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]] ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]
4.7.1. 特定 Profile 配置
<springProfile>
标签允许您根据激活的 Spring profile 选择性地包含或排除配置部分. 在 <configuration>
元素中的任何位置都支持配置 profile. 使用 name
属性指定哪个 proifle 接受配置. <springProfile>
标记可以包含简单的 proifle 名称 (例如 staging
) 或 profile 表达式. profile 表达式允许表达更复杂的 profile 逻辑,
例如 production & (eu-central | eu-west)
. 有关详细信息,请查阅 参考指南 . 以下清单展示了三个示例 profile:
<springProfile name="staging">
<!-- configuration to be enabled when the "staging" profile is active -->
</springProfile>
<springProfile name="dev | staging">
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
</springProfile>
<springProfile name="!production">
<!-- configuration to be enabled when the "production" profile is not active -->
</springProfile>
4.7.2. 环境属性
使用 <springProperty>
标签可以让您暴露 Spring 环境 (Environment
) 中的属性,以便在 Logback 中使用. 如果在 Logback 配置中访问来自 application.properties
文件的值,这样做很有用. 标签的工作方式与 Logback 的标准 <property>
标签类似. 但是,您可以指定属性 (来自 Environment
) 的 source
,而不是指定直接的 value
. 如果需要将属性存储在 local
作用域以外的其他位置,则可以使用 scope
属性.
如果需要回退值 (如果未在 Environment
中设置该属性) ,则可以使用 defaultValue
属性. 以下示例展示了如何暴露属性以便在 Logback 中使用:
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender>
必须以 kebab 风格 (短横线小写风格) 指定 source (例如 my.property-name ) . 但可以使用宽松规则将属性添加到 Environment 中.
|
5. 国际化
Spring Boot支持本地化消息,因此您的应用程序可以迎合不同语言首选项的用户. 默认情况下,Spring Boot在类路径的根目录下查找 messages
资源包的存在.
当已配置资源束的默认属性文件可用时 (即默认情况下为 messages.properties ) ,将应用自动配置. 如果您的资源包仅包含特定于语言的属性文件,则需要添加默认文件. 如果找不到与任何配置的基本名称匹配的属性文件,将没有自动配置的 MessageSource .
|
可以使用 spring.messages
命名空间配置资源包的基本名称以及其他几个属性,如以下示例所示:
spring.messages.basename=messages,config.i18n.messages
spring.messages.fallback-to-system-locale=false
spring.messages.basename 支持以逗号分隔的位置列表,可以是包限定符,也可以是从类路径根目录解析的资源.
|
有关更多支持的选项,请参见 MessageSourceProperties
6. JSON
Spring Boot 为三个 JSON 映射库提供了内置集成:
-
Gson
-
Jackson
-
JSON-B
Jackson 是首选和默认的库.
6.1. Jackson
Spring Boot 提供了 Jackson 的自动配置,Jackson 是 spring-boot-starter-json
的一部分. 当 Jackson 在 classpath 上时,会自动配置 ObjectMapper
bean. Spring Boot 提供了几个配置属性来 自定义 ObjectMapper
的配置.
7. 开发 Web 应用程序
Spring Boot 非常适合用于开发 web 应用程序. 您可以使用嵌入式 Tomcat、Jetty 或者 Undertow 来创建一个独立 (self-contained) 的 HTTP 服务器. 大多数 web 应用程序使用 spring-boot-starter-web
模块来快速搭建和运行,您也可以选择使用 spring-boot-starter-webflux
模块来构建响应式 (reactive) web 应用程序.
如果您尚未开发过 Spring Boot web 应用程序,则可以按照 入门 章节中的 "Hello World!" 示例进行操作.
7.1. Spring Web MVC 框架
Spring Web MVC 框架 (通常简称 "Spring MVC") 是一个富模型-视图-控制器的 web 框架. Spring MVC 允许您创建 @Controller
或者 @RestController
bean 来处理传入的 HTTP 请求. 控制器中的方法通过 @RequestMapping
注解映射到 HTTP.
以下是一个使用了 @RestController
来响应 JSON 数据的典型示例:
@RestController
@RequestMapping(value="/users")
public class MyRestController {
@RequestMapping(value="/{user}", method=RequestMethod.GET)
public User getUser(@PathVariable Long user) {
// ...
}
@RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
List<Customer> getUserCustomers(@PathVariable Long user) {
// ...
}
@RequestMapping(value="/{user}", method=RequestMethod.DELETE)
public User deleteUser(@PathVariable Long user) {
// ...
}
}
Spring MVC 是 Spring Framework 核心的一部分,详细介绍可参考其 参考文档. spring.io/guides 还提供了几个 Spring MVC 相关的指南.
7.1.1. Spring MVC 自动配置
Spring Boot 提供了适用于大多数 Spring MVC 应用的自动配置 (auto-configuration) .
自动配置在 Spring 默认功能上添加了以下功能:
-
引入
ContentNegotiatingViewResolver
和BeanNameViewResolver
bean. -
支持服务静态资源,包括对 WebJar 的支持 (见下文) .
-
自动注册
Converter
、GenericConverter
和Formatter
bean. -
支持
HttpMessageConverter
( 见下文) . -
自动注册
MessageCodesResolver
(见下文) . -
支持静态
index.html
. -
支持自定义
Favicon
(见下文) . -
自动使用
ConfigurableWebBindingInitializer
bean (见下文) .
如果您想保留 Spring Boot MVC 的功能,并且需要添加其他 MVC configuration (interceptor、formatter 和视图控制器等) ,可以添加自己的 WebMvcConfigurerAdapter
类型的 @Configuration
类,但不能带 @EnableWebMvc
注解.
如果您想自定义 RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
或者 ExceptionHandlerExceptionResolver
实例,可以声明一个 WebMvcRegistrationsAdapter
实例来提供这些组件.
如果您想完全掌控 Spring MVC,可以添加自定义注解了 @EnableWebMvc
的 @Configuration
配置类. 或者添加自己的 @Configuration
注解的 DelegatingWebMvcConfiguration
,如Java文档中的 @EnableWebMvc
所述. .
7.1.2. HttpMessageConverters
Spring MVC 使用 HttpMessageConverter
接口来转换 HTTP 的请求和响应. 开箱即用功能包含了合适的默认值,比如对象可以自动转换为 JSON (使用 Jackson 库) 或者 XML (优先使用 Jackson XML 扩展,其次为 JAXB) . 字符串默认使用 UTF-8
编码.
如果您需要添加或者自定义转换器 (converter) ,可以使用 Spring Boot 的 HttpMessageConverters
类:
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = ...
HttpMessageConverter<?> another = ...
return new HttpMessageConverters(additional, another);
}
}
上下文中的所有 HttpMessageConverter
bean 都将被添加到转换器列表中. 您也可以用这种方式来覆盖默认转换器.
7.1.3. 自定义 JSON Serializer 和 Deserializer
如果您使用 Jackson 序列化和反序列化 JSON 数据,可能需要自己编写 JsonSerializer
和 JsonDeserializer
类.
自定义序列化器 (serializer) 的做法通常是 通过一个模块来注册 Jackson, 然而 Spring Boot 提供了一个备选的 @JsonComponent
注解,它可以更加容易地直接注册 Spring Bean.
您可以直接在 JsonSerializer
或者 JsonDeserializer
实现上使用 @JsonComponent
注解. 您也可以在将序列化器/反序列化器 (deserializer) 作为内部类的类上使用. 例如:
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;
@JsonComponent
public class Example {
public static class Serializer extends JsonSerializer<SomeObject> {
// ...
}
public static class Deserializer extends JsonDeserializer<SomeObject> {
// ...
}
}
ApplicationContext
中所有的 @JsonComponent
bean 将被自动注册到 Jackson 中,由于 @JsonComponent
使用 @Component
注解标记,因此组件扫描 (component-scanning) 规则将对其生效.
Spring Boot 还提供了 JsonObjectSerializer
和 JsonObjectDeserializer
基类,
它们在序列化对象时为标准的 Jackson 版本提供了有用的替代方案. 有关详细信息,请参阅 Javadoc 中的 JsonObjectSerializer
和 JsonObjectDeserializer
.
7.1.4. MessageCodesResolver
Spring MVC 有一个从绑定错误中生成错误码的策略,用于渲染错误信息: MessageCodesResolver
. 如果您设置了 spring.mvc.message-codes-resolver.format
属性值为 PREFIX_ERROR_CODE
或 POSTFIX_ERROR_CODE
,
Spring Boot 将为你创建该策略 (请参阅 DefaultMessageCodesResolver.Format
中的枚举) .
7.1.5. 静态内容
默认情况下,Spring Boot 将在 classpath 或者 ServletContext
根目录下从名为 /static
(/public
、/resources
或 /META-INF/resources
) 目录中服务静态内容. 它使用了 Spring MVC 的 ResourceHttpRequestHandler
,因此您可以通过添加自己的 WebMvcConfigurerAdapter
并重写 addResourceHandlers
方法来修改此行为.
在一个独立的 (stand-alone) web 应用程序中,来自容器的默认 servlet 也是被启用的,并充当一个回退支援,Spring 决定不处理 ServletContext
根目录下的静态资源,容器的默认 servlet 也将会处理. 大多情况下,这是不会发生的 (除非您修改了默认的 MVC 配置) ,因为 Spring 始终能通过 DispatcherServlet
来处理请求.
默认情况下,资源被映射到 /
,但可以通过 spring.mvc.static-path-pattern 属性调整. 比如,将所有资源重定位到 /resources/
:
spring.mvc.static-path-pattern=/resources/**
您还可以使用 spring.resources.static-locations
属性来自定义静态资源的位置 (使用一个目录位置列表替换默认值) . 根 Servlet context path /
自动作为一个 location 添加进来.
除了上述提到的标准静态资源位置之外,还有一种特殊情况是用于 Webjars content. 如果以 Webjar 格式打包,则所有符合 /webjars/**
的资源都将从 jar 文件中服务.
如果您的应用程序要包成 jar,请不要使用 src/main/webapp 目录. 虽然此目录是一个通用标准,但它只适用于 war 打包,如果生成的是一个 jar,它将被绝大多数的构建工具所忽略.
|
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用例如静态资源缓存清除 (cache busting) 或者 Webjar 版本无关 URL.
要使用 Webjar 版本无关 URL 功能,只需要添加 webjars-locator-core
依赖. 然后声明您的 Webjar,以 jQuery 为例,添加的 "/webjars/jquery/dist/jquery.min.js"
将变成 "/webjars/jquery/x.y.z/dist/jquery.min.js"
,其中 x.y.z
是 Webjar 的版本.
如果您使用 JBoss,则需要声明 webjars-locator-jboss-vfs 依赖,而不是 webjars-locator-core ,否则所有 Webjar 将被解析成 404 .
|
要使用缓存清除功能,以下配置为所有静态资源配置了一个缓存清除方案,实际上是在 URL 上添加了一个内容哈希,例如 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
:
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
模板中的资源链接在运行时被重写,这得益于 ResourceUrlEncodingFilter 为 Thymeleaf 和 FreeMarker 自动配置. 在使用 JSP 时,您应该手动声明此过滤器. 其他模板引擎现在还不会自动支持,但可以与自定义模板宏 (macro) /helper 和 ResourceUrlProvider 结合使用.
|
当使用例如 Javascript 模块加载器动态加载资源时,重命名文件是不可选的. 这也是为什么支持其他策略并且可以组合使用的原因. fixed策略将在 URL 中添加一个静态版本字符串,而不是更改文件名:
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12
使用此配置,JavaScript 模块定位在 "/js/lib/"
下使用固定版本策略 ("/v12/js/lib/mymodule.js"
) ,而其他资源仍使用内容策略 (<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
) .
有关更多支持选项,请参阅 ResourceProperties
.
该功能已经在一个专门的 博客文章和 Spring 框架的参考文档中进行了详细描述 |
7.1.6. 欢迎页面
Spring Boot 支持静态和模板化的欢迎页面. 它首先在配置的静态内容位置中查找 index.html
文件. 如果找不到,则查找 index
模板. 如果找到其中任何一个,它将自动用作应用程序的欢迎页面.
7.1.7. 自定义 Favicon
Spring Boot 在配置的静态内容位置和根 classpath 中查找 favicon.ico
(按顺序) . 如果该文件存在,则将被自动用作应用程序的 favicon.
7.1.8. 路径匹配与内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射相匹配,将传入的 HTTP 请求映射到处理程序 (例如 Controller 方法上的 @GetMapping
注解) .
Spring Boot 默认选择禁用后缀模式匹配,这意味着像 "GET /projects/spring-boot.json"
这样的请求将不会与 @GetMapping("/projects/spring-boot")
映射匹配. 这被视为是 Spring MVC 应用程序的最佳实践 . 此功能在过去对于 HTTP 客户端没有发送正确的 Accept 请求头的情况还是很有用的,我们需要确保将正确的内容类型发送给客户端. 如今,内容协商 (Content Negotiation) 更加可靠.
还有其他方法可以处理 HTTP 客户端发送不一致 Accept 请求头问题. 我们可以使用查询参数来确保像 "GET /projects/spring-boot?format=json"
这样的请求映射到 @GetMapping("/projects/spring-boot")
,而不是使用后缀匹配:
spring.mvc.contentnegotiation.favor-parameter=true
# We can change the parameter name, which is "format" by default:
# spring.mvc.contentnegotiation.parameter-name=myparam
# We can also register additional file extensions/media types with:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
如果您了解相关注意事项并仍希望应用程序使用后缀模式匹配,则需要以下配置:
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true
或者,不打开所有后缀模式,仅打开支持已注册的后缀模式更加安全:
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true
# You can also register additional file extensions/media types with:
# spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc
7.1.9. ConfigurableWebBindingInitializer
Spring MVC 使用一个 WebBindingInitializer
为特定的请求初始化 WebDataBinder
. 如果您创建了自己的 ConfigurableWebBindingInitializer
@Bean
,Spring Boot 将自动配置 Spring MVC 使用它.
7.1.10. 模板引擎
除了 REST web 服务之外,您还可以使用 Spring MVC 来服务动态 HTML 内容. Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP. 当然,许多其他模板引擎也有自己的 Spring MVC 集成.
Spring Boot 包含了以下的模板引擎的自动配置支持:
如果可以,请尽量避免使用 JSP,当使用了内嵌 servlet 容器,会有几个已知限制. |
当您使用这些模板引擎的其中一个并附带了默认配置时,您的模板将从 src/main/resources/templates
自动获取.
IntelliJ IDEA 根据您运行应用程序的方式来对 classpath 进行不同的排序. 在 IDE 中通过 main 方法来运行应用程序将导致与使用 Maven 或 Gradle 或来以 jar 包方式引用程序的排序有所不同,可能会导致 Spring Boot 找不到 classpath 中的模板. 如果您碰到到此问题,可以重新排序 IDE 的 classpath 来放置模块的 classes 和 resources 到首位. 或者,您可以配置模板前缀来搜索 classpath 中的每一个 templates 目录,比如: classpath*:/templates/ .
|
7.1.11. 错误处理
默认情况下,Spring Boot 提供了一个使用了比较合理的方式来处理所有错误的 /error
映射,其在 servlet 容器中注册了一个全局错误页面. 对于机器客户端而言,它将产生一个包含错误、HTTP 状态和异常消息的 JSON 响应. 对于浏览器客户端而言,将以 HTML 格式呈现相同数据的 whitelabel 错误视图 (可添加一个解析到 error
的 View
进行自定义) .
如果要自定义默认错误处理行为,可以设置多个 server.error
属性. 请参阅附录的 “Server Properties” 部分.
要完全替换默认行为,您可以实现 ErrorController
并注册该类型的 bean,或者简单地添加一个类型为 ErrorAttributes
的 bean 来替换内容,但继续使用现用机制.
BasicErrorController 可以作为自定义 ErrorController 的基类,这非常有用,尤其是在您想添加一个新的内容类型 (默认专门处理 text/html ,并为其他内容提供后备) 处理器的情况下. 要做到这点,您只需要继承 BasicErrorController 并添加一个带有 produces 属性的 @RequestMapping 注解的公共方法,之后创建一个新类型的 bean.
|
您还可以定义一个带有 @ControllerAdvice
注解的类来自定义为特定控制器或异常类型返回的 JSON 文档:
@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(YourException.class)
@ResponseBody
ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}
}
以上示例中,如果同包下定义的控制器 AcmeController
抛出了 YourException
,则将使用 CustomerErrorType
类型的 POJO 来代替 ErrorAttributes
做 JSON 呈现.
自定义错误页面
如果您想在自定义的 HTML 错误页面上显示给定的状态码,请将文件添加到 /error
文件夹中. 错误页面可以是静态 HTML (添加在任意静态资源文件夹下) 或者使用模板构建. 文件的名称应该是确切的状态码或者一个序列掩码.
例如,要将 404 映射到一个静态 HTML 文件,文件夹结构可以如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
使用 FreeMarker 模板来映射所有 5xx
错误,文件夹的结构如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftlh
+- <other templates>
对于更复杂的映射,您还通过可以添加实现了 ErrorViewResolver
接口的 bean 来处理:
public class MyErrorViewResolver implements ErrorViewResolver {
@Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
// Use the request or status to optionally return a ModelAndView
return ...
}
}
您还可以使用常规的 Spring MVC 功能,比如 @ExceptionHandler
methods 方法和 @ControllerAdvice
. 之后,ErrorController
将能接收任何未处理的异常.
映射到 Spring MVC 之外的错误页面
对于不使用 Spring MVC 的应用程序,您可以使用 ErrorPageRegistrar
接口来直接注册 ErrorPages
. 抽象部分直接与底层的内嵌 servlet 容器一起工作,即使您没有 Spring MVC DispatcherServlet
也能使用.
@Bean
public ErrorPageRegistrar errorPageRegistrar(){
return new MyErrorPageRegistrar();
}
// ...
private static class MyErrorPageRegistrar implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
}
}
如果您注册了一个 ErrorPage ,它的路径最终由一个 Filter (例如,像一些非 Spring web 框架一样,比如 Jersey 和 Wicket) 处理,则必须将 Filter 显式注册为一个 ERROR dispatcher,如下示例:
|
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
请注意,默认的 FilterRegistrationBean
不包含 ERROR
调度器 (dispatcher) 类型.
Error handling in a war deployment
当部署到 servlet 容器时,Spring Boot 使用其错误页面过滤器会将有错误状态的请求转发到相应的错误页面.这是必需的,因为 Servlet 规范没有提供用于注册错误页面的 API.根据要将 war 文件部署到的容器以及应用程序使用的技术,可能需要一些其他配置.
如果尚未提交响应,则只能将请求转发到正确的错误页面.默认情况下,WebSphere Application Server 8.0 及更高版本在成功完成 servlet 的 service 方法后提交响应. 您应该将 com.ibm.ws.webcontainer.invokeFlushAfterService
设置为 false
来禁用此行为.
如果您使用的是 Spring Security
,并且想访问错误页面中的用户 principal 信息,则必须配置 Spring Security
过滤器来处理的错误是分发。
为此,请将 spring.security.filter.dispatcher-types
属性配置为 async, error, forward, request
7.1.12. Spring HATEOAS
如果您想开发一个使用超媒体 (hypermedia) 的 RESTful API,Spring Boot 提供的 Spring HATEOAS 自动配置在大多数应用程序都工作得非常好. 自动配置取代了 @EnableHypermediaSupport
的需要,
并注册了一些 bean,以便能轻松构建基于超媒体的应用程序,其包括了一个 LinkDiscoverers
(用于客户端支持) 和一个用于配置将响应正确呈现的 ObjectMapper. ObjectMapper 可以通过设置 spring.jackson.*
属性或者 Jackson2ObjectMapperBuilder
bean (如果存在) 自定义.
您可以使用 @EnableHypermediaSupport
来控制 Spring HATEOAS 的配置. 请注意,这使得上述的自定义 ObjectMapper
被禁用.
7.1.13. CORS 支持
Cross-origin resource sharing 跨域资源共享 (Cross-origin resource sharing,CORS) 是 most browsers实现的一个 W3C specification ,其可允许您以灵活的方式指定何种跨域请求可以被授权,而不是使用一些不太安全和不太强大的方式 (比如 IFRAME 或者 JSONP) .
Spring MVC 从 4.2 版本起开始 支持 CORS. 您可在 Spring Boot 应用程序中使用 @CrossOrigin
注解 配置控制器方法启用 CORS.
还可以通过注册一个 WebMvcConfigurer
bean 并自定义 addCorsMappings(CorsRegistry)
方法来定义 全局 CORS 配置 :
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**");
}
};
}
}
7.2. Spring WebFlux 框架
Spring WebFlux 是 Spring Framework 5.0 中新引入的一个响应式 Web 框架. 与 Spring MVC 不同,它不需要 Servlet API,完全异步且无阻塞,并通过 Reactor 项目 实现响应式流 (Reactive Streams) 规范.
@RestController
@RequestMapping("/users")
public class MyRestController {
@GetMapping("/{user}")
public Mono<User> getUser(@PathVariable Long user) {
// ...
}
@GetMapping("/{user}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long user) {
// ...
}
@DeleteMapping("/{user}")
public Mono<User> deleteUser(@PathVariable Long user) {
// ...
}
}
“WebFlux.fn” 为函数式调用方式,它将路由配置与请求处理分开,如下所示:
@Configuration(proxyBeanMethods = false)
public class RoutingConfiguration {
@Bean
public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
.andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
.andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
}
}
@Component
public class UserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
// ...
}
public Mono<ServerResponse> deleteUser(ServerRequest request) {
// ...
}
}
WebFlux 是 Spring Framework 的一部分,详细信息可查看其 参考文档.
您可以根据需要定义尽可能多的 RouterFunction bean 来模块化路由定义. 如果需要设定优先级,Bean 可以指定顺序.
|
首先,将 spring-boot-starter-webflux
模块添加到您的应用程序中.
在应用程序中同时添加 spring-boot-starter-web 和 spring-boot-starter-webflux 模块会导致Spring Boot 自动配置 Spring MVC,而不是使用 WebFlux . 这样做的原因是因为许多 Spring 开发人员将 spring-boot-starter-webflux 添加到他们的 Spring MVC 应用程序中只是为了使用响应式 WebClient .
您仍然可以通过设置 SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE) 来强制执行您选择的应用程序类型.
|
7.2.1. Spring WebFlux 自动配置
Spring Boot 为 Spring WebFlux 提供自动配置,适用于大多数应用程序.
自动配置在 Spring 的默认基础上添加了以下功能:
如果你要保留 Spring Boot WebFlux 功能并且想要添加其他 WebFlux
配置,可以添加自己的 @Configuration
类,类型为 WebFluxConfigurer
,但不包含 @EnableWebFlux
.
如果您想完全控制 Spring WebFlux,可以将 @EnableWebFlux
注解到自己的 @Configuration
.
7.2.2. 使用 HttpMessageReader 和 HttpMessageWriter 作为 HTTP 编解码器
Spring WebFlux 使用 HttpMessageReader
和 HttpMessageWriter
接口来转换 HTTP 的请求和响应. 它们通过检测 classpath 中可用的类库,配置了 CodecConfigurer
生成合适的默认值.
Spring Boot 通过使用 CodecCustomizer
实例加强定制. 例如,spring.jackson.*
配置 key 应用于 Jackson 编解码器.
如果需要添加或自定义编解码器,您可以创建一个自定义的 CodecCustomizer
组件,如下所示:
import org.springframework.boot.web.codec.CodecCustomizer;
@Configuration(proxyBeanMethods = false)
public class MyConfiguration {
@Bean
public CodecCustomizer myCodecCustomizer() {
return codecConfigurer -> {
// ...
};
}
}
您还可以利用 Boot 自定义 JSON 序列化器和反序列化器.
7.2.3. 静态内容
默认情况下,Spring Boot 将在 classpath 或者 ServletContext
根目录下从名为 /static
(/public
、/resources
或 /META-INF/resources
) 目录中服务静态内容. 它使用了 Spring WebFlux 的 ResourceWebHandler
,因此您可以通过添加自己的 WebFluxConfigurer
并重写 addResourceHandlers
方法来修改此行为.
默认情况下,资源被映射到 /
,但可以通过 spring.webflux.static-path-pattern
属性调整. 比如,将所有资源重定位到 /resources/
:
spring.webflux.static-path-pattern=/resources/**
您还可以使用 spring.resources.static-locations
属性来自定义静态资源的位置 (使用一个目录位置列表替换默认值) ,如果这样做,默认的欢迎页面检测会切换到您自定义的位置. 因此,如果启动时有任何其中一个位置存在 index.html
,那么它将是应用程序的主页.
除了上述提到的标准静态资源位置之外,还有一种特殊情况是用于 Webjars 内容 . 如果以 Webjar 格式打包,则所有符合 /webjars/**
的资源都将从 jar 文件中服务.
Spring WebFlux 应用程序并不严格依赖于 Servlet API,因此它们不能作为 war 文件部署,也不能使用 src/main/webapp 目录.
|
7.2.4. 模板引擎
除了 REST web 服务之外,您还可以使用 Spring WebFlux 来服务动态 HTML 内容. Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 Mustache.
Spring Boot 包含了以下的模板引擎的自动配置支持:
当您使用这些模板引擎的其中一个并附带了默认配置时,您的模板将从 src/main/resources/templates
自动获取.
7.2.5. Error Handling
Spring Boot 提供了一个 WebExceptionHandler
,它以合理的方式处理所有错误. 它在处理顺序中的位置紧接在 WebFlux 提供的处理程序之前,这些处理器排序是最后的. 对于机器客户端,它会生成一个 JSON 响应,其中包含错误详情、HTTP 状态和异常消息. 对于浏览器客户端,有一个 whitelabel 错误处理程序,它以 HTML 格式呈现同样的数据. 您还可以提供自己的 HTML 模板来显示错误 (请参阅下一节) .
自定义此功能的第一步通常会沿用现有机制,但替换或扩充了错误内容. 为此,您可以添加 ErrorAttributes
类型的 bean.
想要更改错误处理行为,可以实现 ErrorWebExceptionHandler
并注册该类型的 bean. 因为 WebExceptionHandler
是一个非常底层的异常处理器,所以 Spring Boot 还提供了一个方便的 AbstractErrorWebExceptionHandler
来让你以 WebFlux 的方式处理错误,如下所示:
public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
// Define constructor here
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions
.route(aPredicate, aHandler)
.andRoute(anotherPredicate, anotherHandler);
}
}
要获得更完整的功能,您还可以直接继承 DefaultErrorWebExceptionHandler
并覆盖相关方法.
自定义错误页面
如果您想在自定义的 HTML 错误页面上显示给定的状态码,请将文件添加到 /error
文件夹中. 错误页面可以是静态 HTML (添加在任意静态资源文件夹下) 或者使用模板构建. 文件的名称应该是确切的状态码或者一个序列掩码.
例如,要将 404 映射到一个静态 HTML 文件,文件夹结构可以如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
使用 Mustache 模板来映射所有 5xx
错误,文件夹的结构如下:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
7.2.6. Web 过滤器
Spring WebFlux 提供了一个 WebFilter
接口,可以通过实现该接口来过滤 HTTP 请求/响应消息交换. 在应用程序上下文中找到的 WebFilter
bean 将自动用于过滤每个消息交换.
如果过滤器的执行顺序很重要,则可以实现 Ordered
接口或使用 @Order
注解来指定顺序. Spring Boot 自动配置可能为您配置了几个 Web 过滤器. 执行此操作时,将使用下表中的顺序:
Web Filter | Order |
---|---|
|
|
|
|
|
|
7.3. JAX-RS 与 Jersey
如果您喜欢 JAX-RS 编程模型的 REST 端点,则可以使用一个实现来替代 Spring MVC. Jersey 和 Apache CXF 都能开箱即用. CXF 要求在应用程序上下文中以 @Bean
的方式将它注册为一个 Servlet
或者 Filter
. Jersey 有部分原生 Spring 支持,所以我们也在 starter 中提供了与 Spring Boot 整合的自动配置支持.
要使用 Jersey,只需要将 spring-boot-starter-jersey
作为依赖引入,然后您需要一个 ResourceConfig
类型的 @Bean
,您可以在其中注册所有端点:
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(Endpoint.class);
}
}
Jersey 对于扫描可执行归档文件的支持是相当有限的. 例如,它无法扫描一个完整的可执行 jar 文件中的端点,同样,当运行一个可执行的 war 文件时,它也无法扫描包中 WEB-INF/classes 下的端点. 为了避免该限制,您不应该使用 packages 方法,应该使用上述的 register 方法来单独注册每一个端点.
|
您可以注册任意数量实现了 ResourceConfigCustomizer
的 bean,以实现更高级的定制化.
所有注册的端点都应注解了 @Components
并具有 HTTP 资源注解 ( @GET
等) ,例如:
@Component
@Path("/hello")
public class Endpoint {
@GET
public String message() {
return "Hello";
}
}
由于 Endpoint 是一个 Spring @Component
,它的生命周期由 Spring 管理,您可以使用 @Autowired
注入依赖并使用 @Value
注入外部配置. 默认情况下,Jersey servlet 将被注册并映射到 /*
. 您可以通过将 @ApplicationPath
添加到 ResourceConfig
来改变此行为.
默认情况下,Jersey 在 ServletRegistrationBean
类型的 @Bean 中被设置为一个名为 jerseyServletRegistration
的 Servlet. 默认情况下,该 servlet 将被延迟初始化,您可以使用 spring.jersey.servlet.load-on-startup
自定义.
您可以禁用或通过创建一个自己的同名 bean 来覆盖该 bean. 您还可以通过设置 spring.jersey.type=filter
使用过滤器替代 servlet (该情况下, 替代或覆盖 @Bean
的为 jerseyFilterRegistration
) .
该过滤器有一个 @Order
,您可以使用 spring.jersey.filter.order
设置. 可以使用 spring.jersey.init.*
指定一个 map 类型的 property 以给定 servlet 和过滤器的初始化参数.
这里有一个 Jersey 示例,您可以解如何设置.
7.4. 内嵌 Servlet 容器支持
Spring Boot 包含了对内嵌 Tomcat, Jetty, 和 Undertow 服务器的支持. 大部分开发人员只需简单地使用对应的 Starter 来获取完整的配置实例. 默认情况下,内嵌服务器将监听 8080
上的 HTTP 请求.
7.4.1. Servlets, Filters, 与 listeners
使用内嵌 servlet 容器时,您可以使用 Spring bean 或者扫描方式来注册 Servlet 规范中的 Servlet、Filter 和所有监听器 (比如 HttpSessionListener
) .
将 Servlet、Filter 和 Listener 注册为 Spring
任何 Servlet
、Filter
或 *Listener
的 Spring bean 实例都将被注册到内嵌容器中. 如果您想引用 application.properties
中的某个值,这可能会特别方便.
默认情况下,如果上下文只包含单个 Servlet,它将映射到 /
. 在多个 Servlet bean 的情况下,bean 的名称将用作路径的前缀. Filter 将映射到 /*
.
如果基于约定配置的映射不够灵活,您可以使用 ServletRegistrationBean
、FilterRegistrationBean
和 ServletListenerRegistrationBean
类来完全控制.
通常把过滤器 bean 无序是安全的. 如果需要特定的顺序,则应使用 @Order
注解 Filter
或使其实现 Ordered
. 您不能通过使用 @Order
注解 Filter
的bean方法来配置 Filter
的顺序.
如果您不能更改 Filter
类以添加 @Order
或实现 Ordered
,则必须为 Filter
定义一个 FilterRegistrationBean
并使用 setOrder(int)
方法设置注册bean的顺序.
则应避免在 Ordered.HIGHEST_PRECEDENCE
顺序点配置读取请求体的过滤器,因为它的字符编码可能与应用程序的字符编码配置不一致. 如果一个 Servlet 过滤器包装了请求,则应使用小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
的顺序点对其进行配置.
要查看应用程序中每个过滤器的顺序,请为 web logging group (logging.level.web=debug ) 启用调试级别的日志记录. 然后,将在启动时记录已注册过滤器的详细信息,包括其顺序和URL模式. .
|
注册 Filter Bean时要小心,因为它们是在应用程序生命周期中很早就初始化的. 如果需要注册与其他bean交互的 Filter ,请考虑改用 DelegatingFilterProxyRegistrationBean .
|
7.4.2. Servlet 上下文初始化
内嵌 servlet 容器不会直接执行 Servlet 3.0+ 的 javax.servlet.ServletContainerInitializer
接口或 Spring 的 org.springframework.web.WebApplicationInitializer
接口. 这是一个有意的设计决策,旨在降低在 war 内运行时第三方类库产生的风险,防止破坏 Sring Boot 应用程序.
如果您需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应注册一个实现了 org.springframework.boot.context.embedded.ServletContextInitializer
接口的 bean. onStartup
方法提供了针对 ServletContext
的访问入口,如果需要,它可以容易作为现有 WebApplicationInitializer
的适配器.
7.4.3. ServletWebServerApplicationContext
Spring Boot 底层使用了一个不同的 ApplicationContext
类型来支持内嵌 servlet. ServletWebServerApplicationContext
是一个特殊 WebApplicationContext
类型,它通过搜索单个 ServletWebServerFactory
bean 来引导自身. 通常,TomcatServletWebServerFactory
、 JettyServletWebServerFactory
或者 UndertowServletWebServerFactory
中的一个将被自动配置.
通常,你不需要知道这些实现类. 大部分应用程序会自动配置,并为您创建合适的 ApplicationContext 和 ServletWebServerFactory .
|
7.4.4. 自定义内嵌 Servlet 容器
可以使用 Spring Environment
属性来配置通用的 servlet 容器设置. 通常,您可以在 application.properties
文件中定义这些属性.
常用服务器设置包括:
Spring Boot 尽可能暴露通用的设置,但并不总是都可以. 针对这些情况,专用的命名空间为特定的服务器提供了自定义功能 (请参阅 server.tomcat
和 server.undertow
) . 例如,您可以使用内嵌 servlet 容器的特定功能来配置 access logs.
有关完整的内容列表,请参阅 ServerProperties 类.
|
以编程方式自定义
如果您需要以编程的方式配置内嵌 servlet 容器,可以注册一个是实现了 WebServerFactoryCustomizer
接口的 Spring bean. WebServerFactoryCustomizer
提供了对 ConfigurableServletWebServerFactory
的访问入口,其中包含了许多自定义 setter 方法. 以下示例使用了编程方式来设置端口:
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
TomcatServletWebServerFactory , JettyServletWebServerFactory 和 UndertowServletWebServerFactory 是 ConfigurableServletWebServerFactory 的具体子类,它们分别为 Tomcat、Jetty 和 Undertow 提供了额外的自定义 setter 方法. .
|
直接自定义 ConfigurableServletWebServerFactory
如果上述的自定义方式太局限,您可以自己注册 TomcatServletWebServerFactory
、JettyServletWebServerFactory
或 UndertowServletWebServerFactory
bean.
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
return factory;
}
Setter 方法提供了许多配置选项. 还有几个 hook 保护方法供您深入定制. 有关详细信息,请参阅 源码文档.
7.5. 内嵌响应式服务器支持
Spring Boot 包括对以下内嵌响应式 Web 服务器的支持: Reactor Netty、Tomcat、Jetty 和 Undertow. 大多数开发人员使用对应的 Starter 来获取一个完全配置的实例. 默认情况下,内嵌服务器在 8080 端口上监听 HTTP 请求.
7.6. 响应式服务器资源配置
在自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 将创建特定的 bean 为服务器实例提供 HTTP 资源: ReactorResourceFactory
或 JettyResourceFactory
.
默认情况下,这些资源也将与 Reactor Netty 和 Jetty 客户端共享以获得最佳性能,具体如下:
-
用于服务器和客户端的的相同技术
-
客户端实例使用了 Spring Boot 自动配置的
WebClient.Builder
bean 构建.
开发人员可以通过提供自定义的 ReactorResourceFactory
或 JettyResourceFactory
bean 来重写 Jetty 和 Reactor Netty 的资源配置 —— 将应用于客户端和服务器.
您可以在 WebClient Runtime 章节中了解有关客户端资源配置的更多内容.
8. RSocket
RSocket 是用于字节流传输的二进制协议. 它通过通过单个连接传递的异步消息来启用对称交互模型.
Spring框架的 spring-messaging
模块在客户端和服务器端都支持RSocket请求者和响应者. 有关更多详细信息,请参见Spring Framework参考中的 RSocket 部分,其中包括RSocket协议的概述.
8.1. RSocket策略自动配置
Spring Boot自动配置一个 RSocketStrategies
bean,该bean提供了编码和解码RSocket有效负载所需的所有基础结构. 默认情况下,自动配置将尝试 (按顺序) 配置以下内容:
-
Jackson的 CBOR 编解码器
-
Jackson的 JSON 编解码器
spring-boot-starter-socket
启动器提供了两个依赖. 查阅 Jackson支持部分,以了解有关定制可能性的更多信息.
开发人员可以通过创建实现 RSocketStrategiesCustomizer
接口的bean来自定义 RSocketStrategies
组件. 请注意,它们的 @Order
很重要,因为它确定编解码器的顺序.
8.2. RSocket服务器自动配置
Spring Boot提供了RSocket服务器自动配置. 所需的依赖由 spring-boot-starter-rsocket
提供.
Spring Boot允许从WebFlux服务器通过WebSocket暴露RSocket,或支持独立的RSocket服务器. 这取决于应用程序的类型及其配置.
对于WebFlux应用程序 (即 WebApplicationType.REACTIVE
类型) ,仅当以下属性匹配时,RSocket服务器才会插入Web服务器:
spring.rsocket.server.mapping-path=/rsocket # a mapping path is defined
spring.rsocket.server.transport=websocket # websocket is chosen as a transport
#spring.rsocket.server.port= # no port is defined
由于RSocket本身是使用该库构建的,因此只有Reactor Netty支持将RSocket插入Web服务器. |
另外,RSocket TCP或Websocket服务器也可以作为独立的嵌入式服务器启动. 除了依赖性要求之外,唯一需要的配置是为该服务器定义端口:
spring.rsocket.server.port=9898 # the only required configuration
spring.rsocket.server.transport=tcp # you're free to configure other properties
8.3. Spring Messaging RSocket支持
Spring Boot将为RSocket自动配置Spring Messaging基础结构.
这意味着Spring Boot将创建一个 RSocketMessageHandler
bean,该bean将处理对您的应用程序的RSocket请求.
8.4. 使用 RSocketRequester
调用RSocket服务
在服务器和客户端之间建立 RSocket
通道后,任何一方都可以向另一方发送或接收请求.
作为服务器,您可以在RSocket @Controller
的任何处理程序方法上注入 RSocketRequester
实例. 作为客户端,您需要首先配置和建立RSocket连接. 在这种情况下,Spring Boot会使用预期的编解码器自动配置 RSocketRequester.Builder
.
RSocketRequester.Builder
实例是一个原型bean,这意味着每个注入点将为您提供一个新实例. 这样做是有目的的,因为此构建器是有状态的,因此您不应使用同一实例创建具有不同设置的请求者.
以下代码显示了一个典型示例:
@Service
public class MyService {
private final Mono<RSocketRequester> rsocketRequester;
public MyService(RSocketRequester.Builder rsocketRequesterBuilder) {
this.rsocketRequester = rsocketRequesterBuilder
.connectTcp("example.org", 9898).cache();
}
public Mono<User> someRSocketCall(String name) {
return this.rsocketRequester.flatMap(req ->
req.route("user").data(name).retrieveMono(User.class));
}
}
9. 安全
默认情况下,如果 Spring Security 在 classpath 上,则 Web 应用程序是受保护的. Spring Boot 依赖 Spring Security 的内容协商策略来确定是使用 httpBasic
还是 formLogin
.
要给 Web 应用程序添加方法级别的安全保护,可以使用 @EnableGlobalMethodSecurity
注解设置. 有关更多其他信息,您可以在 Spring Security 参考指南中找到.
默认的 UserDetailsService
只有一个用户. 用户名为 user
,密码是随机的,在应用程序启动时会以 INFO 级别打印出来,如下所示:
Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35
如果您对日志配置进行微调,请确保将 org.springframework.boot.autoconfigure.security 的级别设置为 INFO . 否则,默认密码不会打印出来.
|
您可以通过提供 spring.security.user.name
和 spring.security.user.password
来更改用户名和密码.
您在 Web 应用程序中默认会获得以下基本功能:
-
一个
UserDetailsService
(或 WebFlux 应用程序中的ReactiveUserDetailsService
) bean,采用内存存储形式,有一个自动生成密码的用户 (有关用户属性,请参阅SecurityProperties.User
) . -
用于整个应用程序 (如果 actuator 在 classpath 上,则包括 actuator 端点) 基于表单登录或 HTTP Basic 认证 (取决于 Content-Type) .
-
一个用于发布身份验证事件的
DefaultAuthenticationEventPublisher
.
您可以通过为其添加一个 bean 来提供不同的 AuthenticationEventPublisher
.
9.1. MVC 安全
默认的安全配置在 SecurityAutoConfiguration
和 UserDetailsServiceAutoConfiguration
中实现. SecurityAutoConfiguration
导入用于 Web 安全的 SpringBootWebSecurityConfiguration
,UserDetailsServiceAutoConfiguration
配置身份验证,这同样适用于非 Web 应用程序. 要完全关闭默认的 Web 应用程序安全配置,可以添加 WebSecurityConfigurerAdapter
类型的 bean (这样做不会禁用 UserDetailsService
配置或 Actuator
的安全保护) .
要同时关闭 UserDetailsService
配置,您可以添加 UserDetailsService
、AuthenticationProvider
或 AuthenticationManager
类型的 bean. Spring Boot 示例中有几个使用了安全保护的应用程序,他们或许可以帮助到您.
可以通过添加自定义 WebSecurityConfigurerAdapter
来重写访问规则. Spring Boot 提供了便捷方法,可用于重写 actuator
端点和静态资源的访问规则. EndpointRequest
可用于创建一个基于 management.endpoints.web.base-path
属性的 RequestMatcher
. PathRequest
可用于为常用位置中的资源创建一个 RequestMatcher
.
9.2. WebFlux 安全
与 Spring MVC 应用程序类似,您可以通过添加 spring-boot-starter-security
依赖来保护 WebFlux 应用程序. 默认的安全配置在 ReactiveSecurityAutoConfiguration
和 UserDetailsServiceAutoConfiguration
中实现. ReactiveSecurityAutoConfiguration
导入用于 Web 安全的 WebFluxSecurityConfiguration
,UserDetailsServiceAutoConfiguration
配置身份验证,这同样适用于非 Web 应用程序. 要完全关闭默认的 Web 应用程序安全配置,可以添加 WebFilterChainProxy
类型的 bean (这样做不会禁用 UserDetailsService
配置或 Actuator
的安全保护) .
要同时关闭 UserDetailsService
配置,您可以添加 ReactiveUserDetailsService
或 ReactiveAuthenticationManager
类型的 bean.
可以通过添加自定义 SecurityWebFilterChain
来重写访问规则. Spring Boot 提供了便捷方法,可用于重写 actuator
端点和静态资源的访问规则. EndpointRequest
可用于创建一个基于 management.endpoints.web.base-path
属性的 ServerWebExchangeMatcher
.
PathRequest
可用于为常用位置中的资源创建一个 ServerWebExchangeMatcher
.
例如,您可以通过添加以下内容来自定义安全配置:
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.pathMatchers("/foo", "/bar")
.authenticated().and()
.formLogin().and()
.build();
}
9.3. OAuth2
OAuth2 是 Spring 支持的一种广泛使用的授权框架.
9.3.1. 客户端
如果您的 classpath 上有 spring-security-oauth2-client
,则可以利用一些自动配置来轻松设置 OAuth2/Open ID Connect 客户端. 该配置使用 OAuth2ClientProperties
的属性. 相同的属性适用于 servlet 和响应式应用程序.
您可以在 spring.security.oauth2.client
前缀下注册多个 OAuth2 客户端和提供者 (provider) ,如下所示:
spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri=https://my-redirect-uri.com
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server/oauth/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server/oauth/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server/token_keys
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name
对于支持 OpenID Connect discovery 的 OpenID Connect 提供者,可以进一步简化配置. 需要使用 issuer-uri
配置提供者,issuer-uri
是其 Issuer Identifier 的 URI.
例如,如果提供的 issuer-uri 是 "https://example.com", 则将对 "https://example.com/.well-known/openid-configuration" 发起一个 OpenID Provider Configuration Request
. 期望结果是一个 OpenID Provider Configuration Response
. 以下示例展示了如何使用 issuer-uri
配置一个 OpenID Connect Provider:
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
默认情况下,Spring Security 的 OAuth2LoginAuthenticationFilter
仅处理与 /login/oauth2/code/*
相匹配的 URL. 如果要自定义 redirect-uri
以使用其他匹配模式,则需要提供配置以处理该自定义模式. 例如,对于 servlet 应用程序,您可以添加类似于以下 WebSecurityConfigurerAdapter
:
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.redirectionEndpoint()
.baseUri("/custom-callback");
}
}
OAuth2 客户端注册常见的提供者
对于常见的 OAuth2 和 OpenID 提供者 (provider) ,包括 Google、Github、Facebook 和 Okta,我们提供了一组提供者默认设置 (分别是 google、github、facebook 和 okta) .
如果您不需要自定义这些提供者,则可以将 provider
属性设置为您需要推断默认值的属性. 此外,如果客户端注册的 key 与默认支持的提供者匹配,则 Spring Boot 也会推断出来.
换而言之,以下示例中的两个配置使用了 Google 提供者:
spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password
9.3.2. 资源服务器
如果在 classpath 上有 spring-security-oauth2-resource-server
,只要指定了 JWK Set URI 或 OIDC Issuer URI,Spring Boot 就可以设置 OAuth2 资源服务器,如下所示:
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
如果授权服务器不支持JWK设置URI,则可以使用用于验证JWT签名的公共密钥来配置资源服务器. 可以使用 spring.security.oauth2.resourceserver.jwt.public-key-location 属性来完成此操作,该属性值需要指向包含PEM编码的x509格式的公钥的文件.
|
相同的属性适用于 servlet 和响应式应用程序.
或者,您可以为 servlet 应用程序定义自己的 JwtDecoder bean,或为响应式应用程序定义 ReactiveJwtDecoder
.
如果使用不透明令牌而不是JWT,则可以配置以下属性以通过自省来验证令牌:
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret
同样,相同的属性适用于servlet和反应式应用程序.
另外,您可以为Servlet应用程序定义自己的 OpaqueTokenIntrospector
Bean,或者为反应性应用程序定义 ReactiveOpaqueTokenIntrospector
.
9.3.3. 授权服务器
目前,Spring Security 没有提供 OAuth 2.0 授权服务器实现. 但此功能可从 Spring Security OAuth 项目获得,该项目最终会被 Spring Security 所取代. 在此之前,您可以使用 spring-security-oauth2-autoconfigure
模块轻松设置 OAuth 2.0 授权服务器,请参阅 其文档以获取详细信息.
9.4. SAML 2.0
9.4.1. 依赖方
如果您在类路径中具有 spring-security-saml2-service-provider
,则可以利用一些自动配置功能来轻松设置SAML 2.0依赖方. 此配置利用 Saml2RelyingPartyProperties
下的属性.
依赖方注册代表身份提供商IDP和服务提供商SP之间的配对配置. 您可以在 spring.security.saml2.relyingparty
前缀下注册多个依赖方,如以下示例所示:
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.identityprovider.sso-url=https://remoteidp1.sso.url
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.identityprovider.sso-url=https://remoteidp2.sso.url
9.5. Actuator 安全
出于安全考虑,默认情况下禁用除 /health
和 /info
之外的所有 actuator. 可用 management.endpoints.web.exposure.include
属性启用 actuator.
如果 Spring Security 位于 classpath 上且没有其他 WebSecurityConfigurerAdapter,则除了 /health
和 /info
之外的所有 actuator 都由 Spring Boot 自动配置保护. 如果您定义了自定义 WebSecurityConfigurerAdapter
,则 Spring Boot 自动配置将不再生效,您可以完全控制 actuator 的访问规则.
在设置 management.endpoints.web.exposure.include 之前,请确保暴露的 actuator 没有包含敏感信息和 / 或被防火墙保护亦或受 Spring Security 之类的保护.
|
9.5.1. 跨站请求伪造保护
由于 Spring Boot 依赖 Spring Security 的默认值配置,因此默认情况下会启用 CSRF 保护. 这意味着当使用默认安全配置时,需要 POST
(shutdown 和 loggers 端点) 、PUT
或 DELETE
的 actuator 端点将返回 403 禁止访问错误.
我们建议仅在创建非浏览器客户端使用的服务时才完全禁用 CSRF 保护. |
有关 CSRF 保护的其他信息,请参阅 Spring Security 参考指南
10. 使用 SQL 数据库
Spring Framework 为 SQL 数据库提供了广泛的支持. 从直接使用 JdbcTemplate
进行 JDBC 访问到完全的对象关系映射 (object relational mapping) 技术,比如 Hibernate. Spring Data提供了更多级别的功能,直接从接口创建的 Repository
实现,并使用了约定从方法名生成查询.
10.1. 配置数据源
Java 的 javax.sql.DataSource
接口提供了一个使用数据库连接的标准方法. 通常,数据源使用 URL 和一些凭据信息来建立数据库连接.
查看 “How-to” 部分 获取更多高级示例,通常您可以完全控制数据库的配置. |
10.1.1. 内嵌数据库支持
使用内嵌内存数据库来开发应用程序非常方便的. 显然,内存数据库不提供持久存储. 在应用启动时,您需要填充数据库,并在应用程序结束时丢弃数据.
How-to 部分包含了如何初始化数据库方面的内容. |
如果您在测试中使用此功能,您可能会注意到,无论使用了多少应用程序上下文,整个测试套件都会重复使用相同的数据库. 如果您想确保每个上下文都有一个单独的内嵌数据库,则应该将 |
以下是 POM 依赖示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
要自动配置内嵌数据库,您需要一个 spring-jdbc 依赖. 在这个例子中,它是通过 spring-boot-starter-data-jpa 引入.
|
如果出于某些原因,您需要配置内嵌数据库的连接 URL,则应注意确保禁用数据库的自动关闭功能.
如果您使用 H2,则应该使用 DB_CLOSE_ON_EXIT=FALSE 来设置. 如果您使用 HSQLDB,则确保不使用 shutdown=true . 禁用数据库的自动关闭功能允许 Spring Boot 控制数据库何时关闭,从而确保一旦不再需要访问数据库时就触发.
|
10.1.2. 连接生产数据库
生产数据库连接也可以使用使用 DataSource
自动配置. Spring Boot 使用以下算法来选择一个特定的实现:
-
出于性能和并发性的考虑,我们更喜欢 HikariCP 连接池. 如果 HikariCP 可用,我们总是选择它.
-
否则,如果 Tomcat 池 DataSource 可用,我们将使用它.
-
如果 HikariCP 和 Tomcat 池数据源不可用,但 Commons DBCP2 可用,我们将使用它.
如果您使用了 spring-boot-starter-jdbc
或者 spring-boot-starter-data-jpa starter
,您将自动得到 HikariCP 依赖.
您完全可以绕过该算法,并通过 spring.datasource.type 属性指定要使用的连接池. 如果您在 Tomcat 容器中运行应用程序,默认提供 tomcat-jdbc ,这点尤其重要.
|
可以手动配置其他连接池. 如果您定义了自己的 DataSource bean,则自动配置将不会触发.
|
数据源配置由 spring.datasource.*
中的外部属性所控制. 例如,您可以在 application.properties
中声明以下部分:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
您至少应该使用 spring.datasource.url 属性来指定 URL,否则 Spring Boot 将尝试自动配置内嵌数据库.
|
通常您不需要指定 driver-class-name ,因为 Spring boot 可以从 url 推导出大多数数据库.
|
对于要创建的池 DataSource ,我们需要能够验证有效的 Driver 类是否可用,因此我们在使用之前进行检查. 例如,如果您设置了 spring.datasource.driver-class-name=com.mysql.jdbc.Driver ,那么该类必须可加载.
|
有关更多支持选项,请参阅 DataSourceProperties
.
这些都是标准选项,与实际的实现无关. 还可以使用各自的前缀 (spring.datasource.hikari.
、spring.datasource.tomcat.
和 spring.datasource.dbcp2.*
) 微调实现特定的设置. 请参考您现在使用的连接池实现的文档来获取更多信息.
例如,如果你使用 Tomcat connection pool,则可以自定义许多其他设置,如下:
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.tomcat.max-wait=10000
# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=50
# Validate the connection before borrowing it from the pool.
spring.datasource.tomcat.test-on-borrow=true
10.1.3. 连接 JNDI 数据源
如果要将 Spring Boot 应用程序部署到应用服务器 (Application Server) 上,您可能想使用应用服务器的内置功能和 JNDI 访问方式来配置和管理数据源.
spring.datasource.jndi-name
属性可作为 spring.datasource.url
、spring.datasource.username
和 spring.datasource.password
属性的替代方法,用于从特定的 JNDI 位置访问 DataSource
. 例如,application.properties
中的以下部分展示了如何访问 JBoss AS 定义的 DataSource
:
spring.datasource.jndi-name=java:jboss/datasources/customers
10.2. 使用 JdbcTemplate
Spring 的 JdbcTemplate
和 NamedParameterJdbcTemplate
类是自动配置的,您可以使用 @Autowire
将它们直接注入您的 bean 中:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JdbcTemplate jdbcTemplate;
@Autowired
public MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// ...
}
您可以使用 spring.jdbc.template.*
属性来自定义一些 template 的属性,如下:
spring.jdbc.template.max-rows=500
NamedParameterJdbcTemplate 在底层重用了相同的 JdbcTemplate 实例. 如果定义了多个 JdbcTemplate 且没有声明 primary 主候选,则不会自动配置 NamedParameterJdbcTemplate .
|
10.3. JPA 与 Spring Data JPA
Java Persistence API (Java 持久化 API) 是一项标准技术,可让您将对象映射到关系数据库. spring-boot-starter-data-jpa
POM 提供了一个快速起步的方法. 它提供了以下关键依赖:
-
Hibernate: 最受欢迎的 JPA 实现之一.
-
Spring Data JPA: 可以轻松地实现基于 JPA 的资源库.
-
Spring ORMs: Spring Framework 的核心 ORM 支持
我们不会在这里介绍太多关于 JPA 或者 Spring Data 的相关内容. 您可以在 spring.io 上查看使用 “JPA 访问数据”,获取阅读 Spring Data JPA 和 Hibernate 的参考文档. |
10.3.1. 实体类
通常,JPA Entity (实体) 类是在 persistence.xml
文件中指定的. 使用了 Spring Boot,该文件将不是必需的,可以使用 Entity Scanning (实体扫描) 来代替. 默认情况下,将搜索主配置类 (使用了 @EnableAutoConfiguration
或 @SpringBootApplication
注解) 下面的所有包.
任何用了 @Entity
、@Embeddable
或者 @MappedSuperclass
注解的类将被考虑. 一个典型的实体类如下:
package com.example.myapp.domain;
import java.io.Serializable;
import javax.persistence.*;
@Entity
public class City implements Serializable {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String state;
// ... additional members, often include @OneToMany mappings
protected City() {
// no-args constructor required by JPA spec
// this one is protected since it shouldn't be used directly
}
public City(String name, String state) {
this.name = name;
this.state = state;
}
public String getName() {
return this.name;
}
public String getState() {
return this.state;
}
// ... etc
}
您可以使用 @EntityScan 注解自定义实体类的扫描位置. 请参见 “从 Spring configuration 配置中分离 @Entity 定义” 章节.
|
10.3.2. Spring Data JPA 资源库
Spring Data JPA 资源库 (repository) 是接口,您可以定义用于访问数据. JAP 查询是根据您的方法名自动创建. 例如,CityRepository
接口可以声明 findAllByState(String state)
方法来查找指定状态下的所有城市.
对于更加复杂的查询,您可以使用 Spring Data 的 Query
注解
Spring Data 资源库通常继承自 Repository
或者 CrudRepository
接口. 如果您使用了自动配置,则将从包含主配置类 (使用了 @EnableAutoConfiguration
或 @SpringBootApplication
注解) 的包中搜索资源库:
以下是一个典型的 Spring Data 资源库接口定义:
package com.example.myapp.domain;
import org.springframework.data.domain.*;
import org.springframework.data.repository.*;
public interface CityRepository extends Repository<City, Long> {
Page<City> findAll(Pageable pageable);
City findByNameAndStateAllIgnoringCase(String name, String state);
}
Spring Data JPA 资源库支持三种不同的引导模式: default
、deferred
和 lazy
. 要启用延迟或懒惰引导,请将 spring.data.jpa.repositories.bootstrap-mode
分别设置为 deferred
或 lazy
. 使用延迟或延迟引导时,自动配置的 EntityManagerFactoryBuilder
将使用上下文的 AsyncTaskExecutor
(如果有) 作为 applicationTaskExecutor
.
我们几乎没有接触到 Spring Data JPA 的表面内容. 有关详细信息,请查阅 Spring Data JPA 参考文档. |
10.3.3. 创建和删除 JPA 数据库
默认情况下,仅当您使用了内嵌数据库 (H2、HSQL 或 Derby) 时才会自动创建 JPA 数据库. 您可以使用 spring.jpa.*
属性显式配置 JPA 设置. 例如,要创建和删除表,您可以将以下内容添加到 application.properties
中:
spring.jpa.hibernate.ddl-auto=create-drop
关于上述功能,Hibernate 自己的内部属性名称 (如果您记住更好) 为 hibernate.hbm2ddl.auto . 您可以使用 spring.jpa.properties.* (在添加到实体管理器之前,该前缀将被删除) 来将 Hibernate 原生属性一同设置:
|
spring.jpa.properties.hibernate.globally_quoted_identifiers=true
上面示例中将 true
值设置给 hibernate.globally_quoted_identifiers
属性,该属性将传给 Hibernate 实体管理器.
默认情况下,DDL 执行 (或验证) 将延迟到 ApplicationContext
启动后. 还有一个 spring.jpa.generate-ddl
标志,如果 Hibernate 自动配置是激活的,那么它将不会被使用,因为 ddl-auto
设置更细粒度.
10.3.4. 在视图中打开 EntityManager
如果您正在运行 web 应用程序,Spring Boot 将默认注册 OpenEntityManagerInViewInterceptor
用于在视图中打开 EntityManager 模式,即运允许在 web 视图中延迟加载. 如果您不想开启这个行为,则应在 application.properties
中将 spring.jpa.open-in-view
设置为 false
.
10.4. Spring Data JDBC
Spring Data 包含了对 JDBC 资源库的支持,并将自动为 CrudRepository
上的方法生成 SQL. 对于更高级的查询,它提供了 @Query
注解.
当 classpath 下存在必要的依赖时,Spring Boot 将自动配置 Spring Data 的 JDBC 资源库. 可以通过添加单个 spring-boot-starter-data-jdbc
依赖引入到项目中. 如有必要,可通过在应用程序中添加 @EnableJdbcRepositories
注解或 JdbcConfiguration
子类来控制 Spring Data JDBC 的配置.
有关 Spring Data JDBC 的完整详细信息,请参阅 参考文档. |
10.5. 使用 H2 的 Web 控制台
H2 database 数据库提供了一个 基于浏览器的控制台基于浏览器的控制台,Spring Boot 可以为您自动配置. 当满足以下条件时,控制台将自动配置:
-
您开发的是一个基于 servlet 的 web 应用程序
-
com.h2database:h2
在 classpath 上 -
您使用了 Spring Boot 的开发者工具.
如果您不使用 Spring Boot 的开发者工具,但仍希望使用 H2 的控制台,则可以通过将 spring.h2.console.enabled 属性设置为 true 来实现.
|
H2 控制台仅用于开发期间,因此应注意确保 spring.h2.console.enabled 在生产环境中没有设置为 true .
|
10.6. 使用 jOOQ
Java 面向对象查询 (Java Object Oriented Querying, jOOQ) 是一款广受欢迎的产品,出自 Data Geekery,它可以通过数据库生成 Java 代码,并允许您使用流式 API 来构建类型安全的 SQL 查询. 商业版和开源版都可以与 Spring Boot 一起使用.
10.6.1. 代码生成
要使用 jOOQ 的类型安全查询,您需要从数据库模式生成 Java 类. 您可以按照 jOOQ 用户手册中的说明进行操作. 如果您使用了 jooq-codegen-maven
插件,并且还使用了 spring-boot-starter-parent
父 POM,则可以安全地省略掉插件的 <version>
标签.
您还可以使用 Spring Boot 定义的版本变量 (例如 h2.version
) 来声明插件的数据库依赖. 以下是一个示例:
<plugin>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen-maven</artifactId>
<executions>
...
</executions>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>${h2.version}</version>
</dependency>
</dependencies>
<configuration>
<jdbc>
<driver>org.h2.Driver</driver>
<url>jdbc:h2:~/yourdatabase</url>
</jdbc>
<generator>
...
</generator>
</configuration>
</plugin>
10.6.2. 使用 DSLContext
jOOQ 提供的流式 API 是通过 org.jooq.DSLContext
接口初始化的. Spring Boot 将自动配置一个 DSLContext
作为 Spring Bean,并且将其连接到应用程序的 DataSource
. 要使用 DSLContext
,您只需要 @Autowire
它:
@Component
public class JooqExample implements CommandLineRunner {
private final DSLContext create;
@Autowired
public JooqExample(DSLContext dslContext) {
this.create = dslContext;
}
}
jOOQ 手册建议使用名为 create 的变量来保存 DSLContext .
|
您可以使用 DSLContext
构建查询:
public List<GregorianCalendar> authorsBornAfter1980() {
return this.create.selectFrom(AUTHOR)
.where(AUTHOR.DATE_OF_BIRTH.greaterThan(new GregorianCalendar(1980, 0, 1)))
.fetch(AUTHOR.DATE_OF_BIRTH);
}
10.6.3. jOOQ SQL 方言
除非配置了 spring.jooq.sql-dialect
属性,否则 Spring Boot 会自动判定用于数据源的 SQL 方言. 如果 Spring Boot 无法检测到方言,则使用 DEFAULT
.
Spring Boot 只能自动配置 jOOQ 开源版本支持的方言. |
10.6.4. 自定义 jOOQ
可通过定义自己的 @Bean
来实现更高级的功能,这些自定义将在创建 jOOQ Configuration
时使用. 您可以为以下 jOOQ 类型定义 bean:
-
ConnectionProvider
-
ExecutorProvider
-
TransactionProvider
-
RecordMapperProvider
-
RecordUnmapperProvider
-
Settings
-
RecordListenerProvider
-
ExecuteListenerProvider
-
VisitListenerProvider
-
TransactionListenerProvider
如果要完全控制 jOOQ 配置,您可以创建自己的 org.jooq.Configuration
@Bean
.
11. 使用 NoSQL 技术
Spring Data提供了其他项目来帮助您访问各种NoSQL技术,包括:
Spring Boot为Redis,MongoDB,Neo4j,Elasticsearch,Solr Cassandra,Couchbase和LDAP提供自动配置. 您可以使用其他项目,但必须自己进行配置. 请参阅 spring.io/projects/spring-data 中的相应参考文档.
11.1. Redis
Redis 是一个集缓存、消息代理和键值存储等丰富功能的数据库. Spring Boot 为 Lettuce 和 Jedis 客户端类库提供了基本自动配置, Spring Data Redis 为他们提供了上层抽象.
使用 spring-boot-starter-data-redis
starter 可方便地引入相关依赖. 默认情况下,它使用 Lettuce. 该 starter 可处理传统应用程序和响应式应用程序.
我们还提供了一个 spring-boot-starter-data-redis-reactive starter,以便与其他带有响应式支持的存储保持一致.
|
11.1.1. 连接 Redis
您可以像所有 Spring Bean 一样注入自动配置的 RedisConnectionFactory
、StringRedisTemplate
或普通的 RedisTemplate
实例. 默认情况下,实例将尝试在 localhost:6379
上连接 Redis 服务器,以下是 bean 示例:
@Component
public class MyBean {
private StringRedisTemplate template;
@Autowired
public MyBean(StringRedisTemplate template) {
this.template = template;
}
// ...
}
您还可以注册任意数量个实现了 LettuceClientConfigurationBuilderCustomizer 的 bean,以进行更高级的自定义. 如果你使用 Jedis,则可以使用 JedisClientConfigurationBuilderCustomizer .
|
如果您添加了自己的任何一个自动配置类型的 @Bean
,它将替换默认设置 (除了 RedisTemplate
,由于排除是基于 bean 名称,而 redisTemplate
不是它的类型) . 默认情况下,如果 commons-pool2
在 classpath 上,您将获得一个连接池工厂.
11.2. MongoDB
MongoDB 是一个开源的 NoSQL 文档数据库,其使用了类似 JSON 的模式 (schema) 来替代传统基于表的关系数据. Spring Boot 为 MongoDB 提供了几种便利的使用方式,包括 spring-boot-starter-data-mongodb
和 spring-boot-starter-data-mongodb-reactive
starter.
11.2.1. 连接 MongoDB 数据库
您可以注入一个自动配置的 org.springframework.data.mongodb.MongoDbFactory
来访问 Mongo 数据库. 默认情况下,该实例将尝试在 mongodb://localhost/test
上连接 MongoDB 服务器,以下示例展示了如何连接到 MongoDB 数据库:
import org.springframework.data.mongodb.MongoDbFactory;
import com.mongodb.DB;
@Component
public class MyBean {
private final MongoDbFactory mongo;
@Autowired
public MyBean(MongoDbFactory mongo) {
this.mongo = mongo;
}
// ...
public void example() {
DB db = mongo.getDb();
// ...
}
}
您可以通过设置 spring.data.mongodb.uri
属性来更改 URL 和配置其他设置,如副本集 (replica set) :
spring.data.mongodb.uri=mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test
另外,只要您使用了 Mongo 2.x,请指定 host/port
. 比如,您可能要在 application.properties
中声明以下内容:
spring.data.mongodb.host=mongoserver
spring.data.mongodb.port=27017
如果您已经定义了自己的 MongoClient
,它将被用于自动配置合适的 MongoDbFactory
. 支持 com.mongodb.MongoClient
和 com.mongodb.client.MongoClient
.
自动配置的 MongoClient
是使用 MongoClientSettings
创建的.
要修改其属性,请声明一个或多个 MongoClientSettingsBuilderCustomizer
beans.
每个都将与用于构建 MongoClientSettings
的 MongoClientSettings.Builder
依次调用.
如果您使用 Mongo 3.0 Java 驱动,则不支持 spring.data.mongodb.host 和 spring.data.mongodb.port . 这种情况下,应该使用 spring.data.mongodb.uri 来提供所有配置.
|
如果未指定 spring.data.mongodb.port ,则使用默认值 27017 . 您可以将上述示例中的改行配置删除掉.
|
如果您不使用 Spring Data Mongo,则可以注入 com.mongodb.MongoClient bean 来代替 MongoDbFactory . 如果要完全控制建立 MongoDB 连接,您还可以声明自己的 MongoDbFactory 或者 MongoClient bean.
|
如果您使用的是响应式驱动,则 SSL 需要 Netty. 如果 Netty 可用且 factory 尚未自定义,则自动配置会自动配置此 factory. |
11.2.2. MongoTemplate
Spring Data MongoDB 提供了一个 MongoTemplate
类,它的设计与 Spring 的 JdbcTemplate
非常相似. 与 JdbcTemplate
一样,Spring Boot 会自动配置一个 bean,以便您能注入模板:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final MongoTemplate mongoTemplate;
@Autowired
public MyBean(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
// ...
}
更多详细信息,参照 MongoOperations
Javadoc .
11.2.3. Spring Data MongoDB Repositories
Spring Data 包含了对 MongoDB 资源库 (repository) 的支持. 与之前讨论的 JPA 资源库一样,基本原理是根据方法名称自动构建查询.
事实上,Spring Data JPA 和 Spring Data MongoDB 共享通用的底层代码,因此你可以拿之前提到的 JPA 示例作为基础,假设 City
现在是一个 Mongo 数据类,而不是一个 JPA @Entity
,他们方式工作相同:
package com.example.myapp.domain;
import org.springframework.data.domain.*;
import org.springframework.data.repository.*;
public interface CityRepository extends Repository<City, Long> {
Page<City> findAll(Pageable pageable);
City findByNameAndStateAllIgnoringCase(String name, String state);
}
您可以使用 @EntityScan 注解来自定义文档扫描位置.
|
有关 Spring Data MongoDB 的完整详细内容,包括其丰富的对象关系映射技术,请参考其 参考文档. |
11.2.4. 内嵌 Mongo
Spring Boot 提供了 内嵌 Mongo的自动配置. 要在 Spring Boot 应用程序中使用它,请添加依赖 de.flapdoodle.embed:de.flapdoodle.embed.mongo
.
可以使用 spring.data.mongodb.port
属性来配置 Mongo 的监听端口. 如果想随机分配空闲端口,请把值设置为 0. MongoAutoConfiguration
创建的 MongoClient
将自动配置随机分配的端口.
如果您不配置一个自定义端口,内嵌支持将默认使用一个随机端口 (而不是 27017) . |
如果您的 classpath 上有 SLF4J,Mongo 产生的输出将自动路由到名为 org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo
的 logger.
您可以声明自己的 IMongodConfig
和 IRuntimeConfig
bean 来控制 Mongo 实例的配置和日志路由.
可以通过声明 DownloadConfigBuilderCustomizer
bean来定制下载配置.
11.3. Neo4j
Neo4j 是一个开源的 NoSQL 图形数据库,它使用了一个节点由关系连接的富数据模型,比传统 RDBMS 的方式更适合连接大数据. Spring Boot 为 Neo4j 提供了便捷引入方式,包括 spring-boot-starter-data-neo4j
starter.
11.3.1. 连接 Neo4j 数据库
您可以像任何 Spring Bean 一样注入一个自动配置的 org.neo4j.ogm.session.Session
. 默认情况下, 该实例将尝试使用在 localhost:7687
上使用 Bolt 协议连接到 Neo4j 服务器,以下示例展示了如何注入 一个 Neo4j Session
:
@Component
public class MyBean {
private final Session session;
@Autowired
public MyBean(Session session) {
this.session = session;
}
// ...
}
您可以通过配置 spring.data.neo4j.*
属性来设置 uri 和凭据:
spring.data.neo4j.uri=bolt://my-server:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=secret
您可以通过添加自己的 org.neo4j.ogm.config.Configuration
Bean 来完全控制 session 创建. 此外,添加 SessionFactory
类型的 Bean 会禁用自动配置,因此您可以掌控所有.
11.3.2. 使用内嵌模式
如果您将 org.neo4j:neo4j-ogm-embedded-driver
添加到应用程序的依赖中,Spring Boot 将自动配置一个进程内内嵌的 Neo4j 实例,当您的应用程序关闭时,该实例将不会保留任何数据.
内嵌 Neo4j OGM 驱动本身不提供 Neo4j 您必须自己声明 org.neo4j:neo4j 依赖,请参考 the Neo4j OGM 文档 获取兼容版本列表.
|
您可以通过设置 spring.data.neo4j.embedded.enabled = false
显式禁用嵌入式模式.
如果嵌入式驱动程序和Neo4j内核位于上述类路径上,则 Data Neo4j Tests会自动使用嵌入式Neo4j实例.
您可以通过在配置中提供数据库文件路径来为内嵌模式启用持久化,例如: spring.data.neo4j.uri=file://var/tmp/graph.db .
|
11.3.3. 使用本机类型
Neo4j-OGM可以将某些类型 (例如 java.time.*
中的类型) 映射到基于 String
的属性或Neo4j提供的本机类型之一. 出于向后兼容的原因,Neo4j-OGM的默认设置是使用基于字符串的表示形式. 要使用本机类型,请添加对 org.neo4j:neo4j-ogm-embedded-native-types
或 org.neo4j: neo4j-ogm-embedded-native-types
的依赖,并配置 spring.data.neo4j.use-native-types
属性,如以下示例所示:
spring.data.neo4j.use-native-types=true
11.3.4. Neo4jSession
默认情况下,如果您正在运行 Web 应用程序,会话 (session) 将被绑定到当前请求的整个处理线程 (即 Open Session in View 模式) . 如果不希望此行为,您可以在 application.properties
中添加以下内容:
spring.data.neo4j.open-in-view=false
11.3.5. Spring Data Neo4j 资源库
Spring Data 包括了对 Neo4j 资源库的支持.
Spring Data Neo4j 与 Spring Data JPA 共享相同的通用底层代码,因此您可以直接把之前的 JPA 示例作为基础,假设 City
现在是一个 Neo4j OGM @NodeEntity
,而不是一个 JPA @Entity
,并且资源库抽象以相同的方式工作:
package com.example.myapp.domain;
import java.util.Optional;
import org.springframework.data.neo4j.repository.*;
public interface CityRepository extends Neo4jRepository<City, Long> {
Optional<City> findOneByNameAndState(String name, String state);
}
spring-boot-starter-data-neo4j
starter 支持资源库和事务管理. 您可以在 @Configuration
bean 上分别使用 @EnableNeo4jRepositories
和 @EntityScan
来自定义位置以查找资源库和实体.
有关 Spring Data Neo4j 的完整详细信息,包括其对象映射技术,请参阅 参考文档. . |
11.4. Solr
Apache Solr 是一个搜素引擎. Spring Boot 为 Solr 5 客户端类库提供了基本的自动配置,并且 Spring Data Solr 为其提供给了顶层抽象. 相关的依赖包含在了 spring-boot-starter-data-solr
starter 中.
11.4.1. 连接 Solr
您可以像其他 Spring Bean 一样注入一个自动配置的 SolrClient
实例. 默认情况下,该实例将尝试通过 localhost:8983/solr
连接到服务器,以下示例展示了如何注入一个 Solr bean:
@Component
public class MyBean {
private SolrClient solr;
@Autowired
public MyBean(SolrClient solr) {
this.solr = solr;
}
// ...
}
如果您添加了自己的 SolrClient
类型的 @Bean
,它将替换掉默认配置.
11.4.2. Spring Data Solr 资源库
Spring Data 包含了对 Apache Solr 资源库的支持. 与之前讨论的 JPA 资源库一样,基本原理是根据方法名称自动构造查询.
事实上,Spring Data JPA 和 Spring Data Solr 共享了相同的通用底层代码,因此您可以使用之前的 JPA 示例作为基础,假设 City
现在是一个 @SolrDocument
类,而不是一个 JPA @Entity
,它的工作方式相同.
有关 Spring Data Solr 的完整详细内容,请参考其 参考文档. |
11.5. Elasticsearch
Elasticsearch 是一个开源、分布式、RESTful 的实时搜索分析引擎. Spring Boot 为 Elasticsearch 提供了基本的自动配置.
Spring Boot 支持以下 HTTP 客户端:
Spring Boot 支持以下 HTTP 客户端:
-
官方 Java Low Level (低级) 和 High Level (高级) REST 客户端
-
Spring Data Elasticsearch提供的
ReactiveElasticsearchClient
Spring Data Elasticsearch 依旧使用传输客户端,但是Spring Data Elasticsearch和Elasticsearch本身已弃用了它的支持. 它将在将来的版本中删除. 您可以使用 spring-boot-starter-data-elasticsearch starter 引入使用它.
由于Elasticsearch和Spring Data Elasticsearch为REST客户端提供了官方支持,因此 Jest 客户端也已被弃用.
11.5.1. 使用 REST 客户端连接 Elasticsearch
Elasticsearch 提供了 两个可用于查询集群的 REST 客户端 : Low Level (低级) 和 High Level (高级) .
如果您的 classpath 上存在 org.elasticsearch.client:elasticsearch-rest-client
依赖,则 Spring Boot 将自动配置并注册默认目标为 localhost:9200
的 RestClient
bean. 您可以进一步调整 RestClient
的配置,如下所示:
spring.elasticsearch.rest.uris=https://search.example.com:9200
spring.elasticsearch.rest.read-timeout=10s
spring.elasticsearch.rest.username=user
spring.elasticsearch.rest.password=secret
您还可以注册实现任意数量的 RestClientBuilderCustomizer
bean,以进行更高级的自定义. 要完全控制注册流程,请定义 RestClient bean.
如果你 classpath 上有 org.elasticsearch.client:elasticsearch-rest-high-level-client
依赖,Spring Boot 将自动配置一个 RestHighLevelClient
,它包装了所有现有的 RestClient
bean,重用其 HTTP 配置.
11.5.2. 使用 Reactive REST 客户端连接
Spring Data Elasticsearch 提供了 ReactiveElasticsearchClient
,用于以响应式查询 Elasticsearch
实例. 它基于 WebFlux的WebClient
构建,因此 spring-boot-starter-elasticsearch
和 spring-boot-starter-webflux
依赖.
默认情况下,Spring Boot将自动配置并注册一个针对 localhost:9200
的 ReactiveElasticsearchClient
bean. 您可以进一步调整其配置,如以下示例所示:
spring.data.elasticsearch.client.reactive.endpoints=search.example.com:9200
spring.data.elasticsearch.client.reactive.use-ssl=true
spring.data.elasticsearch.client.reactive.socket-timeout=10s
spring.data.elasticsearch.client.reactive.username=user
spring.data.elasticsearch.client.reactive.password=secret
如果配置属性不够,并且您想完全控制客户端配置,则可以注册自定义 ClientConfiguration
bean.
11.5.3. 使用 Jest 连接 Elasticsearch
现在,Spring Boot支持官方的 RestHighLevelClient
,不再支持 Jest
.
如果您的 classpath 上存在 Jest
,则可以注入一个默认目标为 localhost:9200
. 的自动配置 JestClient
. 您还可以进一步调整客户端配置:
spring.elasticsearch.jest.uris=https://search.example.com:9200
spring.elasticsearch.jest.read-timeout=10000
spring.elasticsearch.jest.username=user
spring.elasticsearch.jest.password=secret
您还可以注册任何数量实现了 HttpClientConfigBuilderCustomizer
的 bean,以进行更加高级的自定义. 以下示例调整了其他 HTTP 设置:
static class HttpSettingsCustomizer implements HttpClientConfigBuilderCustomizer {
@Override
public void customize(HttpClientConfig.Builder builder) {
builder.maxTotalConnection(100).defaultMaxTotalConnectionPerRoute(5);
}
}
要完全控制注册流程,请定义一个 JestClient
bean.
11.5.4. 使用 Spring Data 连接 Elasticsearch
要连接 Elasticsearch,必须定义由Spring Boot自动配置或由应用程序手动提供的 RestHighLevelClient
bean (请参阅前面的部分) . 有了此配置后,可以像其他任何Spring bean一样注入 ElasticsearchRestTemplate
,如以下示例所示:
@Component
public class MyBean {
private final ElasticsearchRestTemplate template;
public MyBean(ElasticsearchRestTemplate template) {
this.template = template;
}
// ...
}
如果存在 spring-data-elasticsearch
和使用 WebClient
所需的依赖 (通常是 spring-boot-starter-webflux
) 的情况下,Spring Boot还可以将 ReactiveElasticsearchClient 和 ReactiveElasticsearchTemplate
自动配置为bean. 它们与其他REST客户端是等效的.
11.5.5. Spring Data Elasticsearch 资源库
Spring Data 包含了对 Elasticsearch 资源库的支持,与之前讨论的 JPA 资源库一样,其原理是根据方法名称自动构造查询.
事实上,Spring Data JPA 与 Spring Data Elasticsearch 共享了相同的通用底层代码,因此您可以使用之前的 JPA 示例作为基础,假设 City
此时是一个 Elasticsearch @Document
类,而不是一个 JPA @Entity
,它以相同的方式工作.
有关 Spring Data Elasticsearch 的完整详细内容,请参阅其 参考文. . |
Spring Boot使用 ElasticsearchRestTemplate
或 ReactiveElasticsearchTemplate
bean支持经典和响应式式 Elasticsearch 资源库. 给定所需的依赖,最有可能由Spring Boot自动配置这些bean.
如果您希望使用自己的模板来支持 Elasticsearch 存储库,则可以添加自己的 ElasticsearchRestTemplate
或 ElasticsearchOperations
@Bean,只要它名为 "elasticsearchTemplate" 即可. 同样适用于 ReactiveElasticsearchTemplate
和 ReactiveElasticsearchOperations
,其bean名称为 "reactiveElasticsearchTemplate".
您可以选择使用以下属性禁用存储库支持:
spring.data.elasticsearch.repositories.enabled=false
11.6. Cassandra
Cassandra 是一个开源的分布式数据库管理系统,旨在处理商用服务器上的大量数据. Spring Boot 为 Cassandra 提供了自动配置,且 Spring Data Cassandra 为其提供了顶层抽象. 相关依赖包含在 spring-boot-starter-data-cassandra starter 中.
11.6.1. 连接 Cassandra
您可以像其他 Spring Bean 一样注入一个自动配置的 CassandraTemplate
或 Cassandra Session
实例. spring.data.cassandra.* 属性可用于自定义连接. 通常,您会提供 keyspace-name
和 contact-points
属性:
spring.data.cassandra.keyspace-name=mykeyspace
spring.data.cassandra.contact-points=cassandrahost1,cassandrahost2
您还可以注册任意数量实现了 ClusterBuilderCustomizer
的 bean,以进行更高级的自定义.
以下代码展示了如何注入一个 Cassandra bean:
@Component
public class MyBean {
private CassandraTemplate template;
public MyBean(CassandraTemplate template) {
this.template = template;
}
// ...
}
如果您添加了自己的类的为 @CassandraTemplate
的 @Bean
,则其将替代默认值.
11.6.2. Spring Data Cassandra 资源库
Spring Data 包含了基本的 Cassandra 资源库支持. 目前,其限制要比之前讨论的 JPA 资源库要多,并且需要在 finder 方法上使用 @Query
注解.
有关 Spring Data Cassandra 的完整详细内容,请参阅其 参考文档. |
11.7. Couchbase
Couchbase 是一个开源、分布式多模型的 NoSQL 面向文档数据库,其针对交互式应用程序做了优化. Spring Boot 为 Couchbase 提供了自动配置, 且 Spring Data Couchbase 为其提供了顶层抽象. 相关的依赖包含在了 spring-boot-starter-data-couchbase starter 中.
11.7.1. 连接 Couchbase
您可以通过添加 Couchbase SDK 和一些配置来轻松获取 Bucket
和 Cluster
. spring.couchbase.*
属性可用于自定义连接. 通常您会配置 bootstrap host、bucket name 和 password:
spring.couchbase.bootstrap-hosts=my-host-1,192.168.1.123
spring.couchbase.bucket.name=my-bucket
spring.couchbase.bucket.password=secret
您至少需要提供 bootstrap host,这种情况下,bucket name 为 default 且 password 为空字符串. 或者,您可以定义自己的 org.springframework.data.couchbase.config.CouchbaseConfigurer @Bean 来控制整个配置.
|
spring.couchbase.env.timeouts.connect=3000
spring.couchbase.env.ssl.key-store=/location/of/keystore.jks
spring.couchbase.env.ssl.key-store-password=secret
查看 spring.couchbase.env.*
获取更多详细内容.
11.7.2. Spring Data Couchbase 资源库
Spring Data 包含了 Couchbase 资源库支持. 有关 Spring Data Couchbase 的完整详细信息,请参阅其 参考文档.
您可以像使用其他 Spring Bean 一样注入自动配置的 CouchbaseTemplate
实例,前提是有一个默认的 CouchbaseConfigurer
(当您启用 Couchbase 支持时会发生这种情况,如之前所述) .
以下示例展示了如何注入一个 Couchbase bean:
@Component
public class MyBean {
private final CouchbaseTemplate template;
@Autowired
public MyBean(CouchbaseTemplate template) {
this.template = template;
}
// ...
}
您可以在自己的配置中定义以下几个 bean,以覆盖自动配置提供的配置:
-
一个名为
couchbaseTemplate
的CouchbaseTemplate
@Bean
-
一个名为
couchbaseIndexManager
的IndexManager
@Bean
-
一个名为
couchbaseCustomConversions
的CustomConversions
@Bean
为了避免在自己的配置中硬编码这些名称,您可以重用 Spring Data Couchbase 提供的 BeanNames
,例如,您可以自定义转换器,如下:
@Configuration(proxyBeanMethods = false)
public class SomeConfiguration {
@Bean(BeanNames.COUCHBASE_CUSTOM_CONVERSIONS)
public CustomConversions myCustomConversions() {
return new CustomConversions(...);
}
// ...
}
如果您想要安全绕开 Spring Data Couchbase 的自动配置,请提供自己的 org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration 实现.
|
11.8. LDAP
LDAP (Lightweight Directory Access Protocol,轻量级目录访问协议) 是一个开放、厂商中立的行业标准应用协议,其通过 IP 网络访问和维护分布式目录信息服务. Spring Boot 为兼容 LDAP 服务器提供了自动配置,以及支持从 UnboundID 内嵌内存式 LDAP 服务器.
Spring Data LDAP 提供了 LDAP 抽象. 相关依赖包含在了 spring-boot-starter-data-ldap
starter 中.
11.8.1. 连接 LDAP 服务器
要连接 LDAP 服务器,请确保您已经声明了 spring-boot-starter-data-ldap
starter 或者 spring-ldap-core
依赖,然后在 application.properties
声明服务器的 URL:
spring.ldap.urls=ldap://myserver:1235
spring.ldap.username=admin
spring.ldap.password=secret
如果需要自定义连接设置,您可以使用 spring.ldap.base
和 spring.ldap.base-environment
属性.
LdapContextSource
将根据这些设置自动配置. 如果您需要自定义它,例如使用一个 PooledContextSource
,则仍然可以注入自动配置的 LdapContextSource
. 确保将自定义的 ContextSource
标记为 @Primary
,以便自动配置的 LdapTemplate
能使用它.
11.8.2. Spring Data LDAP 资源库
Spring Data 包含了 LDAP 资源库支持. 有关 Spring Data LDAP 的完整详细信息,请参阅其 参考文档.
您还可以像其他 Spring Bean 一样注入一个自动配置的 LdapTemplate
实例:
@Component
public class MyBean {
private final LdapTemplate template;
@Autowired
public MyBean(LdapTemplate template) {
this.template = template;
}
// ...
}
11.8.3. 内嵌内存式 LDAP 服务器
为了测试目的,Spring Boot 支持从 UnboundID 自动配置一个内存式 LDAP 服务器. 要配置服务器,请添加 com.unboundid:unboundid-ldapsdk
依赖并声明一个 spring.ldap.embedded.base-dn
属性:
spring.ldap.embedded.base-dn=dc=spring,dc=io
可以定义多个 base-dn 值,但是,由于名称包含逗号,存在歧义,因此必须使用正确的符号来定义它们. 在 yaml 文件中,您可以使用 yaml 列表表示法:
在属性文件中,必须使用索引方式:
|
默认情况下,服务器将在一个随机端口上启动,并触发常规的 LDAP 支持 (不需要指定 spring.ldap.urls
属性) .
如果您的 classpath 上存在一个 schema.ldif
文件,其将用于初始化服务器. 如果您想从不同的资源中加载脚本,可以使用 spring.ldap.embedded.ldif
属性.
默认情况下,将使用一个标准模式 (schema) 来校验 LDIF
文件. 您可以使用 spring.ldap.embedded.validation.enabled
属性来关闭所有校验. 如果您有自定义的属性,则可以使用 spring.ldap.embedded.validation.schema
来定义自定义属性类型或者对象类.
11.9. InfluxDB
InfluxDB 是一个开源时列数据库,其针对运营监控、应用程序指标、物联网传感器数据和实时分析等领域中的时间序列数据在速度、高可用存储和检索方面进行了优化.
11.9.1. 连接 InfluxDB
Spring Boot 自动配置 InfluxDB
实例,前提是 Influxdb-java
客户端在 classpath 上并且设置了数据库的 URL,如下所示:
spring.influx.url=https://172.0.0.1:8086
如果与 InfluxDB 的连接需要用户和密码,则可以相应地设置 spring.influx.user
和 spring.influx.password
属性.
InfluxDB
依赖于 OkHttp. 如果你需要调整 InfluxDB 在底层使用的 http 客户端,则可以注册一个 InfluxDbOkHttpClientBuilderProvider
bean.
12. 缓存
Spring Framework 支持以透明的方式向应用程序添加缓存. 从本质上讲,将缓存应用于方法上,根据缓存数据减少方法的执行次数. 缓存逻辑是透明的,不会对调用者造成任何干扰. 通过 @EnableCaching
注解启用缓存支持,Spring Boot 就会自动配置缓存设置.
有关更多详细信息,请查看 Spring Framework 参考文档的 相关部分 . |
简而言之,为服务添加缓存的操作就像在其方法中添加注解一样简单,如下所示:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
public class MathService {
@Cacheable("piDecimals")
public int computePiDecimal(int i) {
// ...
}
}
此示例展示了如何在代价可能高昂的操作上使用缓存. 在调用 computePiDecimal
之前,缓存支持会在 piDecimals
缓存中查找与 i
参数匹配的项. 如果找到,则缓存中的内容会立即返回给调用者,并不会调用该方法. 否则,将调用该方法,并在返回值之前更新缓存.
您还可以使用标准 JSR-107 (JCache) 注解 (例如 @CacheResult ) . 但是,我们强烈建议您不要将 Spring Cache 和 JCache 注解混合使用.
|
如果您不添加任何指定的缓存库,Spring Boot 会自动配置一个使用并发 map 的 simple provider . 当需要缓存时 (例如前面示例中的 piDecimals
) ,该 simple provider 会为您创建缓存. 不推荐将 simple provider 用于生产环境,但它非常适合入门并帮助您了解这些功能.
当您决定使用缓存提供者时,请务必阅读其文档以了解如何配置应用程序. 几乎所有提供者都要求您显式配置应用程序中使用的每个缓存. 有些提供了自定义 spring.cache.cache-names
属性以定义默认缓存.
12.1. 支持的缓存提供者
缓存抽象不提供存储实现,其依赖于 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口实现的抽象.
如果您未定义 CacheManager
类型的 bean 或名为 cacheResolver
的 CacheResolver
(请参阅 CachingConfigurer
) ,则 Spring Boot 会尝试检测以下提供者 (按序号顺序) :
-
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, and others)
也可以通过设置 spring.cache.type 属性来强制指定缓存提供者. 如果您需要在某些环境 (比如测试) 中完全禁用缓存,请使用此属性.
|
使用 spring-boot-starter-cache starter 快速添加基本的缓存依赖. starter 引入了 spring-context-support . 如果手动添加依赖,则必须包含 spring-context-support 才能使用 JCache、EhCache 2.x 或 Guava 支持.
|
如果通过 Spring Boot 自动配置 CacheManager
,则可以通过暴露一个实现了 CacheManagerCustomizer
接口的 bean,在完全初始化之前进一步调整其配置. 以下示例设置了一个 flag,表示应将 null
值传递给底层 map:
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setAllowNullValues(false);
}
};
}
在前面示例中,需要一个自动配置的 ConcurrentMapCacheManager . 如果不是这种情况 (您提供了自己的配置或自动配置了不同的缓存提供者) ,则根本不会调用 customizer. 您可以拥有多个 customizer,也可以使用 @Order 或 Ordered 来排序它们.
|
12.1.1. Generic
如果上下文定义了至少一个 org.springframework.cache.Cache
bean,则使用 Generic 缓存. 将创建一个包装所有该类型 bean 的 CacheManager
.
12.1.2. JCache (JSR-107)
JCache 通过 classpath 上的 javax.cache.spi.CachingProvider
(即 classpath 上存在符合 JSR-107 的缓存库) 来引导,jCacheCacheManager
由 spring-boot-starter-cache
starter 提供. 您可以使用各种兼容库,Spring Boot 为 Ehcache 3、Hazelcast 和 Infinispan 提供依赖管理. 您还可以添加任何其他兼容库.
可能存在多个提供者,在这种情况下必须明确指定提供者. 即使 JSR-107 标准没有强制规定一个定义配置文件位置的标准化方法,Spring Boot 也会尽其所能设置一个包含实现细节的缓存,如下所示:
# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.acme.MyCachingProvider
spring.cache.jcache.config=classpath:acme.xml
当缓存库同时提供原生实现和 JSR-107 支持时,Spring Boot 更倾向 JSR-107 支持,因此当您切换到不同的 JSR-107 实现时,还可以使用相同的功能. |
Spring Boot 对 Hazelcast 的支持一般. 如果有一个 HazelcastInstance 可用,它也会自动为 CacheManager 复用,除非指定了 spring.cache.jcache.config 属性.
|
有两种方法可以自定义底层的 javax.cache.cacheManager
:
-
可以通过设置
spring.cache.cache-names
属性在启动时创建缓存. 如果定义了自定义javax.cache.configuration.Configuration
bean,则会使用它来自定义. -
使用
CacheManager
的引用调用org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer
bean 以进行完全自定义.
如果定义了一个标准的 javax.cache.CacheManager bean,它将自动包装进一个抽象所需的 org.springframework.cache.CacheManager 实现中,而不会应用自定义配置.
|
12.1.3. EhCache 2.x
如果可以在 classpath 的根目录中找到名为 ehcache.xml 的文件,则使用 EhCache 2.x. 如果找到 EhCache 2.x,则使用 spring-boot-starter-cache
starter 提供的 EhCacheCacheManager
来启动缓存管理器. 还可以提供其他配置文件,如下所示:
spring.cache.ehcache.config=classpath:config/another-config.xml
12.1.4. Hazelcast
Spring Boot 对 Hazelcast 的支持一般. 如果自动配置了一个 HazelcastInstance
,它将自动包装进 CacheManager
中.
12.1.5. Infinispan
Infinispan 没有默认的配置文件位置,因此必须明确指定. 否则将使用默认配置引导.
spring.cache.infinispan.config=infinispan.xml
可以通过设置 spring.cache.cache-names
属性在启动时创建缓存. 如果定义了自定义 ConfigurationBuilder
bean,则它将用于自定义缓存.
Infinispan 在 Spring Boot 中的支持仅限于内嵌模式,非常简单. 如果你想要更多选项,你应该使用官方的 Infinispan Spring Boot starter. 有关更多详细信息,请参阅 Infinispan 文档 . |
12.1.6. Couchbase
如果 Couchbase Java 客户端和 couchbase-spring-cache
实现可用且已经配置了 Couchbase
,则应用程序会自动配置一个 CouchbaseCacheManager
. 也可以通过设置 spring.cache.cache-names
属性在启动时创建其他缓存. 这些缓存在自动配置的 Bucket
上操作.
您还可以使用 customizer 在其他 Bucket
上创建缓存. 假设您需要在 main Bucket
上有两个缓存 (cache1
和 cache2
) ,并且 “another” Bucket
有一个过期时间为 2 秒的缓存 (cache3
) . 您可以通过配置创建这两个缓存,如下所示:
spring.cache.cache-names=cache1,cache2
然后,您可以定义一个 @Configuration
类来配置其他 Bucket
和 cache3
缓存,如下所示:
@Configuration(proxyBeanMethods = false)
public class CouchbaseCacheConfiguration {
private final Cluster cluster;
public CouchbaseCacheConfiguration(Cluster cluster) {
this.cluster = cluster;
}
@Bean
public Bucket anotherBucket() {
return this.cluster.openBucket("another", "secret");
}
@Bean
public CacheManagerCustomizer<CouchbaseCacheManager> cacheManagerCustomizer() {
return c -> {
c.prepareCache("cache3", CacheBuilder.newInstance(anotherBucket())
.withExpiration(2));
};
}
}
此示例配置复用了通过自动配置创建的 Cluster
.
12.1.7. Redis
如果 Redis 可用并已经配置,则应用程序会自动配置一个 RedisCacheManager
. 通过设置 spring.cache.cache-names
属性可以在启动时创建其他缓存,并且可以使用 spring.cache.redis.*
属性配置缓存默认值. 例如,以下配置创建 cache1
和 cache2
缓存,他们的有效时间为 10 分钟:
spring.cache.cache-names=cache1,cache2
spring.cache.redis.time-to-live=600000
默认情况下,会添加一个 key 前缀,这样做是因为如果两个单独的缓存使用了相同的键,Redis 不支持重叠 key,而缓存也不能返回无效值. 如果您创建自己的 RedisCacheManager ,我们强烈建议您启用此设置.
|
您可以通过添加自己的 RedisCacheConfiguration @Bean 来完全控制配置. 如果您想自定义序列化策略,这种方式可能很有用.
|
如果您需要控制更多的配置,请考虑注册 RedisCacheManagerBuilderCustomizer
bean.
以下示例显示了一个自定义的配置,该定制器配置了 cache1
和 cache2
的特定生存时间
The following example shows a customizer that configures a specific time to live for cache1
and cache2
:
@Bean
public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
return (builder) -> builder
.withCacheConfiguration("cache1",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(10)))
.withCacheConfiguration("cache2",
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)));
}
12.1.8. Caffeine
Caffeine 是一个使用了 Java 8 重写 Guava 缓存,用于取代 Guava 支持的缓存库. 如果 Caffeine 存在,则应用程序会自动配置一个 CaffeineCacheManager
(由 spring-boot-starter-cache
starter 提供) .
可以通过设置 spring.cache.cache-names
属性在启动时创建缓存,并且可以通过以下方式之一 (按序号顺序) 自定义缓存:
-
一个由
spring.cache.caffeine.spec
定义的缓存规范 -
一个已定义的
com.github.benmanes.caffeine.cache.CaffeineSpec
bean -
一个已定义的
com.github.benmanes.caffeine.cache.Caffeine
bean
例如,以下配置创建 cache1
和 cache2
缓存,最大大小为 500,有效时间 为 10 分钟:
spring.cache.cache-names=cache1,cache2
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
如果定义了 com.github.benmanes.caffeine.cache.CacheLoader
bean,它将自动与 CaffeineCacheManager
关联. 由于 CacheLoader
将与缓存管理器管理的所有缓存相关联,因此必须将其定义为 CacheLoader<Object, Object>
. 自动配置会忽略所有其他泛型类型.
12.1.9. Simple
如果找不到其他提供者,则配置使用一个 ConcurrentHashMap
作为缓存存储的简单实现. 如果您的应用程序中没有缓存库,则该项为默认值. 默认情况下,会根据需要创建缓存,但您可以通过设置 cache-names
属性来限制可用缓存的列表. 例如,如果只需要 cache1
和 cache2
缓存,请按如下设置 cache-names
属性:
spring.cache.cache-names=cache1,cache2
如果这样做了,并且您的应用程序使用了未列出的缓存,则运行时在它需要缓存时会触发失败,但在启动时则不会. 这类似于真实缓存提供者在使用未声明的缓存时触发的行为方式.
13. 消息传递
Spring Framework 为消息传递系统集成提供了广泛的支持,从使用 JmsTemplate
简化 JMS API 的使用到异步接收消息的完整基础设施. Spring AMQP 为高级消息队列协议 (Advanced Message Queuing Protocol,AMQP) 提供了类似的功能集合. Spring Boot 还为 RabbitTemplate
和 RabbitMQ 提供自动配置选项. Spring WebSocket 本身包含了对 STOMP 消息传递的支持,Spring Boot 通过 starter 和少量自动配置即可支持它. Spring Boot 同样支持 Apache Kafka.
13.1. JMS
javax.jms.ConnectionFactory
接口提供了一种创建 javax.jms.Connection
的标准方法,可与 JMS broker (代理) 进行交互. 虽然 Spring 需要一个 ConnectionFactory
来与 JMS 一同工作,但是您通常不需要自己直接使用它,而是可以依赖更高级别的消息传递抽象. (有关详细信息,请参阅 Spring Framework 参考文档的相关部分. ) Spring Boot 还会自动配置发送和接收消息所需的基础设施.
13.1.1. ActiveMQ 支持
当 ActiveMQ 在 classpath 上可用时,Spring Boot 也可以配置一个 ConnectionFactory
. 如果 broker 存在,则会自动启动并配置一个内嵌式 broker (前提是未通过配置指定 broder URL) .
如果使用 spring-boot-starter-activemq ,则提供了连接到 ActiveMQ 实例必须依赖或内嵌一个 ActiveMQ 实例,以及与 JMS 集成的 Spring 基础设施.
|
ActiveMQ 配置由 spring.activemq.*
中的外部配置属性控制. 例如,您可以在 application.properties
中声明以下部分:
spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret
默认情况下,CachingConnectionFactory
将原生的 ConnectionFactory
使用可由 spring.jms.*
中的外部配置属性控制的合理设置包装起来:
spring.jms.cache.session-cache-size=5
如果您更愿意使用原生池,则可以通过向 org.messaginghub:pooled-jms
添加一个依赖并相应地配置 JmsPoolConnectionFactory
来实现,如下所示:
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50
有关更多支持的选项,请参阅 ActiveMQProperties . 您还可以注册多个实现了 ActiveMQConnectionFactoryCustomizer 的的 bean,以进行更高级的自定义.
|
默认情况下,ActiveMQ 会创建一个 destination (目标) (如果它尚不存在) ,以便根据提供的名称解析 destination.
13.1.2. Artemis 支持
Spring Boot 可以在检测到 Artemis 在 classpath 上可用时自动配置一个 ConnectionFactory
. 如果存在 broker,则会自动启动并配置一个内嵌 broker (除非已明确设置 mode 属性) .
支持的 mode 为 embedded
(明确表示需要一个内嵌 broker,如果 broker 在 classpath 上不可用则发生错误) 和 native
(使用 netty
传输协议连接到 broker) . 配置后者后,Spring Boot 会使用默认设置配置一个 ConnectionFactory
,该 ConnectionFactory
连接到在本地计算机上运行的 broker.
如果使用了 spring-boot-starter-artemis ,则会提供连接到现有的 Artemis 实例的必须依赖,以及与 JMS 集成的Spring 基础设施. 将 org.apache.activemq:artemis-jms-server 添加到您的应用程序可让您使用内嵌模式.
|
Artemis 配置由 spring.artemis.*
中的外部配置属性控制. 例如,您可以在 application.properties
中声明以下部分:
spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret
内嵌 broker 时,您可以选择是否要启用持久化并列出应该可用的 destination. 可以将这些指定为以逗号分隔的列表,以使用默认选项创建它们,也可以定义类型为 org.apache.activemq.artemis.jms.server.config.JMSQueueConfiguration
或 org.apache.activemq.artemis.jms.server.config.TopicConfiguration
的 bean,分别用于高级队列和 topic (主题) 配置.
默认情况下,CachingConnectionFactory
将原生的 ConnectionFactory
使用可由 spring.jms.*
中的外部配置属性控制的合理设置包装起来:
spring.jms.cache.session-cache-size=5
如果您更愿意使用原生池,则可以通过向 org.messaginghub:pooled-jms
添加一个依赖并相应地配置 JmsPoolConnectionFactory
来实现,如下所示:
spring.artemis.pool.enabled=true
spring.artemis.pool.max-connections=50
有关更多支持的选项,请参阅 ArtemisProperties
.
不涉及 JNDI 查找,使用 Artemis 配置中的 name
属性或通过配置提供的名称来解析目标 (destination) 名称.
13.1.3. 使用 JNDI ConnectionFactory
如果您在应用程序服务器中运行应用程序,Spring Boot 会尝试使用 JNDI 找到 JMS ConnectionFactory
. 默认情况下,将检查 java:/JmsXA
和 java:/XAConnectionFactory
这两个位置. 如果需要指定其他位置,可以使用 spring.jms.jndi-name
属性,如下所示:
spring.jms.jndi-name=java:/MyConnectionFactory
13.1.4. 发送消息
Spring 的 JmsTemplate
是自动配置的,你可以直接将它注入到你自己的 bean 中,如下所示:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final JmsTemplate jmsTemplate;
@Autowired
public MyBean(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
// ...
}
JmsMessagingTemplate 可以以类似的方式注入. 如果定义了 DestinationResolver 或 MessageConverter bean,它将自动关联到自动配置的 JmsTemplate .
|
13.1.5. 接收消息
当存在 JMS 基础设施时,可以使用 @JmsListener
对任何 bean 进行注解以创建监听器 (listener) 端点. 如果未定义 JmsListenerContainerFactory
,则会自动配置一个默认的 (factory) . 如果定义了 DestinationResolver
或 MessageConverter
bean,它将自动关联到默认的 factory.
默认情况下,默认 factory 是具有事务特性的. 如果您在存在有 JtaTransactionManager
的基础设施中运行,则默认情况下它与监听器容器相关联. 如果不是,则 sessionTransacted
flag 将为启用 (enabled) . 在后一种情况下,您可以通过在监听器方法 (或其委托) 上添加 @Transactional
,将本地数据存储事务与传入消息的处理相关联. 这确保了在本地事务完成后传入消息能被告知. 这还包括了发送已在同一 JMS 会话上执行的响应消息.
以下组件在 someQueue
destination 上创建一个监听器端点:
@Component
public class MyBean {
@JmsListener(destination = "someQueue")
public void processMessage(String content) {
// ...
}
}
有关更多详细信息,请参阅 the Javadoc of @EnableJms 的 Javadoc.
|
如果需要创建更多 JmsListenerContainerFactory
实例或覆盖默认值,Spring Boot 会提供一个 DefaultJmsListenerContainerFactoryConfigurer
,您可以使用它来初始化 DefaultJmsListenerContainerFactory
,其设置与自动配置的 factory 设置相同.
例如,以下示例暴露了另一个使用特定 MessageConverter
的 factory:
@Configuration(proxyBeanMethods = false)
static class JmsConfiguration {
@Bean
public DefaultJmsListenerContainerFactory myFactory(
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory());
factory.setMessageConverter(myMessageConverter());
return factory;
}
}
然后,您可以在任何 @JmsListener
注解的方法中使用该 factory,如下所示:
@Component
public class MyBean {
@JmsListener(destination = "someQueue", containerFactory="myFactory")
public void processMessage(String content) {
// ...
}
}
13.2. AMQP
高级消息队列协议 (Advanced Message Queuing Protocol,AMQP) 是一个平台无关,面向消息中间件的连接级协议. Spring AMQP 项目将核心 Spring 概念应用于基于 AMQP 消息传递解决方案的开发. Spring Boot 为通过 RabbitMQ 使用 AMQP 提供了一些快捷方法,包括 spring-boot-starter-amqp
starter.
13.2.1. RabbitMQ 支持
RabbitMQ 是一个基于 AMQP 协议的轻量级、可靠、可扩展且可移植的消息代理. Spring 使用 RabbitMQ 通过 AMQP 协议进行通信.
RabbitMQ 配置由 spring.rabbitmq.*
中的外部配置属性控制. 例如,您可以在 application.properties
中声明以下部分:
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret
另外,您可以配置相同 addresses
属性的连接:
spring.rabbitmq.addresses=amqp://admin:secret@localhost
如果上下文中存在 ConnectionNameStrategy
bean,它将自动用于命名由自动配置的 ConnectionFactory
所创建的连接. 有关更多支持的选项,请参阅 RabbitProperties
.
有关详细信息,请参阅理解 AMQP、RabbitMQ 使用的协议 f. |
13.2.2. 发送消息
Spring 的 AmqpTemplate
和 AmqpAdmin
是自动配置的,您可以将它们直接注入自己的 bean 中,如下所示:
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyBean {
private final AmqpAdmin amqpAdmin;
private final AmqpTemplate amqpTemplate;
@Autowired
public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
this.amqpAdmin = amqpAdmin;
this.amqpTemplate = amqpTemplate;
}
// ...
}
RabbitMessagingTemplate 可以以类似的方式注入. 如果定义了 MessageConverter bean,它将自动关联到自动配置的 AmqpTemplate .
|
如有必要,所有定义为 bean 的 org.springframework.amqp.core.Queue
都会自动在 RabbitMQ 实例上声明相应的队列.
要重试操作,可以在 AmqpTemplate
上启用重试 (例如,在 broker 连接丢失的情况下) :
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=2s
默认情况下禁用重试. 您还可以通过声明 RabbitRetryTemplateCustomizer
bean 以编程方式自定义 RetryTemplate
.
13.2.3. 接收消息
当 Rabbit 基础设施存在时,可以使用 @RabbitListener
注解任何 bean 以创建监听器端点. 如果未定义 RabbitListenerContainerFactory
,则会自动配置一个默认的 SimpleRabbitListenerContainerFactory
,您可以使用 spring.rabbitmq.listener.type
属性切换到一个直接容器. 如果定义了 MessageConverter
或 MessageRecoverer
bean,它将自动与默认 factory 关联.
以下示例组件在 someQueue
队列上创建一个监听监听器端点:
@Component
public class MyBean {
@RabbitListener(queues = "someQueue")
public void processMessage(String content) {
// ...
}
}
有关更多详细信息,请参阅 the Javadoc of @EnableRabbit 的 Javadoc.
|
如果需要创建更多 RabbitListenerContainerFactory
实例或覆盖默认值,Spring Boot 提供了一个 SimpleRabbitListenerContainerFactoryConfigurer
和一个 DirectRabbitListenerContainerFactoryConfigurer
,您可以使用它来初始化 SimpleRabbitListenerContainerFactory
和 DirectRabbitListenerContainerFactory
,其设置与使用自动配置的 factory 相同.
这两个 bean 与您选择的容器类型没有关系,它们通过自动配置暴露. |
例如,以下配置类暴露了另一个使用特定 MessageConverter
的 factory:
@Configuration(proxyBeanMethods = false)
static class RabbitConfiguration {
@Bean
public SimpleRabbitListenerContainerFactory myFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory =
new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setMessageConverter(myMessageConverter());
return factory;
}
}
然后,您可以在任何 @RabbitListener
注解的方法中使用该 factory,如下所示:
@Component
public class MyBean {
@RabbitListener(queues = "someQueue", containerFactory="myFactory")
public void processMessage(String content) {
// ...
}
}
您可以启用重试机制来处理监听器的异常抛出情况. 默认情况下使用 RejectAndDontRequeueRecoverer
,但您可以定义自己的 MessageRecoverer
. 如果 broker 配置了重试机制,当重试次数耗尽时,则拒绝消息并将其丢弃或路由到死信 (dead-letter) exchange 中.
默认情况下重试机制为禁用. 您还可以通过声明 RabbitRetryTemplateCustomizer
bean 以编程方式自定义 RetryTemplate
.
默认情况下,如果禁用重试并且监听器异常抛出,则会无限期地重试传递. 您可以通过两种方式修改此行为: 将 defaultRequeueRejected 属性设置为 false ,以便尝试零重传或抛出 AmqpRejectAndDontRequeueException 以指示拒绝该消息. 后者是启用重试并且达到最大传递尝试次数时使用的机制.
|
13.3. Apache Kafka 支持
通过提供 spring-kafka
项目的自动配置来支持 Apache Kafka
Kafka 配置由 spring.kafka.*
中的外部配置属性控制. 例如,您可以在 application.properties
中声明以下部分:
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup
要在启动时创建主题 (topic) ,请添加 NewTopic 类型的 Bean. 如果主题已存在,则忽略该 bean.
|
有关更多支持的选项,请参阅 KafkaProperties
.
13.3.1. 发送消息
Spring 的 KafkaTemplate
是自动配置的,您可以直接在自己的 bean 中装配它,如下所示:
@Component
public class MyBean {
private final KafkaTemplate kafkaTemplate;
@Autowired
public MyBean(KafkaTemplate kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
// ...
}
如果定义了属性 spring.kafka.producer.transaction-id-prefix ,则会自动配置一个 KafkaTransactionManager . 此外,如果定义了 RecordMessageConverter bean,它将自动关联到自动配置的 KafkaTemplate .
|
13.3.2. 接收消息
当存在 Apache Kafka 基础设施时,可以使用 @KafkaListener
注解任何 bean 以创监听器端点. 如果未定义 KafkaListenerContainerFactory
,则会使用 spring.kafka.listener.*
中定义的 key 自动配置一个默认的 factory.
以下组件在 someTopic
topic 上创建一个监听器端点:
@Component
public class MyBean {
@KafkaListener(topics = "someTopic")
public void processMessage(String content) {
// ...
}
}
如果定义了 KafkaTransactionManager
bean,它将自动关联到容器 factory. 同样,如果定义了 RecordMessageConverter
、ErrorHandler
或 AfterRollbackProcessor
bean,它将自动关联到默认的 factory.
根据监听器类型,将 RecordMessageConverter
或 BatchMessageConverter
bean与默认工厂关联. 如果对于批处理监听器仅存在一个 RecordMessageConverter
bean,则将其包装在 BatchMessageConverter
中.
自定义 ChainedKafkaTransactionManager 必须标记为 @Primary ,因为它通常引用自动配置的 KafkaTransactionManager bean.
|
13.3.3. Kafka Streams
Spring for Apache Kafka 提供了一个工厂 bean 来创建 StreamsBuilder
对象并管理其 stream (流) 的生命周期. 只要 kafka-streams
在 classpath 上并且通过 @EnableKafkaStreams
注解启用了 Kafka Stream,Spring Boot 就会自动配置所需的 KafkaStreamsConfiguration
bean.
启用 Kafka Stream 意味着必须设置应用程序 id 和引导服务器 (bootstrap server) . 可以使用 spring.kafka.streams.application-id
配置前者,如果未设置则默认为 spring.application.name
. 后者可以全局设置或专门为 stream 而重写.
使用专用 properties 可以设置多个其他属性,可以使用 spring.kafka.streams.properties
命名空间设置其他任意 Kafka 属性. 有关更多信息,另请参见 Kafka 属性 .
要使用 factory bean,只需将 StreamsBuilder
装配到您的 @Bean
中,如下所示:
@Configuration(proxyBeanMethods = false)
@EnableKafkaStreams
public static class KafkaStreamsExampleConfiguration {
@Bean
public KStream<Integer, String> kStream(StreamsBuilder streamsBuilder) {
KStream<Integer, String> stream = streamsBuilder.stream("ks1In");
stream.map((k, v) -> new KeyValue<>(k, v.toUpperCase())).to("ks1Out",
Produced.with(Serdes.Integer(), new JsonSerde<>()));
return stream;
}
}
默认情况下,由其创建的 StreamBuilder
对象管理的流会自动启动. 您可以使用 spring.kafka.streams.auto-startup
属性自定义此行为.
13.3.4. 其他 Kafka 属性
自动配置支持的属性可在常见应用程序属性常见应用程序属性中找到. 请注意,在大多数情况下,这些属性 (连接符或驼峰命名) 直接映射到 Apache Kafka 点连形式属性. 有关详细信息,请参阅 Apache Kafka 文档.
这些属性中的前几个适用于所有组件 (生产者 [producer] 、使用者 [consumer] 、管理者 [admin] 和流 [stream] ) ,但如果您希望使用不同的值,则可以在组件级别指定. Apache Kafka 重要性 (优先级) 属性设定为 HIGH、MEDIUM 或 LOW. Spring Boot 自动配置支持所有 HIGH 重要性属性,一些选择的 MEDIUM 和 LOW 属性,以及所有没有默认值的属性.
只有 Kafka 支持的属性的子集可以直接通过 KafkaProperties
类获得. 如果您希望使用不受支持的其他属性配置生产者或消费者,请使用以下属性:
spring.kafka.properties.prop.one=first
spring.kafka.admin.properties.prop.two=second
spring.kafka.consumer.properties.prop.three=third
spring.kafka.producer.properties.prop.four=fourth
spring.kafka.streams.properties.prop.five=fifth
这将常见的 prop.one
Kafka 属性设置为 first
(适用于生产者、消费者和管理者) ,prop.two
管理者属性为 second
,prop.three
消费者属性为 third
,prop.four
生产者属性为 fourth
,prop.five
流属性为 fifth
.
您还可以按如下方式配置 Spring Kafka JsonDeserializer
:
spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.properties.spring.json.value.default.type=com.example.Invoice
spring.kafka.consumer.properties.spring.json.trusted.packages=com.example,org.acme
同样,您可以禁用 JsonSerializer
在 header 中发送类型信息的默认行为:
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.properties.spring.json.add.type.headers=false
以这种方式设置的属性将覆盖 Spring Boot 明确支持的任何配置项. |
13.3.5. 使用嵌入式 Kafka 进行测试
Spring 为 Apache Kafka 提供了一种使用嵌入式Apache Kafka代理测试项目的便捷方法. 要使用此功能,请在 spring-kafka-test
模块中使用 @EmbeddedKafka
注解测试类. 有关更多信息,请参阅Spring for Apache Kafka 参考手册.
要使Spring Boot自动配置与上述嵌入式Apache Kafka代理一起使用,您需要将嵌入式代理地址 (由 EmbeddedKafkaBroker
填充) 的系统属性重新映射到Apache Kafka的Spring Boot配置属性中. 有几种方法可以做到这一点:
-
提供一个系统属性,以将嵌入式代理地址映射到测试类中的
spring.kafka.bootstrap-servers
中:
static {
System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY, "spring.kafka.bootstrap-servers");
}
-
在
@EmbeddedKafka
注解上配置属性名称:
@EmbeddedKafka(topics = "someTopic",
bootstrapServersProperty = "spring.kafka.bootstrap-servers")
-
在配置属性中使用占位符:
spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}
14. 使用 RestTemplate
调用 REST 服务
如果您的应用程序需要调用远程 REST 服务,这可以使用 Spring Framework 的 RestTemplate 类. 由于 RestTemplate
实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何自动配置的 RestTemplate
bean. 但是,
它会自动配置 RestTemplateBuilder
,可在需要时创建 RestTemplate
实例. 自动配置的 RestTemplateBuilder
确保将合适的 HttpMessageConverters
应用于 RestTemplate
实例.
以下代码展示了一个典型示例:
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}
}
RestTemplateBuilder 包含许多可用于快速配置 RestTemplate 的方法. 例如,要添加 BASIC auth 支持,可以使用 builder.basicAuthentication("user", "password").build() .
|
14.1. 自定义 RestTemplate
RestTemplate
自定义有三种主要方法,具体取决于您希望自定义的程度.
要想自定义的作用域尽可能地窄,请注入自动配置的 RestTemplateBuilder
,然后根据需要调用其方法. 每个方法调用都返回一个新的 RestTemplateBuilder
实例,因此自定义只会影响当前构建器.
要在应用程序作用域内添加自定义配置,请使用 RestTemplateCustomizer
bean. 所有这些 bean 都会自动注册到自动配置的 RestTemplateBuilder
,并应用于使用它构建的所有模板.
以下示例展示了一个 customizer,它为除 192.168.0.5
之外的所有主机配置代理:
static class ProxyCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
HttpHost proxy = new HttpHost("proxy.example.com");
HttpClient httpClient = HttpClientBuilder.create().setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
@Override
public HttpHost determineProxy(HttpHost target, HttpRequest request, HttpContext context)
throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, request, context);
}
}).build();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
}
最后,最极端 (也很少使用) 的选择是创建自己的 RestTemplateBuilder
bean. 这样做会关闭 RestTemplateBuilder
的自动配置,并阻止使用任何 RestTemplateCustomizer
bean.
15. 使用 WebClient
调用 REST 服务
如果在 classpath 上存在 Spring WebFlux,则还可以选择使用 WebClient
来调用远程 REST 服务. 与 RestTemplate
相比,该客户端更具函数式风格并且完全响应式. 您可以在 Spring Framework 文档的相关部分中了解有关 WebClient
的更多信息.
Spring Boot 为您创建并预配置了一个 WebClient.Builder
. 强烈建议将其注入您的组件中并使用它来创建 WebClient
实例. Spring Boot 配置该构建器以共享 HTTP 资源,以与服务器相同的方式反射编解码器设置 (请参阅 WebFlux HTTP 编解码器自动配置) 等.
以下代码是一个典型示例:
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://example.org").build();
}
public Mono<Details> someRestCall(String name) {
return this.webClient.get().uri("/{name}/details", name)
.retrieve().bodyToMono(Details.class);
}
}
15.1. WebClient 运行时
Spring Boot 将自动检测用于驱动 WebClient
的 ClientHttpConnector
,具体取决于应用程序 classpath 上可用的类库. 目前支持 Reactor Netty 和 Jetty RS 客户端.
默认情况下 spring-boot-starter-webflux
starter 依赖于 io.projectreactor.netty:reactor-netty
,它包含了服务器和客户端的实现. 如果您选择将 Jetty 用作响应式服务器,则应添加 Jetty Reactive HTTP 客户端库依赖 org.eclipse.jetty:jetty-reactive-httpclient
. 服务器和客户端使用相同的技术具有一定优势,因为它会自动在客户端和服务器之间共享 HTTP 资源.
开发人员可以通过提供自定义的 ReactorResourceFactory
或 JettyResourceFactory
bean 来覆盖 Jetty 和 Reactor Netty 的资源配置 —— 这将同时应用于客户端和服务器.
如果您只希望覆盖客户端选项,则可以定义自己的 ClientHttpConnector
bean 并完全控制客户端配置.
您可以在 Spring Framework 参考文档中了解有关 WebClient
配置选项的更多信息.
15.2. 自定义 WebClient
WebClient
自定义有三种主要方法,具体取决于您希望自定义的程度.
要想自定义的作用域尽可能地窄,请注入自动配置的 WebClient.Builder
,然后根据需要调用其方法. WebClient.Builder
实例是有状态的: 构建器上的任何更改都会影响到之后所有使用它创建的客户端. 如果要使用相同的构建器创建多个客户端,可以考虑使用 WebClient.Builder other = builder.clone()
; 的方式克隆构建器.
要在应用程序作用域内对所有 WebClient.Builder
实例添加自定义,可以声明 WebClientCustomizer
bean 并在注入点局部更改 WebClient.Builder
.
最后,您可以回退到原始 API 并使用 WebClient.create(). 在这种情况下,不会应用自动配置或 WebClientCustomizer.
16. 验证
只要 classpath 上存在 JSR-303 实现 (例如 Hibernate 验证器) ,就会自动启用 Bean Validation 1.1 支持的方法验证功能. 这允许 bean 方法在其参数和/或返回值上使用 javax.validation
约束进行注解. 带有此类注解方法的目标类需要在类级别上使用 @Validated
进行注解,以便搜索其内联约束注解的方法.
例如,以下服务触发第一个参数的验证,确保其大小在 8 到 10 之间:
@Service
@Validated
public class MyBean {
public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code,
Author author) {
...
}
}
17. 发送邮件
Spring Framework 提供了一个使用 JavaMailSender
接口发送电子邮件的简单抽象,Spring Boot 为其提供了自动配置以及一个 starter 模块.
有关如何使用 JavaMailSender 的详细说明,请参阅 参考文档. |
如果 spring.mail.host
和相关库 (由 spring-boot-starter-mail
定义) 可用,则创建默认的 JavaMailSender
(如果不存在) . 可以通过 spring.mail
命名空间中的配置项进一步自定义发件人. 有关更多详细信息,请参阅 MailProperties
.
特别是,某些默认超时时间的值是无限的,您可能想更改它以避免线程被无响应的邮件服务器阻塞,如下示例所示:
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000
也可以使用 JNDI 中的现有 Session
配置一个 JavaMailSender
:
spring.mail.jndi-name=mail/Session
设置 jndi-name
时,它优先于所有其他与 Session 相关的设置.
18. JTA 分布式事务
Spring Boot 通过使用 Atomikos 或 Bitronix 嵌入式事务管理器来支持跨多个 XA 资源的分布式 JTA 事务. 部署在某些 Java EE 应用服务器 (Application Server) 上也支持 JTA 事务.
当检测到 JTA 环境时,Spring 的 JtaTransactionManager
将用于管理事务. 自动配置的 JMS、DataSource 和 JPA bean 已升级为支持 XA 事务. 您可以使用标准的 Spring 方式 (例如 @Transactional
) 来使用分布式事务. 如果您处于 JTA 环境中并且仍想使用本地事务,则可以将 spring.jta.enabled
属性设置为 false
以禁用 JTA 自动配置.
18.1. 使用 Atomikos 事务管理器
Atomikos 是一个流行的开源事务管理器,可以嵌入到 Spring Boot 应用程序中. 您可以使用 spring-boot-starter-jta-atomikos
starter 来获取相应的 Atomikos 库. Spring Boot 自动配置 Atomikos 并确保将合适的依赖设置应用于 Spring bean,以确保启动和关闭顺序正确.
默认情况下,Atomikos 事务日志将写入应用程序主目录 (应用程序 jar 文件所在的目录) 中的 transaction-logs
目录. 您可以通过在 application.properties
文件中设置 spring.jta.log-dir
属性来自定义此目录的位置. 也可用 spring.jta.atomikos.properties
开头的属性来自定义 Atomikos UserTransactionServiceImp
. 有关完整的详细信息,请参阅 AtomikosProperties
Javadoc.
为确保多个事务管理器可以安全地协调相同的资源管理器,必须为每个 Atomikos 实例配置唯一 ID. 默认情况下,此 ID 是运行 Atomikos 的计算机的 IP 地址. 在生产环境中要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并使用不同的值.
|
18.2. 使用 Bitronix 事务管理器
Bitronix 是一个流行的开源 JTA 事务管理器实现. 您可以使用 spring-boot-starter-jta-bitronix
starter 为您的项目添加合适的 Bitronix 依赖. 与 Atomikos 一样,Spring Boot 会自动配置 Bitronix 并对 bean 进行后处理 (post-processes) ,以确保启动和关闭顺序正确.
默认情况下,Bitronix 事务日志文件 (part1.btm
和 part2.btm
) 将写入应用程序主目录中的 transaction-logs
目录. 您可以通过设置 spring.jta.log-dir
属性来自定义此目录的位置. 以 spring.jta.bitronix.properties
开头的属性绑定到了 bitronix.tm.Configuration
bean,允许完全自定义. 有关详细信息,请参阅 Bitronix 文档.
为确保多个事务管理器能够安全地协调相同的资源管理器,必须为每个 Bitronix 实例配置唯一的 ID. 默认情况下,此 ID 是运行 Bitronix 的计算机的 IP 地址. 生产环境要确保唯一性,应为应用程序的每个实例配置 spring.jta.transaction-manager-id 属性,并使用不同的值.
|
18.3. 使用 Java EE 管理的事务管理器
如果将 Spring Boot 应用程序打包为 war
或 ear
文件并将其部署到 Java EE 应用程序服务器,则可以使用应用程序服务器的内置事务管理器. Spring Boot 尝试通过查找常见的 JNDI 位置 (java:comp/UserTransaction
、java:comp/TransactionManager
等) 来自动配置事务管理器. 如果使用应用程序服务器提供的事务服务,
通常还需要确保所有资源都由服务器管理并通过 JNDI 暴露. Spring Boot 尝试通过在 JNDI 路径 (java:/JmsXA
或 java:/JmsXA
) 中查找 ConnectionFactory
来自动配置 JMS,并且可以使用 spring.datasource.jndi-name
属性 属性来配置 DataSource
.
18.4. 混合使用 XA 与非 XA JMS 连接
使用 JTA 时,主 JMS ConnectionFactory
bean 可识别 XA 并参与分布式事务. 在某些情况下,您可能希望使用非 XA ConnectionFactory
处理某些 JMS 消息. 例如,您的 JMS 处理逻辑可能需要比 XA 超时时间更长的时间.
如果要使用非 XA ConnectionFactory
,可以注入 nonXaJmsConnectionFactory
bean 而不是 @Primary
jmsConnectionFactory
bean. 为了保持一致性,提供的 jmsConnectionFactory
bean 还需要使用 xaJmsConnectionFactory
别名.
以下示例展示了如何注入 ConnectionFactory
实例:
// Inject the primary (XA aware) ConnectionFactory
@Autowired
private ConnectionFactory defaultConnectionFactory;
// Inject the XA aware ConnectionFactory (uses the alias and injects the same as above)
@Autowired
@Qualifier("xaJmsConnectionFactory")
private ConnectionFactory xaConnectionFactory;
// Inject the non-XA aware ConnectionFactory
@Autowired
@Qualifier("nonXaJmsConnectionFactory")
private ConnectionFactory nonXaConnectionFactory;
18.5. 支持嵌入式事务管理器
XAConnectionFactoryWrapper
和 XADataSourceWrapper
接口可用于支持其他嵌入式事务管理器. 接口负责包装 XAConnectionFactory
和 XADataSource
bean,并将它们暴露为普通的 ConnectionFactory
和 DataSource
bean,它们透明地加入分布式事务. DataSource
和 JMS 自动配置使用 JTA 变体,前提是您需要有一个 JtaTransactionManager
bean 和在 ApplicationContext
中注册有的相应 XA 包装器 (wrapper) bean.
BitronixXAConnectionFactoryWrapper 和 BitronixXADataSourceWrapper 为如何编写 XA 包装器提供了很好示例.
19. Hazelcast
如果 Hazelcast 在 classpath 上并有合适的配置,则 Spring Boot 会自动配置一个可以在应用程序中注入的 HazelcastInstance
.
如果定义了 com.hazelcast.config.Config
bean,则 Spring Boot 会使用它. 如果您的配置定义了实例名称,Spring Boot 会尝试查找现有的实例,而不是创建新实例.
您还可以指定通过配置使用的 Hazelcast 配置文件,如以下示例所示:
spring.hazelcast.config=classpath:config/my-hazelcast.xml
否则,Spring Boot 会尝试从默认位置查找 Hazelcast 配置: 工作目录或 classpath 根目录中的 hazelcast.xml
,或相同位置中的 .yaml
文件. 我们还检查是否设置了 hazelcast.config
系统属性. 有关更多详细信息,请参见 Hazelcast documentation.
如果 classpath 中存在 hazelcast-client
,则 Spring Boot 会首先尝试通过检查以下配置项来创建客户端:
-
存在
com.hazelcast.client.config.ClientConfig
bean. -
spring.hazelcast.config
属性定义的配置文件. -
存在
hazelcast.client.config
系统属性. -
工作目录中或 classpath 根目录下的
hazelcast-client.xml
. -
工作目录中或 classpath 根目录下的
hazelcast-client.yaml
.
Spring Boot 还为 Hazelcast 提供了缓存支持. 如果启用了缓存,HazelcastInstance 将自动包装在 CacheManager 实现中.
|
20. Quartz 调度器
Spring Boot 提供了几种使用 Quartz 调度器的便捷方式,它们来自 spring-boot-starter-quartz
starter. 如果 Quartz 可用,则 Spring Boot 将自动配置 Scheduler
(通过 SchedulerFactoryBean
抽象) .
自动选取以下类型的 Bean 并将其与 Scheduler
关联起来:
-
JobDetail
: 定义一个特定的 job. 可以使用JobBuilder
API 构建JobDetail
实例. -
Calendar
. -
Trigger
: 定义何时触发 job.
默认使用内存存储方式的 JobStore
. 但如果应用程序中有 DataSource
bean,并且配置了 spring.quartz.job-store-type
属性,则可以配置基于 JDBC 的存储,如下所示:
spring.quartz.job-store-type=jdbc
使用 JDBC 存储时,可以在启动时初始化 schema (表结构) ,如下所示:
spring.quartz.jdbc.initialize-schema=always
默认将使用 Quartz 库提供的标准脚本检测并初始化数据库. 这些脚本会删除现有表,在每次重启时删除所有触发器. 可以通过设置 spring.quartz.jdbc.schema 属性来提供自定义脚本.
|
要让 Quartz 使用除应用程序主 DataSource
之外的 DataSource
,请声明一个 DataSource
bean,使用 @QuartzDataSource
注解其 @Bean
方法. 这样做可确保 SchedulerFactoryBean
和 schema 初始化都使用 Quartz 指定的 DataSource
.
类似地,要让 Quartz 使用应用程序的主 TransactionManager
之外的 TransactionManager
来声明 TransactionManager
bean,并用 @QuartzTransactionManager
注解其 @Bean
方法。
默认情况下,配置创建的 job 不会覆盖已从持久 job 存储读取的已注册的 job. 要启用覆盖现有的 job 定义,请设置 spring.quartz.overwrite-existing-jobs
属性.
Quartz 调取器配置可以使用 spring.quartz
属性和 SchedulerFactoryBeanCustomizer
bean 进行自定义,它们允许以编程方式的 SchedulerFactoryBean
自定义. 可以使用 spring.quartz.properties.*
自定义高级 Quartz 配置属性.
需要强调的是,Executor bean 与调度程序没有关联,因为 Quartz 提供了通过 spring.quartz.properties 配置调度器的方法. 如果需要自定义 Actuator ,请考虑实现 SchedulerFactoryBeanCustomizer .
|
job 可以定义 setter 以注入数据映射属性. 也可以以类似的方式注入常规的 bean,如下所示:
public class SampleJob extends QuartzJobBean {
private MyService myService;
private String name;
// Inject "MyService" bean
public void setMyService(MyService myService) { ... }
// Inject the "name" job data property
public void setName(String name) { ... }
@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
...
}
}
21. 任务执行与调度
在上下文中没有 Executor
bean 的情况下,Spring Boot 会自动配置一个有合理默认值的 ThreadPoolTaskExecutor
,它可以自动与异步任务执行 (@EnableAsync
) 和 Spring MVC 异步请求处理相关联.
如果您在上下文中定义了自定义 您可以使用自动配置的 |
线程池使用 8 个核心线程,可根据负载情况增加和减少. 可以使用 spring.task.execution
命名空间对这些默认设置进行微调,如下所示:
spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s
这会将线程池更改为使用有界队列,在队列满 (100 个任务) 时,线程池将增加到最多 16 个线程. 当线程在闲置10 秒 (而不是默认的 60 秒) 时回收线程,池的收缩更为明显.
如果需要与调度任务执行 (@EnableScheduling
) 相关联,可以自动配置一个 ThreadPoolTaskScheduler
. 默认情况下,线程池使用一个线程,可以使用 spring.task.scheduling
命名空间对这些设置进行微调.
如果需要创建自定义 Actuator 或调度器,则在上下文中可以使用 TaskExecutorBuilder
bean 和 TaskSchedulerBuilder
bean.
22. Spring Integration
Spring Boot 为 Spring Integration 提供了一些便捷的使用方式,它们包含在 spring-boot-starter-integration
starter 中. Spring Integration 为消息传递以及其他传输 (如 HTTP、TCP 等) 提供了抽象.
如果 classpath 上存在 Spring Integration,则 Spring Boot 会通过 @EnableIntegration
注解对其进行初始化.
Spring Boot 还配置了一些由其他 Spring Integration 模块触发的功能. 如果 spring-integration-jmx
也在 classpath 上,则消息处理统计信息将通过 JMX 发布. 如果 spring-integration-jdbc
可用,则可以在启动时创建默认数据库模式,如下所示:
spring.integration.jdbc.initialize-schema=always
有关更多详细信息,请参阅 IntegrationAutoConfiguration
和 IntegrationProperties
类.
默认情况下,如果存在 Micrometer meterRegistry
bean,则 Micrometer 将管理 Spring Integration 的指标. 如果您希望使用旧版 Spring Integration 指标,请将 DefaultMetricsFactory
bean 添加到应用程序上下文中.
23. Spring Session
Spring Boot 为各种数据存储提供 Spring Session 自动配置. 在构建 Servlet Web 应用程序时,可以自动配置以下存储:
-
JDBC
-
Redis
-
Hazelcast
-
MongoDB
Servlet 自动配置取代了使用 @Enable*HttpSession
.
构建响应式 Web 应用程序时,可以自动配置以下存储:
-
Redis
-
MongoDB
reactive 自动配置取代了使用 @Enable*WebSession
.
如果 classpath 上存在单个 Spring Session 模块,则 Spring Boot 会自动使用该存储实现. 如果您有多个实现,则必须选择要用于存储会话的 StoreType
. 例如,要使用 JDBC 作为后端存储,您可以按如下方式配置应用程序:
spring.session.store-type=jdbc
可以将 store-type 设置为 none 来禁用 Spring Session.
|
每个 store 都有自己的额外设置. 例如,可以为 JDBC 存储定制表的名称,如下所示:
spring.session.jdbc.table-name=SESSIONS
可以使用 spring.session.timeout
属性来设置会话的超时时间. 如果未设置该属性,则自动配置将使用 server.servlet.session.timeout
的值.
您可以使用 @Enable*HttpSession
(Servlet) 或 @Enable*WebSession
(Reactive) 来控制 Spring Session 的配置。
这将导致自动配置失效. 然后可以使用注解的属性而不是之前描述的配置属性来配置 Spring Session.
24. 通过 JMX 监控和管理
Java Management Extensions (JMX,Java 管理扩展) 提供了一种监视和管理应用程序的标准机制. 默认情况下,Spring Boot 会创建一个 ID 为 mbeanServer
的 MBeanServer
bean,并暴露使用 Spring JMX 注解 (@ManagedResource
、@ManagedAttribute
或 @ManagedOperation
) 的 bean.
有关更多详细信息,请参阅 JmxAutoConfiguration
类.
25. 测试
Spring Boot提供了许多工具类和注解,可以在测试应用程序时提供帮助. 主要由两个模块提供: spring-boot-test
包含核心项,spring-boot-test-autoconfigure
支持测试的自动配置.
大多数开发人员都使用 spring-boot-starter-test
“Starter”,它会导入Spring Boot 测试模块以及 JUnit Jupiter,AssertJ,Hamcrest和许多其他有用的库.
启动程序还带来了老式引擎,因此您可以运行JUnit 4和JUnit 5测试. 如果已将测试迁移到JUnit 5,则应排除对JUnit 4的支持,如以下示例所示:
|
25.1. 依赖范围测试
spring-boot-starter-test
“Starter” (在 test
scope
) 包含以下的库: :
通常,我们发现这些通用库在编写测试时很有用. 如果这些库不满足您的需求,则可以添加自己的其他测试依赖.
25.2. 测试 Spring 应用程序
依赖注入的主要优点之一是,它应该使您的代码更易于进行单元测试. 您可以使用 new
运算符实例化对象,甚至无需使用 Spring. 您还可以使用模拟对象而不是实际的依赖.
通常,您需要超越单元测试并开始集成测试 (使用Spring ApplicationContext
) . 能够进行集成测试而无需部署应用程序或连接到其他基础结构是很有用的.
Spring框架包括用于此类集成测试的专用测试模块. 您可以直接向 org.springframework: spring-test
声明依赖,也可以使用 spring-boot-starter-test
“Starter” 将其传递.
如果您以前没有使用过 spring-test
模块,则应先阅读 Spring Framework参考文档的相关部分 .
25.3. 测试 Spring Boot 应用程序
Spring Boot应用程序是Spring ApplicationContext
,因此除了对普通Spring上下文进行常规测试以外,无需执行任何其他特殊操作即可对其进行测试.
默认情况下,仅当您使用 SpringApplication 创建Spring Boot的外部属性,日志记录和其他功能时,才将它们安装在上下文中.
|
Spring Boot提供了 @SpringBootTest
注解,当您需要Spring Boot功能时,可以将其用作标准 spring-test
@ContextConfiguration
注解的替代方法. 注解通过创建 SpringApplication
在测试中使用的 ApplicationContext
来起作用.
除了 @SpringBootTest
之外,还提供了许多其他注解来测试应用程序的特定的部分.
如果您使用的是JUnit 4,请不要忘记也将 @RunWith(SpringRunner.class) 添加到测试中,否则注解将被忽略. 如果您使用的是JUnit 5,则无需将等效的 @ExtendWith(SpringExtension.class) 添加为 @SpringBootTest ,而其他 @…Test 注解已经在其中进行了注解.
|
默认情况下,@SpringBootTest
将不会启动服务器. 您可以使用 @SpringBootTest
的 webEnvironment
属性来进一步完善测试的运行方式:
-
MOCK
(默认) : 加载Web ApplicationContext 并提供模拟Web环境. 使用此注解时,不会启动嵌入式服务器. 如果您的类路径上没有Web环境,则此模式将透明地退回到创建常规的非WebApplicationContext
. 它可以与@AutoConfigureMockMvc
或@AutoConfigureWebTestClient
结合使用,以对Web应用程序进行基于模拟的测试. -
RANDOM_PORT
: 加载WebServerApplicationContext
并提供真实的Web环境. 在随机的端口启动并监听嵌入式服务器. -
DEFINED_PORT
: 加载WebServerApplicationContext
并提供真实的Web环境. 在定义的端口(来自application.properties
) 或8080
端口启动并监听嵌入式服务器 -
NONE
: 使用SpringApplication
加载ApplicationContext
,但不提供任何Web环境 (模拟或其他方式) .
如果您的测试是 @Transactional ,则默认情况下它将在每个测试方法的末尾回滚事务. 但是,由于将这种安排与 RANDOM_PORT 或 DEFINED_PORT 一起使用隐式提供了一个真实的Servlet环境,因此HTTP客户端和服务器在单独的线程中运行,因此在单独的事务中运行. 在这种情况下,服务器上启动的任何事务都不会回滚.
|
如果您的应用程序将不同的端口用于管理服务器,则 @SpringBootTest 的 webEnvironment=WebEnvironment.RANDOM_PORT 也将在单独的随机端口上启动管理服务器.
|
25.3.1. 检测 Web 应用程序类型
如果Spring MVC可用,则配置基于常规MVC的应用程序上下文. 如果您只有Spring WebFlux,我们将检测到该情况并配置基于WebFlux的应用程序上下文.
如果两者都存在,则Spring MVC优先. 如果要在这种情况下测试反应式Web应用程序,则必须设置 spring.main.web-application-type
属性:
@SpringBootTest(properties = "spring.main.web-application-type=reactive")
class MyWebFluxTests { ... }
25.3.2. 检测测试配置
如果您熟悉Spring Test Framework,则可能习惯于使用 @ContextConfiguration(classes=…)
以指定要加载哪个Spring @Configuration
. 另外,您可能经常在测试中使用嵌套的 @Configuration
类.
在测试Spring Boot应用程序时,通常不需要这样做. 只要您没有明确定义,Spring Boot的 @*Test
注解就会自动搜索您的主要配置.
搜索算法从包含测试的程序包开始工作,直到找到带有 @SpringBootApplication
或 @SpringBootConfiguration
注解的类. 只要您以合理的方式对代码进行结构化 ,通常就可以找到您的主要配置.
如果您使用测试注解来测试应用程序的特定部分,,则应避免在 应用程序的 main方法 中添加特定于特定区域的配置设置.
|
如果要自定义主要配置,则可以使用嵌套的 @TestConfiguration
类. 与将使用嵌套的 @Configuration
类代替应用程序的主要配置不同的是,在应用程序的主要配置之外还使用了嵌套的 @TestConfiguration
类.
Spring的测试框架在测试之间缓存应用程序上下文. 因此,只要您的测试共享相同的配置 (无论如何发现) ,加载上下文的潜在耗时过程就只会发生一次. |
25.3.3. 排除测试配置
如果您的应用程序使用组件扫描 (例如,如果使用 @SpringBootApplication
或 @ComponentScan
) ,则可能会发现偶然为各地创建的仅为特定测试创建的顶级配置类.
如前所述,@TestConfiguration
可以用于测试的内部类以自定义主要配置. 当放置在顶级类上时, @TestConfiguration
指示不应通过扫描选择 src/test/java
中的类. 然后,可以在需要的位置显式导入该类,如以下示例所示:
@SpringBootTest
@Import(MyTestsConfiguration.class)
class MyTests {
@Test
void exampleTest() {
...
}
}
如果直接使用 @ComponentScan (即不是通过 @SpringBootApplication ) ,则需要向其中注册 TypeExcludeFilter . 有关详细信息,请参见 Javadoc.
|
25.3.4. 使用应用程序参数
如果您的应用程序需要参数,则可以使用 args
属性让 @SpringBootTest
注入参数.
@SpringBootTest(args = "--app.test=one")
class ApplicationArgumentsExampleTests {
@Test
void applicationArgumentsPopulated(@Autowired ApplicationArguments args) {
assertThat(args.getOptionNames()).containsOnly("app.test");
assertThat(args.getOptionValues("app.test")).containsOnly("one");
}
}
25.3.5. 在模拟环境中进行测试
默认情况下,@SpringBootTest
不会启动服务器. 如果您有要在此模拟环境下进行测试的Web端点,则可以另外配置 MockMvc
,如以下示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
class MockMvcExampleTests {
@Test
void exampleTest(@Autowired MockMvc mvc) throws Exception {
mvc.perform(get("/")).andExpect(status().isOk()).andExpect(content().string("Hello World"));
}
}
如果只想关注Web层而不希望启动完整的 ApplicationContext ,请考虑使用@WebMvcTest .
|
另外,您可以配置 WebTestClient
,如以下示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest
@AutoConfigureWebTestClient
class MockWebTestClientExampleTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello World");
}
}
在模拟环境中进行测试通常比在完整的Servlet容器中运行更快. 但是,由于模拟发生在Spring MVC层,因此无法使用MockMvc直接测试依赖于较低级别Servlet容器行为的代码. 例如,Spring Boot的错误处理基于Servlet容器提供的 “error page” 支持. 这意味着,尽管您可以按预期测试MVC层引发并处理异常,但是您无法直接测试是否呈现了特定的自定义错误页面. 如果需要测试这些较低级别的问题,则可以按照下一节中的描述启动一个完全运行的服务器. |
25.3.6. 使用正在运行的服务器进行测试
如果需要启动完全运行的服务器,建议您使用随机端口. 如果使用 @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
,则每次运行测试时都会随机选择一个可用端口.
@LocalServerPort
注解可用于将 将实际使用的端口注入 测试中. 为了方便起见,需要对已启动的服务器进行REST调用的测试可以 @Autowire
附加地使用 WebTestClient
,
该 WebTestClient
解析到正在运行的服务器的相对链接,并带有用于验证响应的专用API,如以下示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RandomPortWebTestClientExampleTests {
@Test
void exampleTest(@Autowired WebTestClient webClient) {
webClient.get().uri("/").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("Hello World");
}
}
这种设置需要在类路径上使用 spring-webflux
. 如果您无法或不会添加webflux,则Spring Boot还提供了 TestRestTemplate
工具:
If you can’t or won’t add webflux, Spring Boot also provides a TestRestTemplate
facility:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class RandomPortTestRestTemplateExampleTests {
@Test
void exampleTest(@Autowired TestRestTemplate restTemplate) {
String body = restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}
}
25.3.7. 自定义 WebTestClient
要定制 WebTestClient
bean,请配置 WebTestClientBuilderCustomizer
bean. 将使用用于创建 WebTestClient
的 WebTestClient.Builder
调用任何此类bean.
25.3.8. 使用 JMX
由于测试上下文框架缓存上下文,因此默认情况下禁用JMX以防止相同组件在同一域上注册. 如果此类测试需要访问 MBeanServer
,请考虑将其标记为脏:
@ExtendWith(SpringExtension.class)
@SpringBootTest(properties = "spring.jmx.enabled=true")
@DirtiesContext
class SampleJmxTests {
@Autowired
private MBeanServer mBeanServer;
@Test
void exampleTest() {
// ...
}
}
25.3.9. 模拟和检测 Bean
运行测试时,有时有必要在应用程序上下文中模拟某些组件. 例如,您可能在开发过程中无法使用某些远程服务的外观. 当您要模拟在实际环境中可能难以触发的故障时,模拟也很有用.
Spring Boot包含一个 @MockBean
注解,可用于为 ApplicationContext
中的bean定义 Mockito
模拟. 您可以使用注解添加新bean或替换单个现有bean定义. 注解可以直接用于测试类,测试中的字段或 @Configuration
类和字段.
在字段上使用时,还将注入创建的模拟的实例. 每种测试方法后,模拟 Bean 都会自动重置.
如果您的测试使用Spring Boot的测试注解之一 (例如
|
下面的示例用模拟实现替换现有的 RemoteService
bean:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
@SpringBootTest
class MyTests {
@MockBean
private RemoteService remoteService;
@Autowired
private Reverser reverser;
@Test
void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}
}
@MockBean 不能用于模拟应用程序上下文刷新期间执行的bean的行为. 到执行测试时,应用程序上下文刷新已完成,并且配置模拟行为为时已晚. 我们建议在这种情况下使用 @Bean 方法创建和配置模拟.
|
此外,您可以使用 @SpyBean
用Mockito间谍包装任何现有的bean. 有关完整的详细信息,请参见 Javadoc.
CGLib代理 (例如为作用域内的Bean创建的代理) 将代理方法声明为 final . 这将阻止 Mockito 正常运行,因为它无法在其默认配置中模拟或监视最终方法.
如果要模拟或监视这样的bean,请通过将 org.mockito: mockito-inline 添加到应用程序的测试依赖中,将Mockito配置为使用其嵌入式模拟生成器. 这允许Mockito模拟和监视 final 方法.
|
Spring的测试框架在测试之间缓存应用程序上下文,并为共享相同配置的测试重用上下文,而 @MockBean 或 @SpyBean 的使用会影响缓存键,这很可能会增加上下文数量.
|
如果您使用 @SpyBean 通过 @Cacheable 方法监视通过名称引用参数的 bean,则必须使用 -parameters 编译应用程序. 这样可以确保一旦侦察到bean,参数名称就可用于缓存基础结构.
|
当您使用 @SpyBean 监视由 Spring 代理的 bean 时,在某些情况下,例如使用 given 或 when 设置期望值时,您可能需要删除 Spring 的代理。
使用 AopTestUtils.getTargetObject(yourProxiedSpy) 执行此操作.
|
25.3.10. 自动配置测试
Spring Boot的自动配置系统适用于应用程序,但有时对测试来说可能有点过多. 它通常仅有助于加载测试应用程序 "切片" 所需的配置部分. 例如,您可能想要测试Spring MVC控制器是否正确映射了URL,并且您不想在这些测试中涉及数据库调用,或者您想要测试JPA实体,并且对那些JPA实体不感兴趣. 测试运行.
spring-boot-test-autoconfigure
模块包括许多注解,可用于自动配置此类 "切片". 它们中的每一个都以相似的方式工作,提供了一个 @…Test
注解 (该注解加载了 ApplicationContext
) 以及一个或多个 @AutoConfigure…
(可用于自定义自动配置设置的注解) .
每个 “slicing” 将组件扫描限制为适当的组件,并加载一组非常受限制的自动配置类. 如果您需要排除其中之一,大多数 @…Test 注解提供了 excludeAutoConfiguration 属性. 或者,您可以使用 @ImportAutoConfiguration#exclude .
|
不支持在一个测试中使用多个 @…Test 注解来包含多个 "片段". 如果您需要多个 “slices”,请选择 @…Test 注解之一,并手动添加其他 “slices” 的 @AutoConfigure… 注解.
|
也可以将 @AutoConfigure… 注解与标准的 @SpringBootTest 注解一起使用. 如果您对 “slicing” 应用程序不感兴趣,但需要一些自动配置的测试bean,则可以使用此组合.
|
25.3.11. 自动配置的JSON测试
要测试对象JSON序列化和反序列化是否按预期工作,可以使用 @JsonTest
注解. @JsonTest
自动配置可用的受支持的JSON映射器,该映射器可以是以下库之一:
-
Jackson
ObjectMapper
, any@JsonComponent
beans and any JacksonModule
s -
Gson
-
Jsonb
可以在附录中找到由 @JsonTest 启用的自动配置列表.
|
如果需要配置自动配置的元素,则可以使用 @AutoConfigureJsonTesters
注解.
Spring Boot包含基于AssertJ的帮助程序,这些帮助程序可与JSONAssert和JsonPath库一起使用,以检查JSON是否按预期方式显示.
JacksonTester
,GsonTester
,JsonbTester
和 BasicJsonTester
类可以分别用于Jackson,Gson,Jsonb和Strings. 使用 @JsonTest
时,可以使用 @Autowired
测试类上的任何帮助程序字段. 以下示例显示了Jackson的测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.json.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.json.*;
import static org.assertj.core.api.Assertions.*;
@JsonTest
class MyJsonTests {
@Autowired
private JacksonTester<VehicleDetails> json;
@Test
void testSerialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make")
.isEqualTo("Honda");
}
@Test
void testDeserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content))
.isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}
}
JSON帮助程序类也可以直接在标准单元测试中使用. 为此,如果不使用 @JsonTest ,请在 @Before 方法中调用帮助程序的 initFields 方法.
|
如果您使用的是Spring Boot基于AssertJ的帮助器,以给定的JSON路径对数字值进行断言,则取决于类型,您可能无法使用 isEqualTo
. 相反,您可以使用AssertJ的满足条件来断言该值符合给定条件. 例如,以下示例断言实际数是一个偏移量为 0.01
且接近 0.15
的浮点值.
assertThat(json.write(message))
.extractingJsonPathNumberValue("@.test.numberValue")
.satisfies((number) -> assertThat(number.floatValue()).isCloseTo(0.15f, within(0.01f)));
25.3.12. 自动配置的 Spring MVC 测试
要测试Spring MVC控制器是否按预期工作,请使用 @WebMvcTest
注解. @WebMvcTest
自动配置Spring MVC基础结构,并将扫描的bean限制为 @Controller
, @ControllerAdvice
, @JsonComponent
, Converter
, GenericConverter
, Filter
, HandlerInterceptor
, WebMvcConfigurer
, 和 HandlerMethodArgumentResolver
. 当使用 @WebMvcTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
.
@EnableConfigurationProperties
可以用来包含 @ConfigurationProperties
bean。
可以在附录中找到 @WebMvcTest 启用的自动配置设置的列表.
|
如果需要注册其他组件,例如Jackson模块,则可以在测试中使用 @Import 导入其他配置类.
|
@WebMvcTest
通常仅限于单个控制器,并与 @MockBean
结合使用,以为所需的协作者提供模拟实现.
@WebMvcTest还可以自动配置MockMvc. Mock MVC提供了一种强大的方法来快速测试MVC控制器,而无需启动完整的HTTP服务器.
您还可以通过在非 @WebMvcTest (例如 @SpringBootTest ) 中使用 @AutoConfigureMockMvc 对其进行注解来自动配置 MockMvc . 以下示例使用 MockMvc :
|
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private MockMvc mvc;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
}
}
如果您需要配置自动配置的元素 (例如,当应该应用servlet过滤器时) ,则可以使用 @AutoConfigureMockMvc 注解中的属性.
|
如果使用 HtmlUnit 或 Selenium,则自动配置还会提供 HtmlUnit WebClient
bean 和 / 或 Selenium WebDriver
bean. 以下示例使用 HtmlUnit:
import com.gargoylesoftware.htmlunit.*;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
@WebMvcTest(UserVehicleController.class)
class MyHtmlUnitTests {
@Autowired
private WebClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}
}
默认情况下,Spring Boot将 WebDriver bean放在特殊的 “scope” 中,以确保驱动程序在每次测试后退出并注入新实例. 如果您不希望出现这种情况,则可以将 @Scope("singleton") 添加到 WebDriver @Bean 定义中.
|
Spring Boot创建的 webDriver 作用域将替换任何用户定义的同名作用域. 如果定义自己的 webDriver 作用域,则使用 @WebMvcTest 时可能会发现它停止工作.
|
如果您在类路径上具有Spring Security,则 @WebMvcTest
还将扫描 WebSecurityConfigurer
Bean. 您可以使用Spring Security的测试支持来代替完全禁用此类测试的安全性. 有关如何使用Spring Security的 MockMvc
支持的更多详细信息,请参见 howto.html 操作方法部分.
有时编写Spring MVC测试是不够的. Spring Boot可以帮助您在 实际服务器上运行完整的端到端测试. |
25.3.13. 自动配置的 Spring WebFlux 测试
要测试 Spring WebFlux 控制器是否按预期工作,可以使用 @WebFluxTest
注解. @WebFluxTest
自动配置Spring WebFlux基础结构,
并将扫描的bean限制为 @Controller
, @ControllerAdvice
, @JsonComponent
, Converter
, GenericConverter
, WebFilter
和 WebFluxConfigurer
. 使用 @WebFluxTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
.
@EnableConfigurationProperties
可以用来包含 @ConfigurationProperties
bean.
可以在附录中找到 @WebFluxTest 启用的自动配置的列表. .
|
如果需要注册其他组件,例如Jackson模块,则可以在测试中使用 @Import 导入其他配置类.
|
通常,@WebFluxTest
仅限于单个控制器,并与 @MockBean
注解结合使用,以为所需的协作者提供模拟实现.
@WebFluxTest
还可以自动配置 WebTestClient
,它提供了一种强大的方法来快速测试WebFlux控制器,而无需启动完整的HTTP服务器.
您还可以通过在非 @WebFluxTest (例如 @SpringBootTest ) 中自动配置 WebTestClient ,方法是使用 @AutoConfigureWebTestClient 对其进行注解. 下面的示例显示一个同时使用 @WebFluxTest 和 WebTestClient 的类:
|
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@WebFluxTest(UserVehicleController.class)
class MyControllerTests {
@Autowired
private WebTestClient webClient;
@MockBean
private UserVehicleService userVehicleService;
@Test
void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.webClient.get().uri("/sboot/vehicle").accept(MediaType.TEXT_PLAIN)
.exchange()
.expectStatus().isOk()
.expectBody(String.class).isEqualTo("Honda Civic");
}
}
WebFlux 应用程序仅支持此设置,因为在模拟的Web应用程序中使用 WebTestClient 目前仅与WebFlux一起使用.
|
@WebFluxTest 无法检测通过功能 Web 框架注册的路由. 为了在上下文中测试 RouterFunction bean,请考虑自己通过 @Import 或使用 @SpringBootTest 导入 RouterFunction .
|
@WebFluxTest 无法检测通过 SecurityWebFilterChain 类型的 @Bean 注册的自定义安全配置. 要将其包括在测试中,您将需要通过 @Import 导入或使用 @SpringBootTest 导入用于注册 bean 的配置.
|
有时编写 Spring WebFlux 测试是不够的. Spring Boot可以帮助您在实际服务器上运行完整的端到端测试. |
25.3.14. 自动配置的 Data JPA 测试
您可以使用 @DataJpaTest
注解来测试JPA应用程序. 默认情况下,它将扫描 @Entity
类并配置 Spring Data JPA 存储库. 如果在类路径上有嵌入式数据库,它也将配置一个. 使用 @DataJpaTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean
可以在附录中找到由 @DataJpaTest 启用的自动配置设置的列表.
|
默认情况下,数据JPA测试是事务性的,并在每次测试结束时回滚. 有关更多详细信息,请参见《 Spring Framework参考文档》中的 相关部分 . 如果这不是您想要的,则可以按以下方式禁用测试或整个类的事务管理:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class ExampleNonTransactionalTests {
}
数据JPA测试也可以注入 TestEntityManager
bean,它为专门为测试设计的标准JPA EntityManager
提供了替代方法. 如果要在 @DataJpaTest
实例之外使用 TestEntityManager
,也可以使用 @AutoConfigureTestEntityManager
注解.
如果需要,还可以使用 JdbcTemplate
. 以下示例显示了正在使用的 @DataJpaTest
注解:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.orm.jpa.*;
import static org.assertj.core.api.Assertions.*;
@DataJpaTest
class ExampleRepositoryTests {
@Autowired
private TestEntityManager entityManager;
@Autowired
private UserRepository repository;
@Test
void testExample() throws Exception {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("1234");
}
}
内存嵌入式数据库通常运行良好,不需要任何安装,因此通常可以很好地进行测试. 但是,如果您希望对真实数据库运行测试,则可以使用 @AutoConfigureTestDatabase
注解,如以下示例所示:
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
class ExampleRepositoryTests {
// ...
}
25.3.15. 自动配置的 JDBC 测试
@JdbcTest
与 @DataJpaTest
相似,但适用于仅需要数据源并且不使用Spring Data JDBC的测试. 默认情况下,它配置一个内存嵌入式数据库和一个 JdbcTemplate
. 使用 @JdbcTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean
可以在 附录中找到 @JdbcTest 启用的自动配置的列表.
|
默认情况下,JDBC测试是事务性的,并在每个测试结束时回滚. 有关更多详细信息,请参见《 Spring Framework参考文档》中的http://docs.jcohy.com/zh-cn/spring-framework/5.2.12.RELEASE/spring-framework-reference/index.htmltesting.html#testcontext-tx-enabling-transactions[相关部分]. 如果这不是您想要的,则可以为测试或整个类禁用事务管理,如下所示:
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class ExampleNonTransactionalTests {
}
如果您希望测试针对真实数据库运行,则可以使用 @AutoConfigureTestDatabase
注解,其方式与DataJpaTest相同. (请参阅自动配置的 Data JPA 测试. )
25.3.16. 自动配置的 Data JDBC 测试
@DataJdbcTest
与 @JdbcTest
相似,但适用于使用 Spring Data JDBC 存储库的测试. 默认情况下,它配置一个内存嵌入式数据库,一个 JdbcTemplate
和 Spring Data JDBC 存储库. 使用 @DataJdbcTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean
可以在附录中找到由 @DataJdbcTest 启用的自动配置的列表.
|
默认情况下,Data JDBC测试是事务性的,并在每个测试结束时回滚. 有关更多详细信息,请参见《 Spring Framework参考文档》中的 相关部分. 如果这不是您想要的,则可以禁用测试或整个测试类的事务管理,如JDBC示例所示.
如果您希望测试针对真实数据库运行,则可以使用 @AutoConfigureTestDatabase
注解,其方式与 DataJpaTest
相同. (请参阅自动配置的 Data JPA测试. )
25.3.17. 自动配置的 jOOQ Tests
您可以以与 @JdbcTest
类似的方式使用 @JooqTest
,但可以用于与jOOQ相关的测试. 由于jOOQ严重依赖与数据库模式相对应的基于 Java 的模式,因此将使用现有的 DataSource
.
如果要将其替换为内存数据库,则可以使用 @AutoConfigureTestDatabase
覆盖这些设置. (有关在Spring Boot中使用jOOQ的更多信息,请参阅本章前面的 "使用 jOOQ". ) 使用 @JooqTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean.
可以在附录中找到 @JooqTest 启用的自动配置的列表.
|
@JooqTest
配置 DSLContext
. 常规 @Component
bean未加载到 ApplicationContext
中. 以下示例显示了正在使用的 @JooqTest
注解:
import org.jooq.DSLContext;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
@JooqTest
class ExampleJooqTests {
@Autowired
private DSLContext dslContext;
}
JOOQ测试是事务性的,默认情况下会在每个测试结束时回滚. 如果这不是您想要的,则可以禁用测试或整个测试类的事务管理,如JDBC示例所示.
25.3.18. 自动配置的 Data MongoDB 测试
您可以使用 @DataMongoTest
测试MongoDB应用程序. 默认情况下,它配置内存嵌入式MongoDB (如果可用) ,配置 MongoTemplate
,扫描 @Document
类,并配置 Spring Data MongoDB 存储库. 使用 @DataMongoTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean.
(有关将 MongoDB 与 Spring Boot 结合使用的更多信息,请参阅本章前面的 "MongoDB")
可以在 附录中找到由 @DataMongoTest 启用的自动配置设置的列表.
|
此类显示正在使用的 @DataMongoTest
注解:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
import org.springframework.data.mongodb.core.MongoTemplate;
@DataMongoTest
class ExampleDataMongoTests {
@Autowired
private MongoTemplate mongoTemplate;
//
}
内存嵌入式MongoDB通常运行良好,并且不需要任何开发人员安装,因此通常可以很好地用于测试. 但是,如果您希望对真实的MongoDB服务器运行测试,则应排除嵌入式MongoDB自动配置,如以下示例所示:
import org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
class ExampleDataMongoNonEmbeddedTests {
}
25.3.19. 自动配置的 Data Neo4j 测试
您可以使用 @DataNeo4jTest
来测试 Neo4j 应用程序. 默认情况下,它使用内存中嵌入式 Neo4j (如果有嵌入式驱动程序可用) ,扫描 @NodeEntity
类,并配置 Spring Data Neo4j 存储库.
使用 @DataNeo4jTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
。
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean. (有关将 Neo4J 与 Spring Boot 结合使用的更多信息,请参阅本章前面的 "Neo4j". )
可以在 附录中找到由 @DataNeo4jTest 启用的自动配置设置的列表.
|
以下示例显示了在Spring Boot中使用Neo4J测试的典型设置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
@DataNeo4jTest
class ExampleDataNeo4jTests {
@Autowired
private YourRepository repository;
//
}
默认情况下,Data Neo4j测试是事务性的,并在每次测试结束时回滚. 有关更多详细信息,请参见《 Spring Framework参考文档》中的http://docs.jcohy.com/zh-cn/spring-framework/5.2.12.RELEASE/spring-framework-reference/index.htmltesting.html#testcontext-tx-enabling-transactions[相关部分] . 如果这不是您想要的,则可以为测试或整个类禁用事务管理,如下所示:
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@DataNeo4jTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
class ExampleNonTransactionalTests {
}
25.3.20. 自动配置的 Data Redis 测试
您可以使用 @DataRedisTest
测试Redis应用程序. 默认情况下,它会扫描 @RedisHash
类并配置Spring Data Redis存储库. 使用 @DataRedisTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
.
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean. (有关将 Redis 与 Spring Boot 结合使用的更多信息,请参阅本章前面的"Redis". )
可以在 附录中找到由 @DataRedisTest 启用的自动配置设置的列表.
|
The following example shows the @DataRedisTest
annotation in use:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
@DataRedisTest
class ExampleDataRedisTests {
@Autowired
private YourRepository repository;
//
}
25.3.21. 自动配置的 Data LDAP 测试
您可以使用 @DataLdapTest
来测试LDAP应用程序. 默认情况下,它配置内存嵌入式LDAP (如果可用) ,配置 LdapTemplate
,扫描 @Entry
类,并配置 Spring Data LDAP 存储库.
使用 @DataLdapTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
.
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean. (有关将 LDAP 与 Spring Boot 结合使用的更多信息,请参阅本章前面的 "LDAP". )
可以在 附录中找到由 @DataLdapTest 启用的自动配置设置的列表.
|
以下示例显示了正在使用的 @DataLdapTest
注解:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
import org.springframework.ldap.core.LdapTemplate;
@DataLdapTest
class ExampleDataLdapTests {
@Autowired
private LdapTemplate ldapTemplate;
//
}
内存嵌入式LDAP通常非常适合测试,因为它速度快并且不需要安装任何开发人员. 但是,如果您希望针对真实的LDAP服务器运行测试,则应排除嵌入式LDAP自动配置,如以下示例所示:
import org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration;
import org.springframework.boot.test.autoconfigure.data.ldap.DataLdapTest;
@DataLdapTest(excludeAutoConfiguration = EmbeddedLdapAutoConfiguration.class)
class ExampleDataLdapNonEmbeddedTests {
}
25.3.22. 自动配置的 REST Clients
您可以使用 @RestClientTest
注解来测试REST客户端. 默认情况下,它会自动配置 Jackson,GSON 和 Jsonb 支持,配置 RestTemplateBuilder
,并添加对 MockRestServiceServer
的支持. 使用 @RestClientTest
注解时,不会扫描常规的 @Component
和 @ConfigurationProperties
.
@EnableConfigurationProperties
可用于包含 @ConfigurationProperties
bean..
可以在 附录中找到由 @RestClientTest 启用的自动配置设置的列表.
|
应该使用 @RestClientTest
的 value
或 components
属性来指定要测试的特定bean,如以下示例所示:
@RestClientTest(RemoteVehicleDetailsService.class)
class ExampleRestClientTest {
@Autowired
private RemoteVehicleDetailsService service;
@Autowired
private MockRestServiceServer server;
@Test
void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
throws Exception {
this.server.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}
}
25.3.23. 自动配置的 Spring REST Docs 测试
您可以使用 @AutoConfigureRestDocs
注解在 Mock MVC,REST Assured 或 WebTestClient
的测试中使用 Spring REST Docs. 它消除了 Spring REST Docs 中对 JUnit 扩展的需求.
@AutoConfigureRestDocs
可用于覆盖默认输出目录 (如果使用Maven,则为 target/generated-snippets
如果使用 Gradle,则为 build/generated-snippets
) . 它也可以用于配置出现在任何记录的 URI 中的主机,方案和端口.
使用 Mock MVC 自动配置的 Spring REST Docs 测试
@AutoConfigureRestDocs
自定义 MockMvc
bean以使用 Spring REST Docs. 您可以使用 @Autowired
注入它,并像通常使用 Mock MVC 和 Spring REST Docs 一样,在测试中使用它,如以下示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class UserDocumentationTests {
@Autowired
private MockMvc mvc;
@Test
void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}
}
如果需要对Spring REST Docs配置进行更多控制,而不是 @AutoConfigureRestDocs
属性提供的控制,则可以使用 RestDocsMockMvcConfigurationCustomizer
bean,如以下示例所示:
@TestConfiguration
static class CustomizationConfiguration
implements RestDocsMockMvcConfigurationCustomizer {
@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
如果要使用Spring REST Docs对参数化输出目录的支持,可以创建 RestDocumentationResultHandler
bean. 自动配置使用此结果处理程序调用 alwaysDo
,从而使每个 MockMvc
调用自动生成默认片段. 以下示例显示了定义的 RestDocumentationResultHandler
:
@TestConfiguration(proxyBeanMethods = false)
static class ResultHandlerConfiguration {
@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}
}
使用 WebTestClient 自动配置的Spring REST Docs测试
@AutoConfigureRestDocs
也可以与 WebTestClient
一起使用. 您可以使用 @Autowired
注入它,并像通常使用 @WebFluxTest
和Spring REST Docs一样在测试中使用它,如以下示例所示:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
@WebFluxTest
@AutoConfigureRestDocs
class UsersDocumentationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void listUsers() {
this.webTestClient.get().uri("/").exchange().expectStatus().isOk().expectBody()
.consumeWith(document("list-users"));
}
}
如果需要对Spring REST Docs配置进行更多控制,而不是 @AutoConfigureRestDocs
属性提供的控制,则可以使用 RestDocsWebTestClientConfigurationCustomizer
bean,如以下示例所示:
@TestConfiguration(proxyBeanMethods = false)
public static class CustomizationConfiguration implements RestDocsWebTestClientConfigurationCustomizer {
@Override
public void customize(WebTestClientRestDocumentationConfigurer configurer) {
configurer.snippets().withEncoding("UTF-8");
}
}
使用 RES TAssured 自动配置的Spring REST Docs测试
@AutoConfigureRestDocs
使一个 RequestSpecification
Bean (可预配置为使用Spring REST Docs) 可用于您的测试. 您可以使用 @Autowired
注入它,并像在使用REST Assured 和Spring REST Docs时一样,在测试中使用它,如以下示例所示:
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
class UserDocumentationTests {
@Test
void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) {
given(documentationSpec).filter(document("list-users")).when().port(port).get("/").then().assertThat()
.statusCode(is(200));
}
}
如果您需要对Spring REST Docs配置进行更多控制,而不是 @AutoConfigureRestDocs
属性所提供的控制,则可以使用 RestDocsRestAssuredConfigurationCustomizer
bean,如以下示例所示:
@TestConfiguration(proxyBeanMethods = false)
public static class CustomizationConfiguration implements RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
25.3.24. 其他的自动配置和切片
每个切片提供一个或多个 @AutoConfigure…
注解,即定义应包含在切片中的自动配置. 可以通过创建自定义 @AutoConfigure…
注解来添加其他自动配置,也可以简单地通过将 @ImportAutoConfiguration
添加到测试中来添加其他自动配置,如以下示例所示:
@JdbcTest
@ImportAutoConfiguration(IntegrationAutoConfiguration.class)
class ExampleJdbcTests {
}
确保不要使用常规的 @Import 注解导入自动配置,因为它们是由Spring Boot以特定方式处理的.
|
25.3.25. 用户配置和切片
因此,变得重要的是,不要使用特定于其功能特定区域的配置设置来乱扔应用程序的主类.
假设您正在使用Spring Batch,并且依赖于它的自动配置. 您可以如下定义 @SpringBootApplication
:
@SpringBootApplication
@EnableBatchProcessing
public class SampleApplication { ... }
因为此类是测试的源配置,所以任何切片测试实际上都尝试启动Spring Batch,这绝对不是您想要执行的操作. 建议的方法是将特定于区域的配置移动到与您的应用程序处于同一级别的单独的 @Configuration
类,如以下示例所示:
@Configuration(proxyBeanMethods = false)
@EnableBatchProcessing
public class BatchConfiguration { ... }
根据您应用程序的复杂性,您可以为您的自定义设置一个 @Configuration 类,或者每个域区域一个类. 后一种方法使您可以在其中一个测试中使用 @Import 注解启用它.
|
测试片将 @Configuration
类从扫描中排除. 例如,对于 @WebMvcTest
,以下配置将在测试切片加载的应用程序上下文中不包括给定的 WebMvcConfigurer
Bean:
@Configuration
public class WebConfiguration {
@Bean
public WebMvcConfigurer testConfigurer() {
return new WebMvcConfigurer() {
...
};
}
}
但是,以下配置将导致自定义 WebMvcConfigurer
由测试片加载.
@Component
public class TestWebMvcConfigurer implements WebMvcConfigurer {
...
}
混乱的另一个来源是类路径扫描. 假定在以合理的方式组织代码的同时,您需要扫描其他程序包. 您的应用程序可能类似于以下代码:
@SpringBootApplication
@ComponentScan({ "com.example.app", "org.acme.another" })
public class SampleApplication { ... }
这样做有效地覆盖了默认的组件扫描指令,并且具有扫描这两个软件包的副作用,而与您选择的切片无关. 例如,@DataJpaTest
似乎突然扫描了应用程序的组件和用户配置. 同样,将自定义指令移至单独的类是解决此问题的好方法.
如果这不是您的选择,则可以在测试层次结构中的某个位置创建 @SpringBootConfiguration ,以便代替使用它. 或者,您可以为测试指定一个源,从而禁用查找默认源的行为.
|
25.3.26. 使用Spock测试Spring Boot应用程序
如果您希望使用Spock来测试Spring Boot应用程序,则应在应用程序的构建中添加对Spock的 spock-spring
模块的依赖. spock-spring
将Spring的测试框架集成到了Spock中.
建议您使用Spock 1.2或更高版本,以受益于Spock的Spring Framework和Spring Boot集成的许多改进. 有关更多详细信息,请参见 Spock的Spring模块的文档.
25.4. 测试实用工具
一些测试实用工具类通常在测试您的应用程序时有用,它们被打包为 spring-boot
的一部分.
25.4.1. ConfigFileApplicationContextInitializer
ConfigFileApplicationContextInitializer
是一个 ApplicationContextInitializer
,您可以将其应用于测试以加载Spring Boot application.properties
文件. 当不需要 @SpringBootTest
提供的全部功能时,可以使用它,如以下示例所示:
@ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
单独使用 ConfigFileApplicationContextInitializer 不能提供对 @Value("${…}") 注入的支持. 唯一的工作就是确保将 application.properties 文件加载到Spring的环境中. 为了获得 @Value 支持,您需要另外配置 PropertySourcesPlaceholderConfigurer 或使用 @SpringBootTest ,后者会为您自动配置一个.
|
25.4.2. TestPropertyValues
使用 TestPropertyValues
,可以快速将属性添加到 ConfigurableEnvironment
或 ConfigurableApplicationContext
. 您可以使用 key=value
字符串来调用它,如下所示:
TestPropertyValues.of("org=Spring", "name=Boot").applyTo(env);
25.4.3. OutputCapture
OutputCapture
是一个JUnit扩展,可用于捕获 System.out
和 System.err
输出. 要使用 add @ExtendWith(OutputCaptureExtension.class)
并将 CapturedOutput
作为参数注入测试类构造函数或测试方法,如下所示:
@ExtendWith(OutputCaptureExtension.class)
class OutputCaptureTests {
@Test
void testName(CapturedOutput output) {
System.out.println("Hello World!");
assertThat(output).contains("World");
}
}
25.4.4. TestRestTemplate
TestRestTemplate
是Spring RestTemplate
的一种便捷替代方案,在集成测试中非常有用. 您可以使用普通模板或发送基本HTTP身份验证 (带有用户名和密码) 的模板. 在这两种情况下,模板都不会通过在服务器端错误上引发异常来以易于测试的方式运行.
Spring Framework 5.0提供了一个新的 WebTestClient ,可用于WebFlux集成测试 和 WebFlux和MVC端到端测试. 与 TestRestTemplate 不同,它为声明提供了流式的API.
|
建议 (但不是强制性的) 使用Apache HTTP Client (版本4.3.2或更高版本) . 如果您在类路径中具有该名称,则 TestRestTemplate
会通过适当配置客户端进行响应. 如果您确实使用Apache的HTTP客户端,则会启用一些其他易于测试的功能:
-
不支持重定向(因此可以断言响应位置).
-
忽略cookie(因此模板是无状态的).
TestRestTemplate
可以在你的集成测试中直接实例化,如下面的例子所示:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
@Test
public void testRequest() throws Exception {
HttpHeaders headers = this.template.getForEntity(
"https://myhost.example.com/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
}
或者,如果将 @SpringBootTest
注解与 WebEnvironment.RANDOM_PORT
或 WebEnvironment.DEFINED_PORT
一起使用,则可以注入完全配置的 TestRestTemplate
并开始使用它.
如有必要,可以通过 RestTemplateBuilder
bean应用其他定制. 未指定主机和端口的所有URL都会自动连接到嵌入式服务器,如以下示例所示:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class SampleWebClientTests {
@Autowired
private TestRestTemplate template;
@Test
void testRequest() {
HttpHeaders headers = this.template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
@TestConfiguration(proxyBeanMethods = false)
static class Config {
@Bean
RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1))
.setReadTimeout(Duration.ofSeconds(1));
}
}
}
26. WebSockets
Spring Boot 为内嵌式 Tomcat、Jetty 和 Undertow 提供了 WebSocket 自动配置. 如果将 war 文件部署到独立容器,则 Spring Boot 假定容器负责配置其 WebSocket 支持.
Spring Framework 为 MVC Web 应用程序提供了 丰富的 WebSocket 支持 ,可以通过 spring-boot-starter-websocket
模块轻松访问.
WebSocket 支持也可用于 响应式 Web 应用程序 ,并且引入 WebSocket API 以及 spring-boot-starter-webflux
:
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
</dependency>
27. Web Services
Spring Boot 提供 Web Service 自动配置,因此您要做的就是定义 Endpoints.
可以使用 spring-boot-starter-webservices
模块轻松访问 Spring Web Services 功能.
可以分别为 WSDL 和 XSD 自动创建 SimpleWsdl11Definition
和 SimpleXsdSchema
bean. 为此,请配置其位置,如下所示:
spring.webservices.wsdl-locations=classpath:/wsdl
27.1. 使用 WebServiceTemplate
调用 Web Service
如果您需要从应用程序调用远程 Web 服务,则可以使用 WebServiceTemplate
类. 由于 WebServiceTemplate
实例在使用之前通常需要进行自定义,因此 Spring Boot 不提供任何自动配置的 WebServiceTemplate
bean.
但是,它会自动配置 WebServiceTemplateBuilder
,可在需要创建 WebServiceTemplate
实例时使用.
以下代码为一个典型示例:
@Service
public class MyService {
private final WebServiceTemplate webServiceTemplate;
public MyService(WebServiceTemplateBuilder webServiceTemplateBuilder) {
this.webServiceTemplate = webServiceTemplateBuilder.build();
}
public DetailsResp someWsCall(DetailsReq detailsReq) {
return (DetailsResp) this.webServiceTemplate.marshalSendAndReceive(detailsReq, new SoapActionCallback(ACTION));
}
}
默认情况下,WebServiceTemplateBuilder
使用 classpath 上的可用 HTTP 客户端库检测合适的基于 HTTP 的 WebServiceMessageSender
. 您还可以按如下方式自定义读取和连接的超时时间:
@Bean
public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) {
return builder.messageSenders(new HttpWebServiceMessageSenderBuilder()
.setConnectTimeout(5000).setReadTimeout(2000).build()).build();
}
28. 创建自己的自动配置
如果您在公司负责开发公共类库,或者如果您在开发一个开源或商业库,您可能希望开发自己的自动配置. 自动配置类可以捆绑在外部 jar 中,他仍然可以被 Spring Boot 获取.
自动配置可以与提供自动配置代码的 starter 以及您将使用的类库库相关联. 我们首先介绍构建自己的自动配置需要了解的内容,然后我们将继续介绍创建 自定义 starter 所需的步骤.
这里有一个 演示项目 展示了如何逐步创建 starter. |
28.1. 理解 自动配置的 Beans
在内部,自动配置使用了标准的 @Configuration
类来实现. @Conditional
注解用于约束何时应用自动配置. 通常,自动配置类使用 @ConditionalOnClass
和 @ConditionalOnMissingBean
注解.
这可确保仅在找到相关类时以及未声明您自己的 @Configuration
时才应用自动配置.
您可以浏览 spring-boot-autoconfigure
的源代码,以查看 Spring 提供的 @Configuration
类 (请参阅 META-INF/spring.factories
文件) .
28.2. 找到候选的自动配置
Spring Boot 会检查已发布 jar 中是否存在 META-INF/spring.factories
文件. 该文件应列出 EnableAutoConfiguration
key 下的配置类,如下所示:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\ com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
必须以这种方式加载自动配置. 确保它们在特定的包空间中定义,并且它们不能是组件扫描的目标. 此外,自动配置类不应启用组件扫描以查找其他组件. 应该使用特定的 @Imports 来代替.
|
如果需要按特定顺序应用配置,则可以使用 @AutoConfigureAfter
或 @AutoConfigureBefore
注解. 例如,如果您提供特定于 Web 的配置,则可能需要在 WebMvcAutoConfiguration
之后应用您的类.
如果您想排序某些不应该彼此直接了解的自动配置,您也可以使用 @AutoConfigureOrder
. 该注解与常规 @Order
注解有相同的语义,但它为自动配置类提供了专用顺序.
与标准的 @Configuration
类一样,自动配置类的应用顺序仅会影响其 bean 的定义顺序。
随后创建这些 bean 的顺序不受影响,并由每个 bean 的依赖关系和任何 @DependsOn
关系确定。
28.3. 条件注解
您几乎总希望在自动配置类中包含一个或多个 @Conditional
注解. @ConditionalOnMissingBean
是一个常用的注解,其允许开发人员在对您的默认值不满意用于覆盖自动配置.
Spring Boot 包含许多 @Conditional
注解,您可以通过注解 @Configuration
类或单独的 @Bean 方法在您自己的代码中复用它们. 这些注解包括:
28.3.1. 类条件
@ConditionalOnClass
和 @ConditionalOnMissingClass
注解允许根据特定类的是否存在来包含 @Configuration
类. 由于使用 ASM 解析注解元数据,您可以使用 value
属性来引用真实类,即使该类实际上可能不会出现在正在运行的应用程序的 classpath
中.
如果您希望使用 String 值来指定类名,也可以使用 name
属性.
此机制不会以相同的方式应用于返回类型是条件的目标的 @Bean
方法: 在方法上的条件应用之前,JVM 将加载类和可能处理的方法引用,如果找不到类,将发生失败.
要处理这种情况,可以使用单独的 @Configuration
类来隔离条件,如下所示:
@Configuration(proxyBeanMethods = false)
// Some conditions
public class MyAutoConfiguration {
// Auto-configured beans
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EmbeddedAcmeService.class)
static class EmbeddedConfiguration {
@Bean
@ConditionalOnMissingBean
public EmbeddedAcmeService embeddedAcmeService() { ... }
}
}
如果使用 @ConditionalOnClass 或 @ConditionalOnMissingClass 作为元注解的一部分来组成自己的组合注解,则必须使用 name 来引用类,在这种情况将不作处理.
|
28.3.2. Bean 条件
@ConditionalOnBean
和 @ConditionalOnMissingBean
注解允许根据特定 bean 是否存在来包含 bean. 您可以使用 value
属性按类型或使用 name
来指定 bean. search
属性允许您限制在搜索 bean 时应考虑的 ApplicationContext
层次结构.
放置在 @Bean
方法上时,目标类型默认为方法的返回类型,如下所示:
@Configuration(proxyBeanMethods = false)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() { ... }
}
在前面的示例中,如果 ApplicationContext
中不包含 MyService
类型的 bean,则将创建 myService
bean.
您需要非常小心地添加 bean 定义的顺序,因为这些条件是根据到目前为止已处理的内容进行计算的. 因此,我们建议在自动配置类上仅使用 @ConditionalOnBean 和 @ConditionalOnMissingBean 注解 (因为这些注解保证在添加所有用户定义的 bean 定义后加载) .
|
@ConditionalOnBean 和 @ConditionalOnMissingBean 不会阻止创建 @Configuration 类. 在类级别使用这些条件并使用注解标记每个包含 @Bean 方法的唯一区别是,如果条件不匹配,前者会阻止将 @Configuration 类注册为 bean.
|
当声明一个 @Bean 方法时,在方法的返回类型中提供尽可能多的类型信息。
例如,如果 bean 的具体类实现接口,则bean方法的返回类型应该是具体类而不是接口。
在使用 bean 条件时,在 @Bean 方法中提供尽可能多的类型信息尤为重要,因为它们的评估只能依赖于方法签名中可用的类型信息.
|
28.3.3. 属性条件
@ConditionalOnProperty
注解允许基于 Spring Environment 属性包含配置. 使用 prefix
和 name
属性指定需要检查的属性. 默认情况下,匹配存在且不等于 false
的所有属性. 您还可以使用 havingValue
和 matchIfMissing
属性创建更高级的检查.
28.3.4. 资源条件
@ConditionalOnResource
注解仅允许在存在特定资源时包含配置. 可以使用常用的 Spring 约定来指定资源,如下所示: file:/home/user/test.dat
.
28.3.5. Web 应用程序条件
@ConditionalOnWebApplication
和 @ConditionalOnNotWebApplication
注解在应用程序为 Web 应用程序的情况下是否包含配置. Web 应用程序是使用 Spring WebApplicationContext
,定义一个 session 作用域或具有 StandardServletEnvironment
的任何应用程序.
28.3.6. SpEL 表达式条件
@ConditionalOnExpression
注解允许根据 SpEL 表达式的结果包含配置.
28.4. 测试自动配置
自动配置可能受许多因素的影响: 用户配置 (@Bean
定义和 Environment
自定义) 、条件评估 (存在特定的类库) 等. 具体而言,每个测试都应该创建一个定义良好的 ApplicationContext
,它表示这些自定义的组合. ApplicationContextRunner
提供了一个好的实现方法.
ApplicationContextRunner
通常被定义为测试类的一个字段,用于收集基本的通用配置. 以下示例确保始终调用 UserServiceAutoConfiguration
:
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(UserServiceAutoConfiguration.class));
如果必须定义多个自动配置,则无需按照与运行应用程序时完全相同的顺序调用它们的声明. |
每个测试都可以使用 runner 来表示特定的用例. 例如,下面的示例调用用户配置 (UserConfiguration
) 并检查自动配置是否正确退回. 调用 run
提供了一个可以与 AssertJ
一起使用的回调上下文.
@Test
void defaultServiceBacksOff() {
this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context).getBean("myUserService").isSameAs(context.getBean(UserService.class));
});
}
@Configuration(proxyBeanMethods = false)
static class UserConfiguration {
@Bean
UserService myUserService() {
return new UserService("mine");
}
}
也可以轻松自定义 Environment
,如下所示:
@Test
void serviceNameCanBeConfigured() {
this.contextRunner.withPropertyValues("user.name=test123").run((context) -> {
assertThat(context).hasSingleBean(UserService.class);
assertThat(context.getBean(UserService.class).getName()).isEqualTo("test123");
});
}
runner 还可用于展示 ConditionEvaluationReport
. 报告可以在 INFO
或 DEBUG
级别下打印. 以下示例展示如何使用 ConditionEvaluationReportLoggingListener
在自动配置测试中打印报表.
@Test
public void autoConfigTest {
ConditionEvaluationReportLoggingListener initializer = new ConditionEvaluationReportLoggingListener(
LogLevel.INFO);
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withInitializer(initializer).run((context) -> {
// Do something...
});
}
28.4.1. 模拟一个 Web 上下文
如果需要测试一个仅在 Servlet 或响应式 Web 应用程序上下文中运行的自动配置,请分别使用 WebApplicationContextRunner
或 ReactiveWebApplicationContextRunner
.
28.4.2. 覆盖 Classpath
还可以测试在运行时不存在特定类和/或包时发生的情况. Spring Boot附带了一个可以由跑步者轻松使用的 FilteredClassLoader
. 在以下示例中,我们声明如果 UserService
不存在,则会正确禁用自动配置:
@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
this.contextRunner.withClassLoader(new FilteredClassLoader(UserService.class))
.run((context) -> assertThat(context).doesNotHaveBean("userService"));
}
28.5. 创建自己的 Starter
一个典型的 Spring Boot 启动器包含用于自动配置和使用的基础技术结构的代码,我们称其为 "acme". 为了使其易于扩展,可以将命名空间中的许多配置项暴露给环境。 最后,提供了一个 "starter" 依赖,以帮助用户尽可能轻松地入门。
具体而言,自定义启动器可以包含以下内容:
-
autoconfigure
模块,为 "acme" 包含自动配置代码. -
starter 模块,它为 "acme" 提供对
autoconfigure
模块依赖以及类库和常用的其他依赖. 简而言之,添加 starter 应该提供该库开始使用所需的一切依赖.
完全没有必要将这两个模块分开. 如果 "acme" 具有多种功能,选项或可选功能,则最好将自动配置分开,这样您可以清楚地表示某些功能是可选的.此外,您还可以制作一个启动器,以提供有关可选的依赖.
同时,其他人只能依靠 autoconfigure
模块来制作自己的具有不同选项的启动器。
如果自动配置相对简单并且不具有可选功能,则将两个模块合并在启动器中绝对是一种选择。
28.5.1. 命名
您应该确保为您的 starter 提供一个合适的命名空间. 即使您使用其他 Maven groupId,也不要使用 spring-boot
作为模块名称的开头. 我们可能会为您以后自动配置的内容提供官方支持.
根据经验,您应该在 starter 后命名一个组合模块. 例如,假设您正在为 acme 创建一个 starter,并且您将自动配置模块命名为 acme-spring-boot
,将 starter 命名为 acme-spring-boot-starter
. 如果您只有一个组合这两者的模块,请将其命名为 acme-spring-boot-starter
.
28.5.2. 配置 keys
此外,如果您的 starter 提供配置 key,请为它们使用唯一的命名空间. 尤其是,不要将您的 key 包含在 Spring Boot 使用的命名空间中 (例如 server
、management
、spring
等) . 如果您使用了相同的命名空间,我们将来可能会以破坏您的模块的方式来修改这些命名空间. 根据经验,所有 key 都必须拥有自己的命名空间 (例如 acme
) .
确保触发元数据生成,以便为您的 key 提供 IDE 帮助. 您可能想查看生成的元数据 (META-INF/spring-configuration-metadata.json
) 以确保您的 key 记录是否正确.
通过为每个属性添加字段 javadoc 来确保记录了配置 keys,如以下示例所示:
@ConfigurationProperties("acme")
public class AcmeProperties {
/**
* Whether to check the location of acme resources.
*/
private boolean checkLocation = true;
/**
* Timeout for establishing a connection to the acme server.
*/
private Duration loginTimeout = Duration.ofSeconds(3);
// getters & setters
}
您仅应将简单文本与 @ConfigurationProperties 字段 Javadoc 一起使用,因为在将它们添加到 JSON 之前不会对其进行处理.
|
这是我们内部遵循的一些规则,以确保描述一致:
-
请勿以 "The" 或 "A" 头描述.
-
对于布尔类型,请从 "Whether" 或 "Enable" 开始描述.
-
对于基于集合的类型,请以 "以逗号分隔的列表" 开始描述
-
使用
java.time.Duration
而不是long
,如果它不等于毫秒,请说明默认单位,例如 "如果未指定持续时间后缀,则将使用秒". -
除非必须在运行时确定默认值,否则请不要在描述中提供默认值.
确保 触发元数据生成 ,以便为您的 key 提供 IDE 帮助. . 您可能需要查看生成的元数据(META-INF/spring-configuration-metadata.json
) ,以确保您的 key 记录是否正确. 在兼容的IDE中使用自己的 starter 也是验证元数据质量的好主意.
28.5.3. autoconfigure
模块
autoconfigure
模块包含类库开始使用所需的所有内容. 它还可以包含配置 key 定义 (例如 @ConfigurationProperties
) 和任何可用于进一步自定义组件初始化方式的回调接口.
您应该将类库的依赖标记为可选,以便您可以更轻松地在项目中包含 autoconfigure 模块. 如果以这种方式执行,则不提供类库,默认情况下,Spring Boot 将会退出.
|
Spring Boot 使用注解处理器来收集元数据文件 (META-INF/spring-autoconfigure-metadata.properties
) 中自动配置的条件. 如果该文件存在,则用于快速过滤不匹配的自动配置,缩短启动时间. 建议在包含自动配置的模块中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>
如果您直接在应用程序中定义了自动配置,请确保配置 spring-boot-maven-plugin
,以防止 repackage
goal 将依赖添加到 fat jar 中:
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
使用 Gradle 4.5 及更早版本时,应在 compileOnly
配置中声明依赖,如下所示:
dependencies {
compileOnly "org.springframework.boot:spring-boot-autoconfigure-processor"
}
使用 Gradle 4.6 或者更高的版本,应在 annotationProcessor
配置中声明依赖,如下所示:
dependencies {
annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
28.5.4. Starter 模块
starter 真的是一个空 jar. 它的唯一目的是为使用类库提供必要的依赖. 您可以将其视为使用类库的一切基础.
不要对添加 starter 的项目抱有假设想法. 如果您自动配置的库经常需要其他 starter,请一并声明它们. 如果可选依赖的数量很多,则提供一组适当的默认依赖可能很难,因为您本应该避免包含对常用库的使用不必要的依赖. 换而言之,您不应该包含可选的依赖.
无论哪种方式,您的 starter 必须直接或间接引用 Spring Boot 的 core starter (spring-boot-starter ) (如果您的 starter 依赖于另一个 starter ,则无需添加它) . 如果只使用自定义 starter 创建项目,则 Spring Boot 的核心功能将通过 core starter 来实现.
|
29. Kotlin 支持
Spring Boot 通过利用其他 Spring 项目 (如 Spring Framework、Spring Data 和 Reactor) 的支持来提供 Kotlin 支持. 有关更多信息,请参阅 Spring Framework Kotlin 支持文档 .
开始学习 Spring Boot 和 Kotlin 最简单方法是遵循这个 全面教程. 您可以通过 start.spring.io 创建新的 Kotlin 项目. 如果您需要支持,请免费加入 Kotlin Slack 的 #spring 频道或使用 Stack Overflow 上的 spring
和 kotlin
标签提问.
29.1. 要求
Spring Boot 支持 Kotlin 1.3.x. 要使用 Kotlin,classpath 下必须存在 org.jetbrains.kotlin:kotlin-stdlib
和 org.jetbrains.kotlin:kotlin-reflect
. 也可以使用 kotlin-stdlib
的变体 kotlin-stdlib-jdk7
和 kotlin-stdlib-jdk8
.
由于 Kotlin 类默认为 final,因此您可能需要配置 kotlin-spring 插件以自动打开 Spring-annotated
类,以便可以代理它们.
在 Kotlin 中序列化/反序列化 JSON 数据需要使用 Jackson 的 Kotlin 模块. 在 classpath 中找到它时会自动注册. 如果 Jackson 和 Kotlin 存在但 Jackson Kotlin 模块不存在,则会记录警告消息.
如果在 start.spring.io 上创建 Kotlin 项目,则默认提供这些依赖和插件. |
29.2. Null 安全
Kotlin 的一个关键特性是 null-safety. 它在编译时处理空值,而不是将问题推迟到运行时并遇到 NullPointerException
. 这有助于消除常见的错误来源,而无需支付像 Optional
这样的包装器的成本. Kotlin 还允许使用有可空值的,如 Kotlin null
安全综合指南中所述.
虽然 Java 不允许在其类型系统中表示 null
安全,但 Spring Framework、Spring Data 和 Reactor 现在通过易于使用的工具的注解提供其 API 的安全性. 默认情况下,Kotlin 中使用的 Java API 类型被识别为放宽空检查的 平台类型. Kotlin 对 JSR 305 注解 的支持与可空注解相结合,为 Kotlin 中 Spring API 相关的代码提供了空安全.
可以通过使用以下选项添加 -Xjsr305
编译器标志来配置 JSR 305 检查: -Xjsr305={strict|warn|ignore}
. 默认行为与 -Xjsr305=warn
相同. 在从 Spring API 推断出的 Kotlin 类型中需要考虑 null 安全的 strict
值,但是应该使用 Spring API 可空声明甚至可以在次要版本之间发展并且将来可能添加更多检查的方案.
29.3. Kotlin API
29.3.1. runApplication
Spring Boot 提供了使用 runApplication<MyApplication>(*args)
运行应用程序的惯用方法,如下所示:
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
这是 SpringApplication.run(MyApplication::class.java, *args)
的替代方式. 它还允许自定义应用程序,如下所示:
runApplication<MyApplication>(*args) {
setBannerMode(OFF)
}
29.3.2. 扩展
Kotlin 扩展 提供了使用附加功能扩展现有类的能力. Spring Boot Kotlin API 利用这些扩展为现有 API 添加新的 Kotlin 特定便利.
提供的 TestRestTemplate
扩展类似于 Spring Framework 为 RestOperations
提供的. 除此之外,扩展使得利用 Kotlin reified 类型参数变为可能.
29.4. 依赖管理
为了避免在 classpath 上混合不同版本的 Kotlin 依赖,Spring Boot会导入Kotlin BOM.
使用Maven,可以通过 kotlin.version
属性自定义Kotlin版本,并且为 kotlin-maven-plugin
提供了插件管理. 使用Gradle,Spring Boot插件会自动将 kotlin.version
与Kotlin插件的版本保一致.
Spring Boot还通过导入Kotlin Coroutines BOM管理Coroutines依赖的版本. 可以通过 kotlin-coroutines.version
属性自定义版本.
如果在 start.spring.io 上构建的 Kotlin 项目有至少一个响应式依赖,则默认提供 org.jetbrains.kotlinx:kotlinx-coroutines-reactor 依赖.
|
29.5. @ConfigurationProperties
@ConfigurationProperties 目前仅适用于 lateinit 或可空的 var 属性 (建议使用前者) ,因为尚不支持由构造函数初始化的不可变类.
与 @ConstructorBinding
结合使用时,@ConfigurationProperties
支持具有不变 val
属性的类,如以下示例所示:
@ConstructorBinding
@ConfigurationProperties("example.kotlin")
data class KotlinExampleProperties(
val name: String,
val description: String,
val myService: MyService) {
data class MyService(
val apiToken: String,
val uri: URI
)
}
29.6. 测试
虽然可以使用 JUnit 4 来测试 Kotlin 代码,但建议使用 JUnit 5. JUnit 5 允许测试类实例化一次,并在所有类的测试中复用. 这使得可以在非静态方法上使用 @BeforeAll
和 @AfterAll
注解,这非常适合 Kotlin.
JUnit 5是默认的,并且提供了vintage引擎与JUnit 4向后兼容. 如果不使用它,请排除 org.junit.vintage: junit-vintage-engine
. 您还需要将 测试实例生命周期切换为 "per-class".
要模拟Kotlin类,建议使用MockK. 如果您需要与Mockito特定的@MockBean和@SpyBean注解相对应的Mockk,则可以使用SpringMockK,它提供类似的@MockkBean和@SpykBean注解.
要使用 JUnit 5,请从 spring-boot-starter-test
中排除 junit:junit 依赖,然后添加 JUnit 5 依赖,并相应地配置 Maven 或 Gradle 插件. 有关更多详细信息,请参阅 JUnit 5 文档. 您还需要将测试实例生命周期切换为 per-class.
为了模拟 Kotlin
类,建议使用 Mockk
. 如果需要 MockK 等效的 Mockito 特定的 @MockBean
和 @SpyBean
注解,则可以使用 SpringMockK,它提供类似的 @MockkBean
和 @SpykBean
注解.
29.7. 资源
29.7.1. 进阶阅读
-
Kotlin Slack (有专用的 #spring 频道)
29.7.2. 示例
-
spring-boot-kotlin-demo: 常规的 Spring Boot + Spring Data JPA 项目
-
mixit: Spring Boot 2 + WebFlux + Reactive Spring Data MongoDB
-
spring-kotlin-fullstack: WebFlux Kotlin 全栈示例,在前端使用 Kotlin2js 代替 JavaScript 和 TypeScript
-
spring-petclinic-kotlin: Spring PetClinic 示例应用的 Kotlin 版本
-
spring-kotlin-deepdive: 将Boot 1.0 + Java 逐步迁移到 Boot 2.0 + Kotlin
-
spring-boot-coroutines-demo: Coroutines 示例程序
30. 下一步
如果您想了解本节中讨论的任何类目的更多信息,可以查看 Spring Boot API 文档 ,也可以直接浏览 源代码 . 如果您有具体问题,请查看 how-to 部分.
如果您对 Spring Boot 的核心功能感到满意,可以继续阅读有关t 生产就绪功能的内容.