003.Spring Cloud Feign 使用 ApplicationListener 问题

使用Spring Cloud的openFeign组件踩坑纪实 背景 2021.05.25 晚上,刚要下班回家,突然被拉到一个群里,说是网关有问题,接入的一个应用接口死活注册不上去,新业务无法使用,而且业务方说已经发布过好几次都不生效,但是同一个应用的其他接口却可以正常注册。听起来还挺诡异的,想着重启大法,重启了下网关应用(其实网关好久没有迭代了,挺稳定的)。结果群里更是炸锅了,说原来可以用的接口也没有了,业务完全停滞(小心脏砰砰直跳)。这下搞大了,放下小书包,开始查问题... 解决过程 其实刚开始完全没有头绪,网关没有做任何改动,只是重启应用,怎么会导致业务方原 阅读详情

1. 场景前提

假设有这样一个场景:在一个 Spring Cloud Feign(Greenwich.SR6)应用中,希望在 Spring 容器启动之后对一些事件做监听,如接收到 ContextRefreshedEvent 事件后,需要做一次初始化操作。一般都是实现 ApplicationListener 接口来监听事件,然后在 onApplicationEvent() 方法里做相应的处理

此时可能会遇到两种情况,一个是监听器中的 onApplicationEvent() 方法被调用了多次,还有一个即是在监听器中使用一些 bean 可能会抛出 NPE 异常

2. ApplicationListener 中初始化多次

2.1 环境搭建

代码已经上传至 https://github.com/masteryourself/diseases ,详见 diseases-spring-cloud/diseases-spring-cloud-feign-listener 工程

2.1.1 代码
1. BaiduFeignClient
@FeignClient(value = "baidu",url = "http://wwww.baidu.com")
public interface BaiduFeignClient {

    @GetMapping("/")
    String index();

}
2. CsdnFeignClient
@FeignClient(value = "csdn",url = "https://blog.youkuaiyun.com/")
public interface CsdnFeignClient {

    @GetMapping("/")
    String index();

}
3. MyApplicationListener
@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    private final AtomicInteger count = new AtomicInteger(0);


    /***********************************    场景一   ***********************************/
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 初始化操作,只能做一次,但实际它会被调用多次
        System.out.println("做了一件非常重要的事情,且只能初始化一次");
    }


    /***********************************    场景二   ***********************************/
    /*@Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        String displayName = event.getApplicationContext().getDisplayName();
        // 第[1]次调用,context 上下文是:FeignContext-baidu
        // 第[2]次调用,context 上下文是:FeignContext-csdn
        // 第[3]次调用,context 上下文是:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@7d3e8655
        System.out.println("第[" + count.incrementAndGet() + "]次调用,context 上下文是:" + displayName);
        // 仅仅适用于 spring cloud F 版本之后,F 版本之前可使用 AtomicBoolean 来判断(因为没有设置 displayName)
        if (displayName.startsWith(FeignContext.class.getSimpleName())) {
            return;
        }
        // 初始化操作,只能做一次
        System.out.println("做了一件非常重要的事情,且只能初始化一次");
    }*/

}
2.2 异常剖析
2.2.1 ApplicationListener 回调机制

在 Spring 容器在创建过程中,都会调用 refresh() 刷新方法,在这个方法的最后一步即是 finishRefresh(),然后用它来发布 ContextRefreshedEvent 事件,它会从容器中找出所有的 ApplicationListener,然后循环调用它们的 onApplicationEvent() 方法

2.2.2 Spring Cloud Feign 原理

@EnableFeignClients -> FeignClientsRegistrar -> 扫描 FeignClient 注解,设置 BeanDefinition 的 BeanClass 类型为 FeignClientFactoryBean,它是 FactoryBean 类型,通过 getObject() 方法获取 Feign 实例

