天啦!竟然从来没有人讲过 SpringBoot 支持配置如此平滑的迁移

SpringBoot 是原生支持配置迁移的,但是官方文档没有看到这方面描述,在源码中才看到此模块,spring-boot-properties-migrator,幸亏我没有跳过。看到这篇文章的各位,可算是捡到宝了,相信你继续往下看下去,定会忍不住点赞、收藏、关注。

效果

先放个效果吸引你 :)

从 SpringBoot 2.0.0 版本开始,配置服务上下文,不支持 server.context-path,而需要server.servlet.context-path配置。但是只要加上以下一个官方依赖,就可以支持使用 server.context-path

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-properties-migrator</artifactId>
    </dependency>

server.context-path 所对应的属性 ServerProperties#contextPath 在 Java 代码中已不存在,server.servlet.context-path 所对应的的属性在内部类 Servlet 中才有,为何加了此依赖就能实现如此神奇的效果呢。

原理

SpringBoot 对外部化配置原生支持迁移功能,所谓迁移,具体是指对应配置的属性名变动,仍可以使用原来的属性名配置。
spring-configuration-metadata.json 的信息可以辅助 IDE 进行配置的提示,也可以用来完成配置的迁移。非常的简单。

相关文章: SpringBoot 配置提示功能

通过阅读代码,获得以下信息:

  1. 监听 ApplicationPreparedEvent 事件(即:环境已准备事件),执行以下操作并收集信息
  2. classpath*:/META-INF/spring-configuration-metadata.json 中载入所有配置
  3. 从上下文的 environment 中过滤出提示的配置(满足条件:1. deprecation 不为 null,且提示 level 为 error)
  4. 判断是否兼容(兼容条件见下一节),提取出兼容的属性
  5. 将 value 对应到 replacement 的 key,并将其属性源命名为:migrate-原名
  6. 将配置迁移的新属性源添加到 environment 中,且添加到原属性源之前(优先级高)。
  7. 监听事件:ApplicationReadyEvent(应用上下文已准备) 或 ApplicationFailedEvent(应用启动失败),打印以上步骤收集的遗留配置信息。以 warn 级别打印兼容的配置,以 error 级别打印不兼容的配置

配置兼容条件

根据元数据中定义的 type 判断

  1. 如果旧类型、新类型其中之一为 null(元数据中未指定),则不兼容
  2. 如果两个类型一样,兼容
  3. 如果新类型是 Duration,而旧类型是 Long 或 Integer,则兼容
  4. 其他情况视为不兼容
  5. environment 中取配置信息,理论上支持 SpringBoot 所有的配置方式

效果

兼容效果:
弃用属性(如果还存在)与替换后的属性都会使用配置文件中的弃用的属性名所对应的的值。

总结

