@Async引发的循环依赖问题

这个以前分析过了,但是再看的时候感觉写的太乱了(流水账),这个是精简版本。

bug复现

先上bug,众所周知,spring通过实例化和属性注入分开解决了循环依赖,理论上也不会有问题。但是意外就那么来了,经过排查是@Async导致,报错如下所示。

java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testA': Bean with name 'testA' has been injected into other beans [testB] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:631)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:769)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)... 25 more

源码分析

引发原因

简单的说:BeanA为了解决循环依赖,实例化后注入给了BeanBBeanB实例化,属性注入完后,轮到BeanA属性注入,注入完后的引用和注入给BeanB的引用不一致了,封装了一层代理,spring就报错了

Aop为没有

不走循环依赖的Aop分析

抛开源码不看,async如果有问题,理论上aop也有问题,都是返回代理,你说对吧。但是,如果aop没有问题,aop对循环依赖做了特殊处理,这里,先分析下正常aop代理,懂哥可以跳过

如果没有循环依赖,那么也是通过AbstractAutoProxyCreator::applyBeanPostProcessorsAfterInitialization来创建的

走依循环依赖的Aop分析

如果遇到依赖循环,那么就会在实例化的时候就创建代理了,这里还有一个重要的就是,他会把依赖循环中创建代理的bean给缓存起来,图中已经标注了,后续执行到applyBeanPostProcessorsAfterInitialization的时候也不会再创建一遍代理,而是跳过

再回到最上层,简单的分析下

  • 属性注入完成
  • 引用地址是一样的
  • 代理对象引用在缓存中

通过一个简单的if判断,换下指针,就可以给spring容器交差了。

至此aop分析完成

@Async分析

翻阅了AsyncAnnotationBeanPostProcessor没有实现SmartInstantiationAwareBeanPostProcessor


创建代理对象都是在applyBeanPostProcessorsAfterInitialization中创建的

总结

原理总结

AsyncAnnotationBeanPostProcessor未做特殊处理

解决方案

网上解决方案很多

  • @Lazy
  • 不使用@Async,自己用线程池实现
  • 解决循环依赖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/61187.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CI/CD 持续集成 持续交付

CI(Continuous integration)持续集成 参考:https://www.jianshu.com/p/2132949ff84a 持续集成是指多名开发者在开发不同功能代码的过程当中,可以频繁的将代码行合并到一起并切相互不影响工作。 持续集成的目的,是让…

操作符算数转换题

目录 1.交换两个变量(不创建临时变量) 2.统计二进制中1的个数 3.打印整数二进制的奇数位和偶数位 4.求两个数二进制中不同位的个数 5.【一维数组】有序序列合并 6.获得月份天数 7.变种水仙花数 8.选择题总结tips 这篇博文主要分享操作符&算…

ChatGPT:引领人机交互的未来

前言 在信息技术飞速发展的时代,人机交互的方式也在不断演进。技术对人们生活和工作的影响。本文将带您深入探讨一款引领人机交互未来的人工智能模型——ChatGPT。 ChatGPT简介 ChatGPT 是一种由开放AI(OpenAI)开发的人工智能模型&#xf…

WIndows 配置多版本python环境,非常清晰明了

配置多个python环境 下面以配置三个python版本环境为例子 首先下载好三个环境的python,如:python2.7、python3.6、python3.10 给个官网链接自己下,想要几版本就下几:https://www.python.org/downloads/windows/ 下载完成后将pyth…

C语言控制语句——跳转关键字

循环和switch专属的跳转:break循环专属的跳转:continue无条件跳转:goto break 循环的break说明 某一条件满足时,不再执行循环体中后续重复的代码,并退出循环 需求:一共吃5碗饭, 吃到第3碗吃饱了, 结束吃饭…

Apache SeaTunnel 2.3.3 版本发布,CDC 支持 Schema Evolution!

时隔两个月, Apache SeaTunnel 终于迎来大版本更新。此次发布的 2.3.3 版本在功能和性能上均有较大优化改进,其中大家期待已久的 CDC Schema evolution(DDL 变更同步)、主键 Split 拆分、JDBC Sink 自动建表功能、SeaTunnel Zeta …

Java正则表达式系列--Pattern和Matcher的使用