在调用 getObject() 方法获取对象时,底层会调用 NamedContextFactory#createContext() 方法创建一个单独的 Spring Context 上下文对象,目的就是为了配置隔离,所以最终每一个 Spring Context 都会调用 refresh() 方法进行刷新操作,这也就造成了我们定义的 ApplicationListener 中的 onApplicationEvent() 方法被调用了多次

解决办法也很简单,Spring 在创建每个 Feign 组件时,会调用 context.setDisplayName(generateDisplayName(name)) 方法设置 displayName,generateDisplayName() 的生成规则就是 FeignContext-xxx(xxx 是 @FeignClient 注解中的 value 属性),所以使用场景二即可解决。

但要注意:这里只适用于 Spring Cloud F 版本之后,在这之前,Spring Cloud Feign 组件并没有调用 setDisplayName() 这个方法赋值,所以可以使用 AtomicBoolean 来判断

3. ApplicationListener 中使用组件导致 NPE

3.1 环境搭建

代码已经上传至 https://github.com/masteryourself/diseases ,详见 diseases-spring-cloud/diseases-spring-cloud-feign-listener-npe 工程

3.1.1 代码
1. MyApplicationListener
/**
 * <p>description : MyApplicationListener, 监听容器刷新事件
 * 1. 如果先注入了 {@link BaiduFeignClient}, 再注入 {@link SomeBean}, spring 调用 onApplicationEvent() 方法的过程如下(第一次 someBean 无值):
 * {@link FeignListenerNpeApplication} -> refresh(4) -> baiduFeignClient -> refresh(2) -> client -> refresh(1) + {@link SomeBean}
 *                                                   -> csdnFeignClient -> refresh(3)
 *
 * 2. 如果先注入了 {@link SomeBean}, 再注入 {@link BaiduFeignClient}, spring 调用 onApplicationEvent() 方法的过程如下(第一次 someBean 有值):
 * {@link FeignListenerNpeApplication} -> refresh(4) -> baiduFeignClient -> refresh(2) -> {@link SomeBean} + client -> refresh(1)
 *                                                   -> csdnFeignClient -> refresh(3)
 *
 * <p>blog : https://blog.youkuaiyun.com/masteryourself
 *
 * @author : masteryourself
 * @version : 1.0.0
 * @date : 2020/6/9 10:56
 */
@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    /***********************************    场景一   ***********************************/
    @Autowired
    private BaiduFeignClient client;

    @Autowired
    private SomeBean someBean;


    /***********************************    场景二   ***********************************/
    /*@Autowired
    private SomeBean someBean;

    @Autowired
    private BaiduFeignClient client;*/

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("context 上下文是:" + event.getApplicationContext().getDisplayName());
        someBean.doSomething();
    }

}
3.2 异常剖析
3.2.1 Spring Cloud Feign 创建时机

场景一代码:先为 client 对象赋值,而它是一个 Feign 对象,所以在初始化 Feign 对象时,将会执行 refersh() 方法刷新,而在刷新过程中,将会触发 onApplicationEvent() 事件,最终导致在方法里使用的 someBean 对象是空的,此时的执行流程图为:

场景一

场景二代码:先为 someBean 对象赋值,然后再为 client 对象赋值,所以在 onApplicationEvent() 方法里不会抛出 NPE 异常,此时的执行流程图为:

场景二

spring-cloud动态路由“动态”的理解 spring-cloud动态路由“动态”的理解非动态可以通过硬编码来配置路由读取yml文件配置路由动态动态路由接口RouteDefinitionRepository实现RouteDefinitionRepository接口自定义路由配置规则采用数据库+redis配置路由信息自定义RedisRouteDefinitionWriter路由操作类动态路由引申出的事件监听器开启debugger调用链反追踪... 阅读详情

相关推荐

不用买机械臂!用Python+ROS仿真实现RGB-D物体抓取全流程(Gazebo/MoveIt教程)

