需求
有个独立的API项目,该项目主要是对外部各个系统提供API接口,为了保证调用的安全,需要对请求进行校验,主要校验包括调用频率,访问IP,是否跨域和Token,其中IP和是否跨域的配置会根据接入方进行相应的修改,为了避免每次有新的接入方就得去修改一次配置文件并重启项目,所以打算使用动态配置的方式。
初级实现方案:API服务每隔5分钟向管理端请求一次数据,管理端添加IP和域白名单的管理,这个实现方案,简单好用,但是弊端也明显,管理端每次修改完配置后,客户端需要等待下次请求后才会加载对应的配置,同时,还需要自己管理获取到的配置文件
更新方案:在springboot启动时,先从远端获取配置文件,并将其加载进Environment对象中,其余的,就都交给Spring了。同时配合spring-cloud-context实现远程配置变更后,本地重新拉取配置并更新
瞎折腾阶段
以下代码基于springboot 2.3.4.RELEASE版本
我们先从run方法开始搞事情
点进去之后,springboot会在这里初始化ConfigurableEnvironment对象
继续向下,由于我这里是启动的SERVLET环境,所以会初始化一个StandardServletEnvironment对象
这里是给ConfigurableEnvironment做一些初始化工作,我们先不管了,重点在这里,listeners.environmentPrepared(environment);,Springboot通过事件,将Environment的加载分发出去
具体的监听器则是配置在spring.factories里面,跟配置加载相关的监听器如图
我们进入到ConfigFileApplicationListener类中,先判断接收到的事件是不是ApplicationEnvironmentPreparedEvent事件,是的话,调用onApplicationEnvironmentPreparedEvent方法,通过loadPostProcessors方法,从spring.factories中读取配置的EnvironmentPostProcessor,通过Order排序后,依次执行
emmm… 跑了这么远,我们要用到的扩展点也就在这儿了,我们只需要自己扩展一个EnvironmentPostProcessor,并注册进去,就好啦
具体实现
- 先定义一个实现类,实现EnvironmentPostProcessor接口
代码中对地址倒序排序是因为注册PropertySources时使用了addAfter方法,导致先加载的使用顺序会靠后,为了使使用顺序与配置文件的书写顺序一致,才在此处对资源顺序进行了反转
package fun.fanx.remote.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.boot.env.RandomValuePropertySource;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropert