@Async引发的循环依赖问题

这篇具有很好参考价值的文章主要介绍了@Async引发的循环依赖问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

bug复现

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

java.lang.IllegalStateException: Failed to load ApplicationContext

	at 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就报错了
@Async引发的循环依赖问题,源码,java

@Async引发的循环依赖问题,源码,java

Aop为没有

不走循环依赖的Aop分析

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

@Async引发的循环依赖问题,源码,java

@Async引发的循环依赖问题,源码,java

如果没有循环依赖,那么也是通过AbstractAutoProxyCreator::applyBeanPostProcessorsAfterInitialization来创建的
@Async引发的循环依赖问题,源码,java

走依循环依赖的Aop分析

如果遇到依赖循环,那么就会在实例化的时候就创建代理了,这里还有一个重要的就是,他会把依赖循环中创建代理的bean给缓存起来,图中已经标注了,后续执行到applyBeanPostProcessorsAfterInitialization的时候也不会再创建一遍代理,而是跳过
@Async引发的循环依赖问题,源码,java
再回到最上层,简单的分析下

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

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

至此aop分析完成
@Async引发的循环依赖问题,源码,java

@Async分析

翻阅了AsyncAnnotationBeanPostProcessor没有实现SmartInstantiationAwareBeanPostProcessor

@Async引发的循环依赖问题,源码,java
创建代理对象都是在applyBeanPostProcessorsAfterInitialization中创建的

@Async引发的循环依赖问题,源码,java

总结

原理总结

AsyncAnnotationBeanPostProcessor未做特殊处理

解决方案

网上解决方案很多文章来源地址https://www.toymoban.com/news/detail-678134.html

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

到了这里,关于@Async引发的循环依赖问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring源码解析——IOC之循环依赖处理

    循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图所示: 注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。 最全面的Java面试网站

    2024年02月08日
    浏览(53)
  • Spring解决循环依赖问题

    例如,就是A对象依赖了B对象,B对象依赖了A对象。(下面的代码属于 属性的循环依赖 ,也就是初始化阶段的循环依赖,区别与底下 构造器的循环依赖 ) 问题来了: A Bean创建 —— 依赖了 B 属性 ——  触发 B Bean创建 ——  B 依赖了 A 属性 ——  需要 A Bean(但A Bean还在创建

    2024年02月12日
    浏览(45)
  • 从源码层面深度剖析Spring循环依赖 | 京东云技术团队

    以下举例皆针对单例模式讨论 图解参考 https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce 对于单例Bean来说,在Spring容器整个生命周期内,有且只有一个对象。 Spring 在创建 Bean 过程中,使用到了三级缓存,即 DefaultSingletonBeanRegistry.java 中定义的: 以 com.gyh.general 包下的 OneBean 为

    2024年02月13日
    浏览(36)
  • Spring如何解决循环依赖问题

    循环依赖问题在Spring中主要有三种情况: (1)通过构造方法进行依赖注入时产生的循环依赖问题。 (2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。 (3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。 在Spring中,只有第

    2024年02月06日
    浏览(46)
  • Spring怎么解决循环依赖问题?

    循环依赖是指 一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用 , 举个例子 : A 依赖B , B依赖C , C依赖A , 这样就形成了循环依赖;   ①构造器的循环依赖:这种依赖spring是处理不了的,直接拋出BeanCurrentlyInCreationException异常。 ②单例模式下的se

    2024年02月08日
    浏览(55)
  • springboot升级出现循环依赖问题

    问题从spring boot 2.3.12升级到2.6.15版本后,项目启动后访问报错. The dependencies of some of the beans in the application context form a cycle. serviceCollectionIdCacheService ┌─────┐ | serviceProductInfoProviderImpl ↑ ↓ | serviceOfflineProviderImpl ↑ ↓ | serviceProductMappingProviderImpl └─────┘ Relying

    2024年02月15日
    浏览(46)
  • Spring 是如何解决循环依赖问题的?

    提示:这里简述项目相关背景: 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大)         我们都知道,如果在代码中,将 两个或多个 Bean 互相之间持有对方的引用 就会发生循环依赖。循环的依赖将会导致注入死循环,这

    2024年02月13日
    浏览(45)
  • 关于SpringBoot中出现的循环依赖问题

    环境: SpringBoot2.7.8 背景: 在增加出库订单时需要对物品表的的数量进行修改 因此我在OutboundController中创建了几个公共方法,并将其注入到Spring中,结果给我报了这一串错误。 Description: The dependencies of some of the beans in the application context form a cycle: ┌──-──┐ | getGoodsNumber defi

    2024年02月11日
    浏览(83)
  • (循环依赖问题)学习spring的第九天

     Bean实例的属性填充  Spring在属性注入时 , 分为如下几种情况 : 注入单向对象引用 : 如usersevice里注入userdao , userdao里没有注入其他属性     注入双向对象引用 : 如usersevice里注入userdao , userdao也注入usersevice属性  (搞清原理即可) 问题提出 : 形成死循环 解决问题 : 三级缓存解决

    2024年01月22日
    浏览(38)
  • 详细解释一下Spring是如何解决循环依赖问题的

    我们都知道,如果在代码中,将 两个或多个Bean互相之间持有对方的引用就会发生循环依赖。循环的依赖将会导致注入死循环 ,这是Spring发生循环依赖的原因 1. 互相依赖: A依赖B , B依赖A , 他们之间形成了循环依赖 2. 间接依赖: A依赖B ,B依赖C , C又依赖A,形成了循环依

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包