本文详细介绍了如何利用Python和ROS仿真环境(Gazebo/MoveIt)实现RGB-D物体抓取全流程,无需购买昂贵机械臂硬件。从环境搭建、机械臂模型导入、深度视觉系统配置到MoveIt运动规划,提供完整的实战指南,特别适合个人开发者和学生团队进行机械臂抓取算法验证与开发。

weixin_29061041的博客 340

为什么不能创建监听程序_3.Spring Boot ApplicationListener事件监听机制实现解析

通过SpringFactoriesLoader工厂加载类加载。使用key为applicationListener进行加载。Spring Boot事件监听模式监听模式四要素:事件。监听器广播器触发机制Spring Boot内置了以下事件ApplicationStartingEvent--->run方法首次启动时立即调用ApplicationEnvironmentPreparedEvent---...

weixin_39811193的博客 196

09、Flutter FFI Dart Native API

本文介绍了 Dart Native API 的基本用法,解决了 Dart 与 C 异步通讯的问题

eieihihi的专栏 4399

SPRING CLOUD FEIGN 使用 APPLICATIONLISTENER 问题

1. 场景前提 假设有这样一个场景:在一个 Spring Cloud Feign(Greenwich.SR6)应用中,希望在 Spring 容器启动之后对一些事件做监听,如接收到ContextRefreshedEvent事件后,需要做一次初始化操作。一般都是实现ApplicationListener接口来监听事件,然后在onApplicationEvent()方法里做相应的处理 此时可能会遇到两种情况,一个是监听器中的onApplicationEvent()方法被调用了多次,还有一个即是...

zzy7075的专栏 705

激光三角测量法:斜射与直射式技术的原理与应用解析

本文深入解析激光三角测量法中斜射式与直射式技术的原理、应用及对比。斜射式适用于高精度小范围测量,尤其擅长处理反光表面;直射式则适合大范围位移检测,结构更简单。文章详细介绍了两种技术的光路分析、数学建模、应用场景及选型建议,并探讨了系统校准与误差分析方法,为工业检测领域提供实用参考。

weixin_29280219的博客 414

阿里云大模型ACP认证模拟考试(二)

摘要:在阿里云大模型高级工程师 ACP 认证暂无真题题库的现状下,模拟题具有重要意义。其依照真实考试设计,涵盖题型、题量与分值分布,助力考生熟悉考试框架,合理分配答题时间精力,提升效率。同时,模拟题紧扣官方课程,能帮助考生精准定位知识盲点,如大模型架构原理和通义千问应用实践等重点内容,以便针对性完善知识体系。此外,通过练习模拟题,考生可掌握不同题型的解题思维与技巧,快速洞察复杂题目本质,提高答题准确率。

davidkorhenn的博客 2076

FeignClient引起ApplicationListener.onApplicationEvent()多次执行

场景 Spring cloud 或者 Spring boot项目中,使用FeignClient 实现客户端调用。项目中有通过ApplicationListener初始化的方法。 @Component @Slf4j public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEve

时无英雄、使竖子成名 2103

异步fifo的UVM验证(最终最终版)

更新了refmdl以及scb

qq_44674008的博客 2988

实现SmartApplicationListener并在方法上加上@Async注解,同时引入Feign接口对象导致循环依赖问题

