Spring Boot 2.4.0正式发布,全新的配置文件加载机制(不向下兼容)

u=2656353677,2997395625&fm=26&gp=0.jpg

✍前言

北京时间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版本发布时那会麽?前后相差将好半年:

image

直达电梯: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停止维护
image

回忆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. 说明:小版本号的升级对于新特性来说一般选择性忽略
    5. 关于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.propertiesapplication.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 Data 2020.0:Spring Boot 2.4.0使用的是最新发布的Spring Data 2020.0
  • 支持Java 15:此版本的Spring Boot完全支持Java 15,最小支持依旧是Java 8
  • 自定义属性名支持:当使用构造函数绑定时,属性的名称需要和参数名称保持一样。如果您想使用Java保留关键字,这可能是一个问题。如下例子:
@ConfigurationProperties(prefix = "sample")
public class SampleConfigurationProperties {

  private final String importValue;

  // import是Java关键字
  public SampleConfigurationProperties(@Name("import") String importValue) {
    this.importValue = importValue;
  }

}

@Name注解是Spring Boot 2.4.0新增的注解,能标注在ElementType.PARAMETER

  • 支持导入无扩展名的配置文件:如果您有这样的需求,现在就可以通过向Spring Boot引导提供关于内容类型的提示来导入这些文件
    • 此版本对Spring Boot的配置文件加载进行了完全重新改造,并且不向下兼容,具体参见下篇文章
  • 新增StartupEndpoint:显示有关应用程序启动的信息。此端点可以帮助您识别启动时间超过预期的bean
    • 此端点依赖于Spring Framework 5.3.0新提供的应用启动追踪新特性。具体可参考ApplicationStartupStartupStep这个两个API是如何做追踪的
  • 新增RedisCacheMetrics:用于监控使用redis时的puts、gets、deletes以及缓存命中率等信息
    • 此指标信息默认不开启,需你增加配置spring.cache.redis.enable-statistics = true
  • 新增些Web配置项spring.web.locale、spring.web.locale-resolver、spring.web.resources.*、management.server.base-path,这些属性既支持Servlet也支持WebFlux
    • 对应的只能用于 Spring MVC或servelt下配置项spring.mvc.locale/spring.mvc.locale-resolver/spring.resources.*/management.server.servlet.context-path均以标注为过期
  • 支持Flyway 7:这个版本升级到Flyway 7,带来了一些额外的属性。如:spring.flyway.url/user/password(开源版本);spring.flyway.cherry-pick/jdbc-properties...(团队版本)
  • H2数据库控制台支持配置密码:可通过spring.h2.console.settings.web-admin-password属性配置通过密码访问H2控制台
  • 增强的错误分析器FailureAnalizers:现在即使你还没有创建ApplicationContext,FailureAnalizers都会生效来帮你定位错误位置
  • 处理/标注Spring Boot 2.2和2.3中过期项:按照Spring Boot的版本兼容性政策,在2.2版本已被标记为@Deprecated的在2.4.0版本会被删除,在2.3版本中被标记为@Deprecated的计划在2.5.0版本中将其移除

✍总结

Spring Boot 2.4.0版本的升级目标,基本和Spring Framework 5.3.0保持一致:为云原生做努力。表现在除了删除些无用类,禁止不需要的类的加载外,重点还会体现在它对配置文件加载机制的重构上,这将是下文的内容,也是本次升级的重头戏,敬请关注。

Spring Boot重写了对配置文件的加载机制,并且新引入了近40个类来处理(老方式仅有区区几个类),可见其重视、重要程度。因此,为了适应未来的发展,你一定要掌握,并且越早越好,下篇将为你揭晓。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345