使用配置迁移功能,需要以下步骤:

  1. 引入依赖:spring-boot-properties-migrator(支持配置迁移)、spring-boot-configuration-processor(生成元数据文件,如果已经有完整的,不需要此依赖)
  2. 元数据文件spring-configuration-metadata.json 中弃用属性名对应的 properties 中必须有 deprecation(在additional-spring-configuration-metadata.json 中添加,相关文章: SpringBoot 配置提示功能
  3. deprecation 中需指定 levelerror
  4. deprecation 中需指定 replacement
  5. replacement 对应的属性配置在元数据文件中存在,与弃用属性兼容

经典示例之配置上下文

再说回一开始展示的配置上下文示例。

# 配置 servlet 服务上下文
server:
  context-path: test

从 SpringBoot 2.0.0 版本开始,以上配置不支持,点到配置元数据文件中(spring-configuration-metadata.json),发现如下信息:

{
  "properties": [
    {
      "name": "server.context-path",
      "type": "java.lang.String",
      "description": "Context path of the application.",
      "deprecated": true,
      "deprecation": {
        "level": "error",
        "replacement": "server.servlet.context-path"
      }
    },
    {
      "name": "server.servlet.context-path",
      "type": "java.lang.String",
      "description": "Context path of the application.",
      "sourceType": "org.springframework.boot.autoconfigure.web.ServerProperties$Servlet"
    }

替换属性名为:server.servlet.context-path,此属性在org.springframework.boot.autoconfigure.web.ServerProperties 中,且在类中可以发现,server.context-path 所对应的属性 ServerProperties#contextPath 在代码中已不存在,而是在内部类 Servlet 中有,也就是对应 server.servlet.context-path 的属性才有。

但是其满足配置兼容的条件,为什么实际上使用却好像不兼容呢?
其实是因为没有引入依赖,当引入依赖,就会发现此方式配置可以起作用。

示例之两种属性都存在

代码示例见 https://gitee.com/lw888/spring-boot-source-example/tree/master/properties-migrator

1、引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>

2、Java 配置
此处故意保留弃用属性

@Data
@Configuration
@ConfigurationProperties(prefix = "my")
public class MyProperties {
  /** the project name */
  private String name;

  private App app;

  @Data
  public static class App {
    private String name;
  }
}

3、元数据配置,spring-configuration-metadata.json 由程序生成,自定义配置放在 additional-spring-configuration-metadata.json

{
  "properties": [
    {
      "name": "my.name",
      "type": "java.lang.String",
      "description": "the project name.",
      "deprecation": {
        "reason": "test the properties-migrator feature.",
        "replacement": "my.app.name",
        "level": "error"
      }
    },
    {
      "name": "my.app.name",
      "type": "java.lang.String",
      "sourceType": "com.lw.properties.migrator.config.MyProperties$App",
      "description": "the project name."
    }
  ]
}

4、在 properties 或 yml 文件中配置

my:
  name: lw
  app:
    name: app

5、打印配置信息

@Slf4j
@SpringBootApplication
public class PropertiesMigratorApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext context =
        SpringApplication.run(PropertiesMigratorApplication.class, args);
    MyProperties myProperties = context.getBean(MyProperties.class);
    log.info("myProperties.name:{}", myProperties.getName());
    log.info(
        "myProperties$app.name:{}",
        Optional.ofNullable(myProperties.getApp()).orElse(new App()).getName());
  }
}

6、打印信息如下:

2019-11-23 21:42:09.580 WARN 109408 --- [ main] o.s.b.c.p.m.PropertiesMigrationListener :
The use of configuration keys that have been renamed was found in the environment:

Property source ‘applicationConfig: [classpath:/application.yml]‘:
Key: my.name
Line: 4
Replacement: my.app.name
Key: server.context-path
Line: 2
Replacement: server.servlet.context-path

Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.
......... myProperties.name:lw
......... myProperties\(app.name:lw ......... serverProperties\)servlet.contextPath:/app

7、效果解析
在 yml 中弃用属性名优先级更高,弃用属性与新属性都使用此弃用属性名对应的值。

参考资料

SpringBoot 2.2.1.RELEASE 源码
公众号:逸飞兮(专注于 Java 领域知识的深入学习,从源码到原理,系统有序的学习)

原文地址:https://www.cnblogs.com/lw5946/p/11933190.html

时间: 2024-10-18 01:50:41

天啦!竟然从来没有人讲过 SpringBoot 支持配置如此平滑的迁移的相关文章

给统计人讲Python(2)_Pandas入门

#Pandas官方文档:http://pandas.pydata.org/pandas-docs/stable/ #-*-author Yangami-*- import pandas as pd import numpy as np 一.基本类型 用列表.数组和字典构建series lst = list('abcedfg') # 列表 arr = np.arange(7) # 数组 # 构建方法 ser1 = pd.Series(lst) ser2 = pd.Series(arr) #一维序列

python学习第二讲,pythonIDE介绍以及配置使用

目录 python学习第二讲,pythonIDE介绍以及配置使用 一丶集成开发环境IDE简介,以及配置 1.简介 2.PyCharm 介绍 3.pycharm 的安装 二丶IDE 开发Python,以及配置IDE 1.初始化IDE 2.打开python项目. 3.pycharm设置python的默认解释器版本. 4.pycharm设置为所有用户可以使用,以及启动pycharm 5.设置pycharm启动图标(快捷方式) 6.卸载之前的pycharm python学习第二讲,pythonIDE介绍

阿里牛人讲谈(一)

数据可视化产品: 以下是演讲实录: 赵昆:大家好,我叫赵昆,我在淘宝的花名叫昆无.很高兴我给大家做这样一个分享,因为今天这个时间是1:30,我觉得是一个很困难的时间,因为很多人在这个时间比较困,特别是大家刚刚吃饱了饭,基于这么一种现状,给大家提供一个招数,我曾经用过的一个招数,如果他们觉得听我讲的很枯燥,很没有意思,或者想睡觉,可以向我一样,假装拿出一个手机,接一个电话,到会场外面透一口气,这样可以给我留一点面子,我也少一点压力,也很希望,我这次分享不会让大家出现这种局面. 我们回到整体,就是淘

给统计人讲Python(4)_股票数据处理

本地代码是.ipynb格式的转换到博客上很麻烦,这里展示部分代码,了解更多可以查看我的git-hub:https://github.com/Yangami/Python-for-Statisticians/tree/master/Numpy 股票数据分析 任务: 1)从股票历史数据中取出目标股票的目标时间段数据 2)组织目标时间段数据并计算相关指标 3)将目标数据的相关指标输出到文本文件和csv文件 #导入numpy.pandas import pandas as pd import numpy

第36讲 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景

在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种数据库自身体系结构的理解.今天这一讲,作为补充 Java 面试考察知识点的完整性,关于数据库的应用和细节还需要在实践中深入学习.今天我要问你的问题是,谈谈 MySQL 支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?典型回答所谓隔离级别(Isolation Level),就是在数据库事务中,

Struts2 第四讲 -- Struts2的基本配置

5.struts2的基本配置 5.1 struts2的访问连接url 在struts1中,通过<action path=“/primer/helloWorldAction.action”>节点的path属性指定访问该action的URL路径. 在struts2中,访问struts2中action的URL路径由两部份组成:包的命名空间+action的名称 例如: 访问本例子HelloWorldAction的URL路径为: /primer/helloWorldAction.action (注意:完

BGP(一)——原理精讲及内外部邻居配置

BGP概述 一.BGP/BGP4:Border Gateway Protocol,边界网关协议 是一种基于距离矢量算法的自治系统之间的路由. 二.BGP并非要找到具体的网络信息,而是提供可以用与找到自治系统的信息. 而运行于自治系统内部的路由协议,用于找到具体的网络. BGP作用(特点) 作用: 在不同的大型网络之间 , 长时间以稳定的方式传输大量的路由 并且能够实现路由的灵活控制,同时确保无环 特点: (1) 传输路由稳定 BGP使用TCP协议,端口号179. BGP启动时传播整个路由表,之后

JAVA入门到精通-第54讲-sql server安装配置和卸载

Sybase在UNIX操作系统上用的更好: Windows身份认证: 只要能登陆到windows就认为是合法用户: 混合模式: 不但能进windows,还需要用户名和密码: sa 超级管理员,权限最高的:密码越复杂越好: 不打补丁,1433端口会不被监听: 前面是解压缩,安装: 补丁:一款软件发布后, 发现有安全漏洞或者软件缺陷,升级的效果: 服务管理器: 把勾勾去掉: 原文地址:https://www.cnblogs.com/xuxaut-558/p/10035677.html

推荐一个集成环境 XAMPP

摘自:http://blog.sina.com.cn/s/blog_72c4b92501012ll7.html 一个新手接触 Joomla! 的过程应该是这样的:看到这个词之后首先要弄明白“什么是Joomla!”,然后就想“试用一下看看”.可是,Joomla! 是 web 程序,它需要有服务器环境才能运行.如果你还不了解 Joomla! 是否合你意,就贸然去购买服务器空间来测试,恐怕你是不会愿意的.因此,对于一个迫切想要测试Joomla!如何安装.运行.操作的新手来说,最方便的莫过于在自己的 P