问题重现 定义一个类A (1)实现SmartApplicationListener接口 (2)在重写的onApplicationEvent方法上定义@Async注解 (3)在类中注入其它服务的Feign接口对象 示例如下: @Slf4j @Component public class A implements SmartApplicationListener { @Autowired private FeignX feignX; @Override public boo

我有一个梦想 713

【超分辨率(Super-Resolution)】关于【超分辨率重建】专栏的相关说明,包含专栏简介、专栏亮点、适配人群、相关说明、阅读顺序、超分理解、实现流程、研究方向、论文代码数据集汇总等

本专栏研究领域为【超分辨率重建】,涵盖图像超分、视频超分,实时超分,4K修复等方面。主要内容包括主流算法模型的论文精读、论文复现、毕业设计、涨点手段、调参技巧、论文写作、应用落地等方面。算法模型从SRCNN开始更新至今,一般是一篇论文精读对应一篇论文复现。论文精读详解理论,归化繁为简,归纳核心,积累词句,培养阅读论文和论文写作能力。论文复现依托Pytorch代码,实现完整的模型训练流程,总结调参方法,记录碰到的bug,论文插图可视化,培养读写代码能力、做实验的能力、以及应用落地能力。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 3万+

某智能物流无人值守系统 ExportReport SQL注入漏洞复现

某智能物流无人值守系统 ExportReport存在SQL注入漏洞,未经身份验证的远程攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。

0xSec 818

Spring中的ApplicationListener和ContextRefreshedEvent的理解

借鉴业余草中作者herman的文章,结合我自己的项目,发表一下观点,不合理的地方请各位读者多多指教。转载地址:https://www.xttblog.com/?p=2053ApplicationListener和ContextRefreshedEvent一般都是成对出现的。事件机制作为一种编程机制,在许多语言中都提供了支持。JAVA语言也不例外,java中的事件机制的参与者有3种角色:event...

zhuifengshaonian201的博客 1万+

Python爬虫实战:使用最新技术爬取路透社新闻数据

网络爬虫(Web Crawler)是一种自动获取网页内容的程序,是搜索引擎的重要组成部分。随着大数据和人工智能技术的发展,爬虫技术在数据采集、市场分析、舆情监控等领域发挥着越来越重要的作用。requests:同步HTTP请求库aiohttp:异步HTTP请求库:HTML解析库lxml:高效的HTML/XML解析库selenium:浏览器自动化工具playwright:现代浏览器自动化工具pandas:数据处理和分析库scrapy:专业的爬虫框架:生成随机User-Agentredis:缓存和分布式爬虫支持。

2201_76125261的博客 1204

Spring ContextRefreshedEvent 事件触发多次问题

ContextRefreshedEvent 触发多次问题ContextRefreshedEvent 在哪被触发什么情况下 context 会 refresh() 多次怎么样才能执行一次 ContextRefreshedEvent 在哪被触发 在 AbstractApplicationContext 的 refresh() 方法中的 finishRefresh() 步骤中被触发。 也就是说当 context refresh 一次就会触发一次 ContextRefreshedEvent 事件 什么情况下 co

qq_36390263的博客 2399

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Bug:引入Feign后触发了2次、4次ContextRefreshedEvent

Bug:引入Feign后发现监控onApplication中ContextRefreshedEvent事件触发了2次或者4次。

燕双嘤 1516

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

刚刚接触ApplicationEvent和ApplicationListener

SpringBoot中ApplicationEvent和ApplicationListener 假如在某业务中,需要发邮件,或者单独记录日志,但是呢,邮件并不重要,发送成功最好,不成功拉倒的那种。前提是不能影响主业务的正常结束。(其实是记录了一个不是很重要的日志,用到的时候需要单独记录统计的); 据说消息队列可以做很多事,但是!我不会!!!再说了就为了这点事加上消息队列,不太合适吧。 springboot 2.3.9.RELEASE (全是抄来的) 先定义事件:MyEvent 继承ApplicationEv

weixin_46060289的博客 661

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

SpringBoot和SpringCloud下的ContextRefreshedEvent事件踩坑

前言 由于之前的项目时SpringBoot的一体项目,后续想对项目进行微服务的重构,然而在重构的时候发现SpringCloud下的ContextRefreshedEvent事件触发的顺序和原本项目触发的顺序不一样。 踩坑 首先上代码: 原本的逻辑是在事件触发的时候为了避免重复触发所以加了一个判断条件,只有在第一次context==null的情况下才会执行对应的逻辑代码。(实际上不加判断条件貌...

Box_clf的博客 1945

Spring ApplicationListener使用方法及问题

使用场景在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载、初始化缓存、特定任务的注册等等。这个时候我们就可以使用Spring提供的ApplicationListener来进行操作。用法本文以在Spring boot下的使用为例来进行说明。首先,需要实现ApplicationListener接口并实现onApplicationEvent方法。把需要处理的操作放在onAppl

程序新视界 6万+

详解Spring中的ApplicationListener和ContextRefreshedEvent

  转自:https://www.xttblog.com/?p=2053   事件机制作为一种编程机制,在许多语言中都提供了支持。JAVA语言也不例外,java中的事件机制的参与者有3种角色: event object event source event listener 这三个角色的含义字面上很好解,它们就定义了事件机制的一个基本模型。作为一种常用的编程设计机制,许多开源框...

lolichan的博客 1168

spring cloud @RefreshScope

在最近看到一些项目用@value注入配置属性,并且乱用@RefreshScope,看得有点发慌 ####1:sprin boot 2.0后不推荐使用@Value 所以建议将使用***@ConfigurationProperties***,把有共性的属性统一归类方便管理,并且***配置刷新是不需要加@RefreshScope***的 ####2:@RefreshScope 不能修饰在 @Scheduled、listener、Timmer等类中 配置刷新后会卸载类,并重新实例化类(如果类中存在计数等情况需要注意

qq_16590151的专栏 630

【Spring Cloud】FeignClient 首次请求花费大量时间/超时的问题

问题: 项目启动后,首次使用 Feign 请求往往会消耗大量时间,并有一定概率因此导致请求超时。 探索 仔细观察日志,会发现本质上是因为feign client的初始化花费了大量时间。 2019-01-28 16:19:46.074 INFO 3740 --- [nio-9790-exec-2] s.c.a.AnnotationConfigApplicationContext :...

ss的程序人生 1万+

观察者模式ApplicationListener异步处理事件

配置类中添加 @EnableAsync开启对异步任务的支持, 并在相应的方法中使用 @Async注解来声明一个异步任务 Environment 可以直接获取配置文件的值 @Configuration @EnableAsync public class TaskExecutorConfig { /*** * 创建异步任务执行线程池 * @return */ ...

占星安啦的博客 3370

异常解决——Spring Cloud FeignClient: BeanCreationException: Error creating bean with

最近自己搭建了Spring Cloud 架构,在做客户端的时候,FeignClient标注的Service接口 无法注入,can not be autowired 。 网上找了一堆资料也没发现可用的。问题发现:开始找问题: 刚开始搭建客户端的时候接口注入是ok的,但是在我集成了swagger 和 mybatis 之后,FeignClient接口就无法注入了。分析和解决:既然问题找到了,那么...

weixin_33758863的博客 1335

ContextRefreshedEvent事件使用注意事项

0 概述 ContextRefreshedEvent 事件会在Spring容器初始化完成会触发该事件。我们在实际工作也可以能会监听该事件去做一些事情,但是有时候使用不当也会带来一些问题。 1 防止重复触发 主要因为对于web应用会出现父子容器,这样就会触发两次,那么如何避免呢?下面给出一种简单的解决方案。 @Component public class TestTask imple

huangshanchun的专栏 1万+

异常解决——Spring Cloud FeignClient: BeanCreationException: Error creating bean with name XXXService

最近自己搭建了Spring Cloud 架构,在做客户端的时候,FeignClient标注的Service接口 无法注入,can not be autowired 。 网上找了一堆资料也没发现可用的。 问题发现: 开始找问题: 刚开始搭建客户端的时候接口注入是ok的,但是在我集成了swagger 和 mybatis 之后,FeignClient接口就无法注入了。  分析和解决: 既然问题找...

Thancks 1万+
上一篇: 002.Spring 使用 Async 导致循环创建异常
下一篇: 07.Spring Cloud 之 Hystrix
masteryourself
博客等级 码龄9年 13粉丝 48原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值