前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【禁止血压飙升】啥都没干,只是加了个ApplicationListener实现类,你告诉我服务挂了=͟͟͞͞(꒪ᗜ꒪ ‧̣̥̇)

【禁止血压飙升】啥都没干,只是加了个ApplicationListener实现类,你告诉我服务挂了=͟͟͞͞(꒪ᗜ꒪ ‧̣̥̇)

作者头像
烟雨平生
发布2024-06-06 18:58:02
650
发布2024-06-06 18:58:02
举报
文章被收录于专栏:数字化之路数字化之路

从日志看,使用fastjson库来序列化需要打印到日志的Java对象时,触发了StackOverflowError。这个错误表明需要序列化的对象很可能存在递归引用的问题,即对象直接或间接地引用了自己。

交待下背景:报错项目的SpringBoot版本:2.5.2。

引发报错的变更: 只是新增一个服务类,这个类干了两个事:

  1. 提供一个Rest接口
  2. SpringBoot启动时从Spring容器中拿到几个对象。
代码语言:javascript
复制

import com.zkh360.gbb.user.advertisement.job.FetchAdvertisementReportDailyJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@Slf4j
public class FetchReportCommon implements ApplicationListener<ApplicationReadyEvent> {
    private static StringRedisTemplate stringRedisTemplate;
    private Map<String, FetchAdvertisementReportDailyJob> mapper;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        this.mapper = this.applicationContext.getBeansOfType(FetchAdvertisementReportDailyJob.class);
        stringRedisTemplate = this.applicationContext.getBean(StringRedisTemplate.class);
    }

    @PostMapping("/fetch/report/refreshToken/trigger")
    public String refreshAccessToken() {
        log.info("[refreshAccessToken] start.");
        try {
            for (FetchAdvertisementReportDailyJob value : mapper.values()) {
                value.refreshAccessToken();
            }
            log.info("[refreshAccessToken] finished");
            return "success";
        } catch (Exception e) {
            log.info("[refreshAccessToken] failed,errorMessage:{}", e.getMessage());
            return "failed,errorMessage:" + e.getMessage();
        }
    }

}

报错原因:log了一个包含存在循环依赖的对象。

net.ai-as.ad.job.FetchReportCommon#onApplicationEvent中的ApplicationReadyEvent是个超级对象,包含了Spring容器的上下文对象AnnotationConfigServletWebServerApplicationContext,同时也依赖了FetchReportCommon。

org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 是Spring Boot提供的特定实现类,继承自AnnotationConfigApplicationContext并扩展了ServletWebServerApplicationContext。 这个类是专门为基于Servlet API的Web应用程序设计的,它结合了基于注解的配置和Servlet Web服务器的功能。

在Spring Boot中,当你创建一个Web应用程序时,AnnotationConfigServletWebServerApplicationContext或其子类通常会被用来初始化和配置应用程序上下文。例如,Spring Boot自动配置类会根据你的应用程序需求自动创建一个这样的上下文实例,并配置嵌入式Web服务器。

这个类是Spring Boot中实现Web应用程序快速开发和部署的关键组件之一,它结合了Spring的依赖注入和Web应用程序的Servlet API,提供了一个简单而强大的运行时环境。

不敢相信,Spring应用容器对象 居然是一个事件对象的构建参数!!

这很明显没有遵守SOLID中的LoD法则。 按照最少知识原则(Least Knowledge Principle),一个对象应该对其他对象有最少的了解,即“Talk only to your immediate friends and not to strangers”。

是不是场景比较特殊? 来看看SpringBoot是如何实例化ApplicationReadyEvent对象的:

org.springframework.boot.context.event. EventPublishingRunListener#running

好吧,事情已经发生了,我们就没啥不好意思讲的。

再画类图,来更直观的观摩下这个循环引用:

这样看循环是不是清晰了

想基于这个循环依赖引发fastjson中的Stack0verflowError还需要一个打印applicationReadyEvent。

留下小作业:如何解决?

问题已经搞清了: SpringBoot2.5.2在初始化ApplicationListener实现类FetchReportCommon,在执行onApplicationEvent方法后,会把入参对象applicationReadyEvent打印到日志。 applicationReadyEvent对象中包含了一个循环依赖。 打印applicationReadyEvent时,fastjson就StackOverflowError了。

那么如何改?

小结

新加的类虽然增强了应用程序的功能,但也带来了一系列挑战。 项目中对调用RestController的方法以及传递的参数都进行了日志记录。这是一种好的实践,因为它可以帮助我们追踪请求和响应的流程,以及调试潜在的问题。然而,这种全面的日志策略可能会引入性能问题, 从日志策略到事件处理,再到第三方库的使用,每一个环节都需要我们仔细设计和审查。特别是对于循环依赖和递归引用这种潜在的问题,我们需要提前识别并采取措施避免。通过这些经验,我们可以继续优化我们的代码,确保应用程序的稳定性和可靠性。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 的数字化之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 留下小作业:如何解决?
  • 小结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档