Spring Boot 2.4.0正式发布,全新的配置文件加载机制(不向下兼容)
千里之行,始于足下。关注公众号【 BAT的乌托邦 】,有Spring技术栈、MyBatis、JVM、中间件等小而美的 原创专栏 供以免费学习。分享、成长,
✍前言
你好,我是YourBatman。
北京时间2020-11-12,
Spring Boot 2.4.0
正式发布。2.4.0是第一个使用
新版本方案
的Spring Boot发行版本。
注意:2.4.0版本号没有.RELEASE
后缀,没有.RELEASE
后缀,没有.RELEASE
后缀。使用的是Spring最新的版本发布规则。此规则详解请参考上篇文章: Spring改变版本号命名规则:此举对非英语国家很友好
还记得
Spring Boot 2.3.0.RELEASE
版本发布时那会麽?前后相差将好半年:
直达电梯: Spring Boot 2.3.0正式发布:优雅停机、配置文件位置通配符新特性一览
一般来说,次版本号的升级会有点料,根据之前的爆料此次升级据说是做了大量的更新和改进。那么老规矩,作为小白鼠的我先代你玩一玩,初体验吧。
也可参见官方的更新日志: Spring Boot 2.4.0 Release Notes
✍正文
除了刚发布的Spring Boot 2.4.0,Spring Boot 2.3.x/2.2.x仍旧是
活跃的
维护的版本。Spring Boot遵循的是
Pivotal OSS
支持策略,从发布日期起支持主要版本
3年
(注意:是主要版本)。下面是详情: -
2.3.x
:
支持的版本
。2020.05发布,是现在的活跃的主干 -
2.2.x
:
支持的版本
。2019.10发布,是现在的活跃的主干 -
2.1.x
:2018.10发布,支持到2020.10月底,
建议尽快升级
EOL分支
: -
2.0.x
:2018.3发布,2019.4.3停止维护 -
1.5.x
:生命已终止的版本。2017.1发布,是最后一个1.x分支,2019.8.1停止维护
回忆2.3版本的新特性
可能大部分小伙伴都还没用过2.3.x分支,没想到2.4.x就已发布。因此这里先对2.3.x版本的新特性,来波简单回忆: 1. 优雅停机。这是
2.3.x主打的新特性
:在关闭时,web服务器将不再允许新的请求,并将等待完成的请求给个宽限期让它完成。这个宽限期是可以设置的:可以使用
spring.lifecycle.timeout-per-shutdown-phase=xxx
来配置,默认值是30s。 2. 配置文件位置支持通配符。简单的说,如果你有MySql的配置和Redis配置的话,你就可以把他们分开来放置,这个新特性也是棒棒哒。隔离性更好目录也更加清晰了(注意:此格式只支持放在classpath外部): 1. mysql:
/config/mysql/application.properties
2. redis:
/config/redis/application.properties
3. 核心依赖升级。 1. Spring Data Neumann。备注:很明显这个还是旧的命名方式。在Spirng新的版本规则下,Spring Data最新版本为Spring Data 2020.0.0 2. Spring Session Dragonfruit(很明显这个也还是旧的命名方式) 3. Spring Security 5.3 4.
Spring Framework
没有升级,使用的依旧是和Spring Boot 2.2相同的
5.2.x
版本 1. 说明:小版本号的升级对于新特性来说一般选择性忽略 6. 关于
Bean Validation
:从此版本开始,
spring-boot-starter-web
不会再把validation带进来,所以若使用到,你需要自己添加这个
spring-boot-starter-validation
依赖 1. 一般来说建议你手动引入,毕竟Bean Validation的使用还是很广泛,并且真的非常非常好用
做足功课后,就开始最新的Spring Boot 2.4.0之旅吧。
2.4.0主要新特性
全新的配置文件处理(properties/yaml)
这个改变 最为重磅 ,本次改变了配置文件的加载逻辑,旨在 简化 和 合理化 外部配置的加载方式,它可能具有不向下兼容性。
Spring Boot 2.4改变了处理
application.properties
和
application.yml
文件的方式: - 若你只是简单的文件application.properties/yaml,那么升级对你是
无缝
的,你感受不到任何变化 - 若你使用了比较复杂的文件,如
application-profile.properties/yaml
这种(或者使用了Spirng Cloud的配置中心、(带有分隔符----的)多yaml文件),那么默认是不向下兼容的,需要你显式的做出些更改
因为配置文件隶属于程序的一部分,特别是我们现在几乎都会使用到配置中心。因此下面针对于老版本升级到Spring Boot 2.4.0做个简单的迁移指导。
说明:因配置文件加载逻辑完全进行了重写,因此详细版本我放到了下文专文讲解,有兴趣可保持关注
老版本版本配置属性迁移指南
老版本:2.4.0之前的版本都叫老版本。
Spring Boot 2.4对
application.poperties/yaml
的处理做了更新/升级。旨在简化和合理化外部配置的加载方式。它还提供了新功能:
spring.config.import
支持。所以呢,对于Spring Boot 2.4.0之前的版本(老版本)若升级到2.4.0需要做些修改,指导建议如下:
方式一:恢复旧模式(不推荐)
如果你还未准备好做配置迁移的修改,Spring Boot也帮你考虑到了,提供了
一键切换
到旧模式的“按钮”。具体做法是:只需要在
Environment
里增加一个属性
spring.config.use-legacy-processing = true
就搞定。最简的方式就是把这个属性放在application.poperties/yaml里即可。
spring.config.use-legacy-processing = true
增加此配置后,Spring Boot对配置文件的解析恢复到原来模式:仍旧使用
ConfigFileApplicationListener
去解析。
ConfigFileApplicationListener
属于Spring Boot非常核心的底层代码,这次做了不向下兼容的改进,可见它对进击云原生的决心
值得注意的是:此API在2.4.0已被标记为过期:
// @since 1.0.0
// @deprecated since 2.4.0 in favor of {@link ConfigDataEnvironmentPostProcessor}
@Deprecated
public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
}
按照Spring Boot的版本策略,此类将在Spring Boot 2.6.0版本被移除。因此:若不是迫不得已(时间紧急),并不建议你用兼容手法这么去做,因为这将成为技术债,迟早要还的。
说明:很多RD其实只会看到当前的方便,获得利益(比如快速上线获奖),坑交给后人。我个人认为作为程序员应该有一定自我修养,自我追求,不为一时的爽而持续给团队积累债务,毕竟积重难返。
方式二:按新规则迁移(推荐)
若你对配置文件的使用有如下情行,那么你需要做迁移: 1. 多文档的yaml文件(带有----分隔符的文件) 2. 在Jar外使用配置文件,或者使用形如application-{xxx}.properties/yaml这种配置 3. 若在多文档yaml中使用到了
spring.profiles
配置项 4. ...
Spring Boot 2.4.0升级对配置文件的改动是最大的,并且还
不具备向下兼容性
,简单的说就是从此版本开始要把Spring Boot的配置文件加载机制重学一遍(比如还增加了
spring.config.import
,增加了对kubernetes配置的支持等等),并且还要学会如何迁移。
为了更好的描述好这个非常非常重要的知识点,下篇文章我会用专文来全面介绍 Spring Boot这套全新的配置文件加载机制,并且辅以原理,以及和过去方式的比较,帮助你更全面、更快速、更劳的掌握它,欢迎持续关注。
说明:Spring Boot的配置文件加载机制非常非常重要,因为你也知道你平时开发中很大程度实际上是在跟它的配置项打交道。新的配置加载方式比老的更加优秀,适应发展,敬请期待
从spring-boot-starter-test中删除Vintage Engine
Spring Boot 2.2.0
版本开始就引入JUnit 5作为单元测试
默认库
,在此之前,spring-boot-starter-test包含的是JUnit 4的依赖,Spring Boot 2.2.0版本之后替换成了Junit Jupiter(Junit5)。
Vintage Engine属于Junit5的一个模块,它的作用是:允许用JUnit 5运行用JUnit 4编写的测试,从而提供了向下兼容的能力。
从2.2.0到现在经过了2个版本的迭代,到
Spring Boot 2.4.0
这个版本决定了把Vintage Engine从spring-boot-starter-test正式移除。因此:若你的工程仍需要对JUnit4支持,那么请手动引入依赖项(如果工程量不大,强烈建议使用JUnit5,比4好用太多):
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
</exclusion>
</exclusions>
</dependency>
说明:其实在2.4.0之前,若你是从https://start.spring.io
生成的项目其实也是不会带有vintage-engine的。只不过它是通过显式的在pom里通过exclusion标签来排除的
嵌入式数据库检测
改进嵌入式数据库检测机制:仅当数据库在
内存中
时才将其视为嵌入式数据库。所以如果使用H2、HSQL等产品,
但是
你是基于文件的持久性或使用的是服务器模式,那么将不会检测为内存数据库。而对于
非内存
数据库,你可能需要额外做如下动作: 1. sa用户名将不会再被主动设置。所以如果你的数据库需要用户名,请增加配置项:
spring.datasource.username = sa
2. 这种数据库将不会再被自动初始化,若要使用请根据需要更改
spring.datasource.initialization-mode
的值
Logback配置属性
Logback一些配置项改名了,更加表名了它是logback的配置项。
新增了配置类LogbackLoggingSystemProperties
用于对应,它继承自之前的LoggingSystemProperties
之前的配置项有些被废弃(此版本还未删除,后续版本肯定会删除的),对应关系如下:
~~老(已废弃)~~ | 新 -------- | ----- ~~logging.pattern.rolling-file-name~~ | logging.logback.rollingpolicy.file-name-pattern ~~logging.file.clean-history-on-start~~ | logging.logback.rollingpolicy.clean-history-on-start ~~logging.file.max-size~~ | logging.logback.rollingpolicy.max-file-size ~~logging.file.total-size-cap~~ | logging.logback.rollingpolicy.total-size-cap ~~logging.file.max-history~~ | logging.logback.rollingpolicy.max-history
一些属性是被放到system environment里面的:
~~老(已废弃)~~ | 新 -------- | ----- ~~ROLLING_FILE_NAME_PATTERN ~~ | LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN ~~LOG_FILE_CLEAN_HISTORY_ON_START~~ | LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START ~~LOG_FILE_MAX_SIZE~~ | LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE ~~LOG_FILE_TOTAL_SIZE_CAP~~ | LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP ~~LOG_FILE_MAX_HISTORY~~ | LOGBACK_ROLLINGPOLICY_MAX_HISTORY
不再注册DefaultServlet
从Spring Boot 2.4开始,默认将不会再注册
DefaultServlet
。因为在绝大多数的应用中,Spring MVC提供的
DispatcherServlet
是
唯一
需要被注册的Servlet。从源码处感受下这次改动:
AbstractServletWebServerFactory:
// 2.4.0之前版本,默认值是true
private boolean registerDefaultServlet = true;
// 2.4.0以及之后版本,默认值是false
private boolean registerDefaultServlet = false;
当然喽,若你的工程强依赖于此Servelt,那么可以通过此配置项
server.servlet.register-default-servlet = true
把它注册上去。
补课:什么是DefaultServlet?
它是Java EE提供的标准技术,如Tomcat、Jetty等都提供了这个类。简而言之它的作用就是兜底(拦截
/
),当别的servlet都没匹配上时就交给它来处理,一般用于处理静态资源如
.jpg,.html,.js
这类的静态文件。
DefaultServlet
在传统web容器里,会被配置在tomcat目录(此处以tomcat为例)下的
conf/web.xml
里:
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
说明:tomcat下的web.xml对其加载的所有的Application都生效,并且最终和Application自己的web.xml内容合并,遇相同的话后者优先级更高
在Spring Boot 嵌入式容器 里配置是这样的(完全等价于xml配置):
private void addDefaultServlet(Context context) {
Wrapper defaultServlet = context.createWrapper();
defaultServlet.setName("default");
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
defaultServlet.addInitParameter("debug", "0");
defaultServlet.addInitParameter("listings", "false");
defaultServlet.setLoadOnStartup(1);
// Otherwise the default location of a Spring DispatcherServlet cannot be set
defaultServlet.setOverridable(true);
context.addChild(defaultServlet);
context.addServletMappingDecoded("/", "default");
}
值得注意的是:Spring Boot注册的
DispatcherServlet
的path也是
/
(覆盖掉了
DefaultServelt
)。在Spring MVC环境下倘若是静态资源,也不用DefaultServelt费心,Spring MVC专门提供了一个
DefaultServletHttpRequestHandler
用于处理静态资源(虽然最终还是Dispatcher给
defaultServlet
去搞定)。
现在的Spring Boot服务大都是REST服务,并无静态资源需要提供,因此就没有必要启用
DefaultServletHttpRequestHandler
和注册
DefaultServlet
来增加不必要的开销喽。
HTTP traces不再包含cookie头
Http traces默认将不再包含请求头
Cookie
以及响应头
Set-Cookie
。源码处感受一下:
org.springframework.boot.actuate.trace.http.Include:
// 2.4.0版本之前:包含COOKIE_HEADERS这个头
static {
Set<Include> defaultIncludes = new LinkedHashSet<>();
defaultIncludes.add(Include.REQUEST_HEADERS);
defaultIncludes.add(Include.RESPONSE_HEADERS);
defaultIncludes.add(Include.COOKIE_HEADERS);
defaultIncludes.add(Include.TIME_TAKEN);
DEFAULT_INCLUDES = Collections.unmodifiableSet(defaultIncludes);
// 2.4.0版本以及之后:不包含COOKIE_HEADERS这个头
static {
Set<Include> defaultIncludes = new LinkedHashSet<>();
defaultIncludes.add(Include.REQUEST_HEADERS);
defaultIncludes.add(Include.RESPONSE_HEADERS);
defaultIncludes.add(Include.TIME_TAKEN);
DEFAULT_INCLUDES = Collections.unmodifiableSet(defaultIncludes);
}
若你仍旧想保留老的习惯,那么请用配置项
management.trace.http.include = cookies, errors, request-headers, response-headers
自行控制。
Neo4j
这个版本对Neo4j的支持进行了重大调整。直接用源码来说明差异:
Spring Boot 2.4.0之前版本:
@ConfigurationProperties(prefix = "spring.data.neo4j")
public class Neo4jProperties implements ApplicationContextAware { ... }
// 无Neo4jDataProperties配置类
Spring Boot 2.4.0以及之后版本:
@ConfigurationProperties(prefix = "spring.neo4j")
public class Neo4jProperties { ... }
@ConfigurationProperties(prefix = "spring.data.neo4j")
public class Neo4jDataProperties { ... }
其它升级关注点
- Spring Framework 5.3 :Spring Boot 2.4.0使用的是5.3.0主线分支(之前使用的5.2.x或更低)
- Spring Framework 5.3的新特性应该 重点关注 ,请移步我上篇文章: Spring Framework 5.3.0正式发布,在云原生路上继续发力
- Spring Data 2020.0 :Spring Boot 2.4.0使用的是最新发布的Spring Data 2020.0
- 此版本的命名方式不同于之前,是因为使用了Spirng最新的release train命名方式。Spring在2020年4月份发布了最新的版本命名方式,可参考前面这篇文章: Spring改变版本号命名规则:此举对非英语国家很友好
- 支持Java 15 :此版本的Spring Boot完全支持Java 15,最小支持依旧是Java 8
- 自定义属性名支持:当使用 构造函数绑定 时,属性的名称需要和参数名称保持一样。如果您想使用Java保留关键字,这可能是一个问题。如下例子:
@ConfigurationProperties(prefix = "sample")
public class SampleConfigurationProperties {
private final String importValue;