我们的项目是单体项目,使用的是springboot的框架,随着对接的外部服务越来越多,配置文件越来越臃肿。。我们将对接的外部服务的代码单独抽离出来形成service依赖,之后以jar包的形式引入,这时候外部服务配置放到哪里算是个难题了,我主张将配置文件附着在service依赖中,这样主项目的配置文件将会非常整洁。这里举个例子,A项目是主项目,B、C两个项目分别是对接外部服务B、C的Service项目,我将对接B的配置文件放到B项目,将对接C项目的配置文件放到C项目,A直接引入B、C的依赖即可直接使用,不用在A项目中再单独配置对接B、C项目的配置了。

要想实现上面的功能,需要使用到SpringBoot的扩展点功能EnvironmentPostProcessor

一、EnvironmentPostProcessor的使用

官方文档:https://docs.spring.io/spring-boot/docs/2.5.2/reference/htmlsingle/#howto.application.customize-the-environment-or-application-context

该类的作用是在SpringBoot项目启动之前自定义环境变量,可以在项目启动之前从非标准springboot配置文件中读取相关的配置并填充到springboot上下文中。

1.实现EnvironmentPostProcessor 接口

对于properties文件

public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

    @Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
} private PropertySource<?> loadYaml(Resource path) {
Assert.isTrue(path.exists(), () -> "Resource " + path + " does not exist");
try {
return this.loader.load("custom-resource", path).get(0);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to load yaml configuration from " + path, ex);
}
} }

对于yaml文件

@Slf4j
@Order
public class YamlExtPluginProcessor implements EnvironmentPostProcessor { private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); @Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
Resource path = new ClassPathResource("application-AAA.yaml");
if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exists");
}
try {
List<PropertySource<?>> load = loader.load("application-AAA", path);
log.info("发现了{}个配置文件", load.size());
for (PropertySource<?> propertySource : load) {
environment.getPropertySources().addLast(propertySource);
}
log.info("已加载 {} 配置文件", "application-AAA.yaml");
} catch (IOException e) {
throw new IllegalArgumentException("Failed to load yaml configuration from " + path, e);
}
}
}

2.在resources资源文件夹中新建META-INF/spring.factories文件

填充内容

org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

如果只是主项目中需要配置额外的配置文件,只需要做到这里就能满足需求了,但是在我的使用场景中,并不能满足需求,我的需求是A外部依赖B、C,而这些配置要放到B、C,B和C不可运行,只是Service依赖,尽管大多数的使用都一样,但是还是有所不同。

二、外部依赖式配置

A项目resources目录

│  application-A.yaml

└─META-INF
spring.factories

B项目resources目录

│  application-B.yaml

└─META-INF
spring.factories

然后分别在A、B项目中实现EnvironmentPostProcessor接口读取相关的配置文件,并注册到spring.factories文件即可。

1.配置文件名字问题

配置文件名一定要保持唯一,这里在resources目录下新建application-xxx.properties配置文件,xxx对应着项目名,这样好记还能保持唯一性。如果配置文件名不唯一又会如何呢?如果配置文件名字都写作application-plugin.yaml,A项目有一个,B项目也有一个,则如果A项目中的先生效了,B项目中的配置文件将会被直接忽略。所以配置文件名字不能有重复的。

关于配置文件的加载先后顺序和位置问题,可以参考文档:https://blog.csdn.net/J080624/article/details/80508606

官方文档:https://docs.spring.io/spring-boot/docs/2.5.2/reference/htmlsingle/#features.external-config

2.EnvironmentPostProcessor优先级问题

官方文档中对于优先级问题有这么个提示,大意是我们读取了配置并将其放到了配置的最后,或许应当定义一个优先级以让配置在合适的情况下生效。

我的需求里,B项目和C项目的依赖中并不是放了所有的对接B、C服务的配置,而是大部分不可变的配置放到B、C,比如请求B/C服务的url;少部分不同环境不同配置的配置项放到可变的主项目的配置中,比如请求的认证信息,测试环境和生产环境不一样,那就要分别放到A项目的测试环境配置、生产环境配置文件中。

我需要B、C项目中没有但是A项目中有的配置,要全部配置一起生效;B、C项目中有的配置,A项目中也有的配置,要A项目中的生效。

B、C作为一个配角,可不能抢了主角A的戏。

解决方法就是什么都不做,或者只是加一个@Order注解到EnvironmentPostProcessor实现类上,使用默认最低的优先级;如果使用了最高的优先级,则会“喧宾夺主”,B和C项目会覆盖主项目A中的同名配置。

三、其它引入外部配置的方法

其实说起来很简单,只需要使用

spring:
profiles:
include: B,C

该配置将需要的外部配置文件引入进来即可,但是有局限性

  1. 需要外部配置文件的位置放到resources目录下并且配置文件名一定得是application-xxx.properties,符合springboot的命名规范才行,当然配置文件名字也不能一样
  2. 需要手动修改主项目A的配置,这个需要使用者反编译引入的jar包才能知道该如何做,增加了使用的复杂度

