解锁新技能《AnnotationConfigApplicationContext@7a8414ea has been closed already》

news/2025/2/13 2:27:24/

最近工作过程中遇到了标题中的错误,导致整个项目都不可以访问;原因是我修改了consul的配置,修改过后由于consul监控配置开关是打开着的,所以监控到配置做了修改后会重新创建ApplicationContext对象,并且会将之前的ApplicationContext对象销毁调;由于我在项目启动的时候初始化了一个ApplicationContext全局变量,会在项目中AOP记录访问日志的时候使用,所以每个接口都会报标题中的错误;

一、原因分析
  • Consul配置监控类ConfigWatch监控consul配置中心的配置变动,如果配置有变动会触发RefreshEvent事件
	@Timed("consul.watch-config-keys")public void watchConfigKeyValues() {if (!this.running.get()) {return;}for (String context : this.consulIndexes.keySet()) {//********RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);//配置有变动,触发RefreshEvent事件this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));//*****}this.firstTime = false;}
  • RefreshEventListener监听器监听RefreshEvent事件,并创建新的ApplicationContext对象,销毁原来的ApplicationContext对象
	@Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationReadyEvent) {handle((ApplicationReadyEvent) event);}else if (event instanceof RefreshEvent) {//监听RefreshEvent事件handle((RefreshEvent) event);}}public void handle(ApplicationReadyEvent event) {this.ready.compareAndSet(false, true);}public void handle(RefreshEvent event) {if (this.ready.get()) { // don't handle events before app is readylog.debug("Event received " + event.getEventDesc());// 触发ContextRefresher类的refresh方法来创建ApplicationContext对象Set<String> keys = this.refresh.refresh();log.info("Refresh keys changed: " + keys);}}
  • ContextRefresher类新建ApplicationContext并发送EnvironmentChangeEvent事件
	public synchronized Set<String> refresh() {Set<String> keys = refreshEnvironment();this.scope.refreshAll();return keys;}public synchronized Set<String> refreshEnvironment() {Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());updateEnvironment();Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();//触发EnvironmentChangeEvent事件,可以在此事件的监听器来修改重建后的ApplicationContextthis.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));return keys;}
//此方法的实现(LegacyContextRefresher.updateEnvironment)是真正用来新建ApplicationContext和销毁原来ApplicationContext的
protected abstract void updateEnvironment();
  • LegacyContextRefresher.updateEnvironment实现
public class LegacyContextRefresher extends ContextRefresher {@Deprecatedpublic LegacyContextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {super(context, scope);}public LegacyContextRefresher(ConfigurableApplicationContext context, RefreshScope scope,RefreshAutoConfiguration.RefreshProperties properties) {super(context, scope, properties);}@Overrideprotected void updateEnvironment() {addConfigFilesToEnvironment();}/* For testing. */ ConfigurableApplicationContext addConfigFilesToEnvironment() {ConfigurableApplicationContext capture = null;try {//*****************省略*******************SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class).bannerMode(Banner.Mode.OFF).web(WebApplicationType.NONE).environment(environment);// Just the listeners that affect the environment (e.g. excluding logging// listener because it has side effects)builder.application().setListeners(Arrays.asList(new BootstrapApplicationListener(), new BootstrapConfigFileApplicationListener()));// 新建ApplicationContext对象,实际会进入org.springframework.boot.builder.SpringApplicationBuilder#run方法创建;capture = builder.run();//*************省略**********************}finally {ConfigurableApplicationContext closeable = capture;while (closeable != null) {try {//closeable.close();}catch (Exception e) {// Ignore;}if (closeable.getParent() instanceof ConfigurableApplicationContext) {closeable = (ConfigurableApplicationContext) closeable.getParent();}else {break;}}}return capture;}}

上述builder.run方法会进入org.springframework.boot.builder.SpringApplicationBuilder#run最终是在org.springframework.boot.SpringApplication#run(java.lang.String…)方法中创建:

二、解决方案

在原因分析中我们知道新建ApplicationContext后会发送EnvironmentChangeEvent事件,而EnvironmentChangeEvent事件会接收一个ApplicationContext作为参数,我们的解决方案就是监听EnvironmentChangeEvent事件,修改定义的全局变量;

方案一:使用@RabbitListener注解监听EnvironmentChangeEvent事件
    @EventListenerpublic void handler(EnvironmentChangeEvent event){ApplicationContext context = (ApplicationContext) event.getSource();//修改定义的全局ApplicationContext对象IOCContext.setCONTEXT(context);System.out.println(event.getSource());}

此方案适用于具体项目之中,如果想用在springboot脚手架类SDK中,这种方案就行不通了;

方案二:定义实现ApplicationListener监听器接口类
public class EnvironmentChangeApplicationListener implements ApplicationListener<EnvironmentChangeEvent> {@Overridepublic void onApplicationEvent(EnvironmentChangeEvent event) {Object source = event.getSource();if (Objects.isNull(source) || !(source instanceof ApplicationContext)) {return;}IOCContext.setCONTEXT((ApplicationContext) source);}
}

GitHub地址:https://github.com/mingyang66/spring-parent


http://www.ppmy.cn/news/592072.html

相关文章

AIDA模型:什么是营销中的 AIDA 模型?

AIDA模型&#xff0c;也常被称为营销漏斗主要涉及&#xff1a;注意、兴趣、欲望和行动模型&#xff0c;是一个广告效果模型&#xff0c;它确定了个人在购买产品或服务的过程中所经历的阶段。AIDA模型常用于数字营销、销售策略和公共关系活动中。 AIDA 模型层次结构分解 AIDA模型…

python实现邮件发送功能

用最简单的方法实现邮件发送功能&#xff0c;拒绝重复造轮子&#xff0c;提供工作效率。 目录 1、安装依赖 2、发送文本邮件 3、发送多人邮件 4、发送附件邮件 1、安装依赖 pip install yagmail 2、发送文本邮件 import yagmail# 初始化 config yagmail.SMTP(user1740837…

利用AIDA64对电脑进行简单的稳定性测试的技巧分享

下载AIDA64到电脑并安装好&#xff0c;AIDA64可以检测电脑中的软硬件信息&#xff0c;底层的硬件扫描&#xff0c;使它可以详细的显示出PC硬件每一个方面的信息&#xff0c;AIDA64提供的稳定性测试十分强大&#xff0c;也是款不错的 内存检测工具&#xff0c;能够让CPU、RAM等硬…

AdaIn介绍

1、BN、LN、IN、GN的异同 深度学习特征归一化方法——BN、LN、IN、GN 2、IN和AdaIN原理与代码实现

AIDI简介

AIDI(Artificial Inteligent Defect Inspection)是一款基于深度学习的智能工业视觉软件&#xff0c;用于解决复杂缺陷检测、定位、分类问题&#xff0c;可适应多种不同工作场景&#xff0c;具有强大的智能性。AIDI具有自学习功能&#xff0c;随着软件持续运行&#xff0c;缺陷检…

Ada学习

Ada语言国内网络上这方面资料很少或者几乎没有,应用范围也很窄,Ada语言是美国军方用于军用系统开发的语言&#xff0c;Ada语言也是一种面向对象的语言&#xff0c;旧的标准是Ada83&#xff0c;新的标准是Ada95。不过因为应用场景是嵌入式系统&#xff0c;所以我没有使用它面向对…

FPU烤机是什么意思 FPU烤机用什么软件

品牌型号&#xff1a;DellVostro 15 7510 系统&#xff1a;Windows 10 家庭版 软件版本&#xff1a;AIDA64试用版 部分用户可能电脑型号不一样&#xff0c;但系统版本一致都适合该方法。 AIDA64除了可以查看硬件信息之外&#xff0c;还可以进行专业的烤机测试&#xff0c;今…

在Windows上搭建Redis集群环境教程

目录 1、环境依赖 2、安装配置 3、安装Ruby 4 、安装Ruby驱动 5、集群脚本安装 6、测试 1、环境依赖 Redis、Ruby语言运行环境、Redis的Ruby驱动redis-xxxx.gem、创建Redis集群的工具redis-trib.rb 链接&#xff1a;https://share.weiyun.com/GYvF5S5J 密码&#xff1a…