一、需求
我本地项目想直接连接测试环境nacos,由于nacos中的配置信息我不能修改,否则测试环境会出问题。但是我还想改配置怎么办呢?
可以在本地springboot项目启动拿到nacos配置时修改。
二、举个例子
例如:测试环境nacos中配置的server.tomcat.basedir配置(tomcat临时目录),在我本地无法创建相应的文件夹,导致无法启动项目,所以我就只能修改server.tomcat.basedir的值(还不能修改nacos云配置)。
无法创建对应目录,如图:
项目启动报错,如图:
三、解决办法
第一种方法,借助idea的断点调试功能进行修改
1、我们找到NacosPropertySourceBuilder类的loadNacosData方法,把断点打到如图位置
2、使用Evaluate执行dataMap.put()方法把值重新覆盖就可以了。
以上操作,只适合临时启动用,每次启动都要修改一下,很麻烦。可以使用第二种方法。
第二种方法,使用代码修改
1、先自定义一个类实现SpringApplicationRunListener接口。
SpringApplicationRunListener是springboot的生命周期,不懂可以参考:springboot扩展点_现实、太残忍的博客-CSDN博客_springboot扩展点
@Slf4j
public class LocalHostSpringApplicationRunListener implements SpringApplicationRunListener {
/**
* 必须添加参数为SpringApplication application和String[] args构造方法,否则启动报错
* @param application
* @param args
*/
public LocalHostSpringApplicationRunListener(SpringApplication application, String[] args) {
}
@Override
public void starting() {
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
}
/**
* 环境上下文准备完成时
* @param context the application context
*/
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
//判断上下文环境是ServletWebServer环境
if(context instanceof AnnotationConfigServletWebServerApplicationContext){
//从上下文环境中获取总配置信息
MutablePropertySources mutablePropertySources = context.getEnvironment().getPropertySources();
//获取nacos配置信息
CompositePropertySource bootstrapPropertiesCompositePropertySource = (CompositePropertySource)mutablePropertySources.get("bootstrapProperties");
//获取nacos配置信息中的配置信息(包装了好几层)
for (PropertySource compositePropertySource : bootstrapPropertiesCompositePropertySource.getPropertySources()) {
for (PropertySource nacosPropertySource : ((CompositePropertySource) compositePropertySource).getPropertySources()) {
//最终拿到对应的配置文件信息
NacosPropertySource nacosPropertySourceTarget = (NacosPropertySource) nacosPropertySource;
//找到你要修改的文件名称(dataId)
if("sys-test.properties".equals(nacosPropertySourceTarget.getDataId())){
//修改值
Map<String, Object> source = nacosPropertySourceTarget.getSource();
source.put("server.tomcat.basedir","/tmp");
log.info("已修改server.tomcat.basedir为/tmp");
}
};
};
}
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
}
@Override
public void started(ConfigurableApplicationContext context) {
}
@Override
public void running(ConfigurableApplicationContext context) {
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
2、在resources文件夹下的META-INF文件夹创建一个spring.factories文件
解释:Spring factories是 Spring Boot 一种解耦的扩展机制,仿照了 Java 中 SPI 的扩展机制,可以实现自动加载可扫描包之外的 Bean。
3、完毕
方法二具体原理:
1、通过断点可以得知,nacos获取配置阶段发生在springboot生命周期的“上下文准备阶段”。
所以是spingboot的prepareContext方法,具体发生在执行applyInitializers方法中。
其中包括一个PropertySourceBootstrapConfiguration类是springcloud的用于远程配置处理的类。
从代码中我们可以看到listeners.contextPrepared(context);,所以我们正好可以利用springboot生命周期的“上下文准备阶段”进行nacos配置的修改。
2、进行SpringApplicationRunListener的springboot生命周期接口实现
ConfigurableApplicationContext是应用的上下文信息,里面包含Environment环境信息。
Environment环境信息里面包含PropertySources配置信息。
因为配置信息有多个(本地配置,nacos配置等),所以我们要找到nacos的配置信息,一层一层获取到map然后修改