所以还是使用EnvironmentPostProcessor扩展点最好,使用者只需要引入jar包依赖,理想情况下什么都不需要配置就可以直接使用了。

巧用SpringBoot扩展点EnvironmentPostProcessor的更多相关文章

  1. SpringBoot扩展点EnvironmentPostProcessor

    一.背景 之前项目中用到了Apollo配置中心,对接Apollo配置中心后,配置中心的属性就可以在程序中使用了,那么这个是怎么实现的呢?配置中心的属性又是何时加载到程序中的呢?那么我们如果找到了这个是 ...

  2. 三万字盘点Spring/Boot的那些常用扩展点

    大家好,我是三友. Spring对于每个Java后端程序员来说肯定不陌生,日常开发和面试必备的.本文就来盘点Spring/SpringBoot常见的扩展点,同时也来看看常见的开源框架是如何基于这些扩展 ...

  3. Spring Boot 中如何使用 Dubbo Activate 扩展点

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 公司的核心竞争力在于创新 – <启示录> 』 继续上一篇:< Spri ...

  4. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  5. Spring扩展点-v5.3.9

    Spring 扩展点 **本人博客网站 **IT小神 www.itxiaoshen.com 官网地址****:https://spring.io/projects/spring-framework T ...

  6. Spring系列14:IoC容器的扩展点

    Spring系列14:IoC容器的扩展点 回顾 知识需要成体系地学习,本系列文章前后有关联,建议按照顺序阅读.上一篇我们详细介绍了Spring Bean的生命周期和丰富的扩展点,没有阅读的强烈建议先阅 ...

  7. Rafy 框架 - 插件级别的扩展点

    本章说明如何使用额外的插件(如客户化插件)对另一插件(如产品插件)进行扩展.   使用场景 在 产品线工程 中,项目的研发分为领域工程和应用工程.这个过程中会需要对领域工程中的内容进行大量的扩展.   ...

  8. 玩转Asp.net MVC 的八个扩展点

    MVC模型以低耦合.可重用.可维护性高等众多优点已逐渐代替了WebForm模型.能够灵活使用MVC提供的扩展点可以达到事半功倍的效果,另一方面Asp.net MVC优秀的设计和高质量的代码也值得我们去 ...

  9. Asp.net MVC 的八个扩展点

    http://www.cnblogs.com/richieyang/p/5180939.html MVC模型以低耦合.可重用.可维护性高等众多优点已逐渐代替了WebForm模型.能够灵活使用MVC提供 ...

随机推荐

  1. linux rpm包解压

    rpm2cpio xxx.rpm | cpio -div

  2. Rust trait

    Rust trait Rust中的trait类似于Java中的接口,定义了一组可以被类型选择实现的"契约"或共享行为,. 特征定义: trait Playable{ fn play ...

  3. python基础之面向对象(三))(实战:烤地瓜(SweetPotato))

    一.分析"烤地瓜"的属性和方法 示例属性如下: cookedLevel : 这是数字:0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的 ...

  4. readlink 函数用法 -(转自 JK198310的专栏)

    相关函数: stat, lstat, symlink 表头文件: #include <unistd.h> 定义函数:ssize_t readlink(const char *path, c ...

  5. IT行业新闻事件

    台积电: http://www.eefocus.com/component/394512 新闻合集: https://mail.qq.com/cgi-bin/frame_html?sid=q3Mhqr ...

  6. Redis SWAPDB 命令背后做了什么

    Redis SWAPDB 命令背后做了什么 目录 Redis SWAPDB 命令背后做了什么 0x00 摘要 0x01 SWAPDB 基础 1.1 命令说明 1.2 演示 0x02 预先校验 0x03 ...

  7. Linux将一个文件夹或文件夹下的所有内容复制到另一个文件夹

    Linux将一个文件夹或文件夹下的所有内容复制到另一个文件夹     1.将一个文件夹下的所有内容复制到另一个文件夹下 cp -r /home/packageA/* /home/cp/packageB ...

  8. (转)修改python默认排序方式

    在Java中,自定义类可以通过继承comparable接口,重写compareTo方法来使用内置sort()函数来对自定义对象排序,我就在想Python中有没有类似的操作. 首先随便写个自定义类,比如 ...

  9. Sqlserver语句获取本周、上一周、本月数据

    sql语句获取本周.上一周.本月数据 获取周数据 1 本周 2 select * from table1 where datediff(week,时间字段,getdate()) = 0 3 上周 4 ...

  10. 树莓派3B+常用操作

    安装Docker 替换成清华源 清华源地址https://mirrors.tuna.tsinghua.edu.cn/help/docker-ce/ echo "deb [arch=armhf ...