原文网址:Java正则表达式系列--Pattern和Matcher的使用_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Java的正则表达式中的两个重要类的用法:Pattern和Matcher。 在Java中,java.util.regex包定义了正则表达式使用到的相关类&#xff0c…

如何以Base64形式存储、返回图片数据

在Java中,可以使用Base64类来将图片转换为Base64编码。下面是一个示例代码: Java代码直接处理: import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Base64; public class ImageToBase64…

【GO】LGTM_Grafana_Tempo(2)_官方用例改后实操

最近在尝试用 LGTM 来实现 Go 微服务的可观测性,就顺便整理一下文档。 Tempo 会分为 4 篇文章: Tempo 的架构官网测试实操跑通gin 框架发送 trace 数据到 tempogo-zero 微服务框架使用发送数据到 tempo 根据官方文档实操跑起来 tempo,中间根…

为何反射探针关闭Mipmap后变成了白图

1)为何反射探针关闭Mipmap后变成了白图 2)2021.3 Android从AssetBundle中加载视频播放失败问题 3)SBP是否可以解决打包时FBX等模型文件中额外的GameObject 4)Addressables加载已打包过的Prefab后Mono脚本丢失 这是第349篇UWA技术知…

servlet初体验之环境搭建!!!

我们需要用到tomcat服务器,咩有下载的小伙伴看过来:如何正确下载tomcat???_明天更新的博客-CSDN博客 1. 创建普通的Java项目,并在项目中创建libs目录存放第三方的jar包。 建立普通项目 创建libs目录存放第三…

高速公路自动驾驶汽车超车控制方法研究

目录 摘要 ............................................................................................................ I Abstract ...................................................................................................... II 目录 ...............…

ZooKeeper集群环境搭建

🥇🥇【大数据学习记录篇】-持续更新中~🥇🥇 个人主页:beixi 本文章收录于专栏(点击传送):【大数据学习】 💓💓持续更新中,感谢各位前辈朋友们支持…

端到端自动驾驶综述

End-to-end Autonomous Driving: Challenges and Frontiers 文章脉路 Introduction 从经典的模块化的方法到端到端方法的一个对比, 讲了各自的优缺点, 模块化的好处是各个模块都有自己明确的优化的目标, 可解释性较强, 且容易debug, 缺点是各个模块优化的目标并不是最终的驾…

开始MySQL之路——MySQL的DataGrip图形化界面

下载DataGrip 下载地址:Download DataGrip: Cross-Platform IDE for Databases & SQL 安装DataGrip 准备好一个文件夹,不要中文和空格 C:\Develop\DataGrip 激活DataGrip 激活码: VPQ9LWBJ0Z-eyJsaWNlbnNlSWQiOiJWUFE5TFdCSjBaIiwibGl…

DockerCompose介绍与使用

DockerCompose介绍与使用 1、DockerCompose介绍 DockerCompose用于定义和运行多容器 Docker 应用程序的工具。 通过 Compose可以使用 YAML 文件来配置应用程序需要的所有服务。一个使用Docker容器的应用,通常由多个容器组成,使用Docker Compose不再需要…

vue naive ui 按钮绑定按键

使用vue (naive ui) 绑定Enter 按键 知识点: 按键绑定Button全局挂载使得message,notification, dialog, loadingBar 等NaiveUI 生效UMD方式使用vue 与 naive ui将vue默认的 分隔符大括号 替换 为 [[ ]] <!DOCTYPE html> <html lang"en"> <head>…

LeetCode-452-用最少数量的箭引爆气球

题目描述&#xff1a; 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出…

Myabtis学习记录

mapper.xml常用标签 foreach标签 MyBatis中mapper.xml中foreach的使用_mapper.xml foreach_Willing卡卡的博客-CSDN博客 if标签 Mybatis的mapper.xml中if标签test判断的用法_if标签的test_大唐冠军侯的博客-CSDN博客

一文讲通物联网嵌入式

最近有很多同学问我&#xff0c;物联网近几年一直是科技的热点&#xff0c;嵌入式和物联网有什么关系呢&#xff1f;我在这里统一给大家讲解一下。 嵌入式是应用于物联网产品方向的一种嵌入式操作系统。类似于Android系统是谷歌开发的移动操作系统&#xff0c;嵌入式实际上也是…