当前位置: 首页 > news >正文

《网站建设与管理》方案深圳网络营销推广外包

《网站建设与管理》方案,深圳网络营销推广外包,软件下载站网站源码免费,电子商务网站建设规划设计任务书一、Async 注解下的循环依赖问题 我们都知道 Spring IOC 单例模式下可以帮助我们解决循环依赖问题,比如下面自己依赖自己循环依赖的场景: Component public class TestAsync {ResourceTestAsync async;public void test() {System.out.println("t…

一、@Async 注解下的循环依赖问题

我们都知道 Spring IOC 单例模式下可以帮助我们解决循环依赖问题,比如下面自己依赖自己循环依赖的场景:

@Component
public class TestAsync {@ResourceTestAsync async;public void test() {System.out.println("test....");}
}

从容器中获取到该 bean 执行测试方法:

public class App {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext("com.demo.test");TestAsync testAsync = context.getBean("testAsync", TestAsync.class);testAsync.test();}}

在这里插入图片描述

可以看到正常执行,但当我们加上 @Async 注解后:

@Component
@EnableAsync
public class TestAsync {@ResourceTestAsync async;@Asyncpublic void test() {System.out.println("test....");}
}

再次执行发现报错了:

在这里插入图片描述

是不是很奇怪,难道代理对象就会有问题吗,如果换成 @Transactional 呢:

@Component
@EnableTransactionManagement
public class TestAsync {@ResourceTestAsync async;@Transactionalpublic void test() {System.out.println("test....");}
}

再次执行,发现可以正常运行:

在这里插入图片描述

那为什么 @Async 会有问题呢,其实和我们上篇文章中讲解的 BeanPostProcessor 扩展接口有关,这里先说一下解决方法:

将依赖注入换成懒加载的方式即可:

@Component
@EnableAsync
public class TestAsync {@Resource@LazyTestAsync async;@Asyncpublic void test() {System.out.println("test...." + Thread.currentThread().getName());}
}

在这里插入图片描述

可以看到恢复正常了,下面从源码角度分析下出现该问题的原因。

如果不了解 BeanPostProcessor 扩展接口,可以先看下下面这篇文章:

Spring 源码解析 - BeanPostProcessor 扩展接口

二、源码分析

2.1 @EnableAsync

当使用 @EnableAsync 开启异步支持时,会向 Spring 容器中注入AsyncConfigurationSelector.class 类:

在这里插入图片描述

在该类中,selectImports 下根据 adviceMode 选择注入配置类,adviceMode 默认为 PROXY,会注入 ProxyAsyncConfiguration.class 配置类:

在这里插入图片描述
ProxyAsyncConfiguration.class 配置类下,注入了一个 AsyncAnnotationBeanPostProcessor 扩展类:

在这里插入图片描述

下面看下AsyncAnnotationBeanPostProcessor 扩展类的继承树:

在这里插入图片描述

AsyncAnnotationBeanPostProcessorBeanPostProcessor 获得bean初始化前后的扩展能力,从 ProxyProcessorSupport 获取代理能力。

这里重点看 BeanPostProcessor 扩展,在 BeanPostProcessor 中有两个核心的扩展方法如下:

public interface BeanPostProcessor {/***  实例化及依赖注入完成后、bean 初始化方法触发之前执行*/Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;/***  bean 初始化方法触发后执行*/Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;}

下面看挨个看AsyncAnnotationBeanPostProcessor中这两个方法干了什么:

AsyncAnnotationBeanPostProcessor没有直接实现 postProcessBeforeInitialization ,实现在 AbstractAdvisingBeanPostProcessor 下的 postProcessBeforeInitialization 方法:

在这里插入图片描述

没有做任何操作,直接返回的原 bean ,同样 postProcessAfterInitialization 方法也在 AbstractAdvisingBeanPostProcessor 下:

在这里插入图片描述

在这里实际生成了代理 bean 进行返回。

到这我们需要记住 AsyncAnnotationBeanPostProcessorpostProcessBeforeInitialization 前通知没有做任何操作, postProcessAfterInitialization 后通知创建了代理实例。

2.2 getEarlyBeanReference

了解过循环依赖的应该知道 Spring 中使用三级缓存来解决循环依赖问题,其中实例化 bean 后,会首先曝光至第三级缓存中,该逻辑在 AbstractAutowireCapableBeanFactory 类的 doCreateBean 方法下:

在这里插入图片描述
在这里插入图片描述

doCreateBean 方法下的 populateBean 主要是进行了依赖的注入:

在这里插入图片描述

在进行依赖注入时,会递归尝试从三级缓存中获取 bean ,由于这里是循环依赖,已经放入了第三级缓存中,因此可以命中,这快的源码逻辑在 DefaultSingletonBeanRegistry 类下的 getSingleton 方法:

在这里插入图片描述

这里可以看出三级缓存命中会执行 ObjectFactory.getObject() 方法获取一个早期的实例,获取之后存入二级缓存中,从前面放入三级缓存可以看出其实是触发的 AbstractAutowireCapableBeanFactory 下的 getEarlyBeanReference 方法:

在这里插入图片描述

这里会判断是否存在 SmartInstantiationAwareBeanPostProcessor 类型的 BeanPostProcessor 扩展,然后尝试使用 getEarlyBeanReference 获取一个早期的实例,从前面 AsyncAnnotationBeanPostProcessor 的继承树可以看出并没有实现 SmartInstantiationAwareBeanPostProcessor 因此这里拿到的就是原来的 bean 实例:

在这里插入图片描述
在这里插入图片描述

到这里我们需要记住,Spring 单例缓存中存储的是 TestAsync 真正的实例对象。

2.3 initializeBean

在依赖注入后会触发 initializeBean 方法,进行 bean 的初始化操作:

在这里插入图片描述

initializeBean 方法中,执行初始化前,先执行BeanPostProcessors的前置方法,并且将前置方法返回的 bean 代替原先创建的 bean ,在 bean 初始化后执行BeanPostProcessors的后置方法,并将后置方法返回的 bean 代替原先的 bean,从上面对 AsyncAnnotationBeanPostProcessor的简单分析得出,前置方法没做任何处理,后置方法会生成一个代理对象,因此initializeBean 方法最终返回的是代理对象:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看出最后生成的是一个代理对象,现在应该就会发现一个问题了,在 Spring 单例容器中和依赖注入中的都是 TestAsync 真正的实例,而这里返回的是代理实例,现在相当于单例的 bean 存在了两个不同的实例。

2.4 判断是否出现重复实例

在回到 doCreateBean 方法下 initializeBean 执行后:

在这里插入图片描述

如果是单例的话,则尝试从容器中获取当前的 beanName 实例,由于前面已经曝光到了二级缓存中,因此这里可以获取到,但容器中的bean实例和当前的 bean实例已经不是一个实例了,因此会进入到 else if 中,这里获取到该 beanName 所有的依赖,通过 removeSingletonIfCreatedForTypeCheckOnly 删除已经创建好的 bean 实例,因为单例模式下 Spring 仅允许有一个实例,这里可以看下 removeSingletonIfCreatedForTypeCheckOnly 方法的逻辑:

在这里插入图片描述
这里主要判断 alreadyCreated 中是否存在,如果不存在则删除单例缓存中的实例,那 alreadyCreated 什么时候放入的呢,其实在AbstractAutowireCapableBeanFactory 类的 doGetBean 方法中触发的 markBeanAsCreated 方法:
在这里插入图片描述
在这里插入图片描述

因此 removeSingletonIfCreatedForTypeCheckOnly 方法这里会返回 false,在回到 doCreateBean 中继续看,由于 removeSingletonIfCreatedForTypeCheckOnly 返回 false ,正好符合条件被加入了 actualDependentBeans 集合中,再下面如果actualDependentBeans 集合不为空则抛出异常,这个异常是不是和之前报错的异样一样:

在这里插入图片描述

三、为什么 @Transactional 不会出现这种问题呢

从上面的分析可以得出结论假如 getEarlyBeanReference 可以获取到代理实例,是不是就不会发生后面的问题,这恰恰也是 @Transactional 情况下的不会出现该问题的关键点。

@Transactional 代理使用的是 InfrastructureAdvisorAutoProxyCreator

在这里插入图片描述

InfrastructureAdvisorAutoProxyCreator的继承树可以看到,其继承了 SmartInstantiationAwareBeanPostProcessor 方法。并且在父类 AbstractAutoProxyCreator 中重写了 getEarlyBeanReference 方法:

在这里插入图片描述

下面可以 debug 一下 AbstractAutowireCapableBeanFactory 中的 getEarlyBeanReference 方法:

在这里插入图片描述
可以看到这里还是真正的实例对象,下面会进到 AbstractAutoProxyCreator 中重写的 getEarlyBeanReference 方法,最终进到当前类的 wrapIfNecessary 方法:
在这里插入图片描述

到这里就已经生成了一个代理类了,再回到 AbstractAutowireCapableBeanFactory 中的 getEarlyBeanReference 方法中,这时返回的就是代理类了。

在这里插入图片描述

http://www.mmbaike.com/news/56085.html

相关文章:

  • 帮别人做网站赚钱吗seo专业学校
  • 怎么做阿里妈妈企业网站百度平台推广联系方式
  • 信息管理与信息系统专业重庆快速排名优化
  • wordpress配置邮箱东莞seo优化公司
  • 数码公司网站建设调查在线网页制作
  • 动态网站开发技术需求分析百度seo怎么提高排名
  • 中国建设银行行号查询网站软文写作经验是什么
  • 网站开发 方案网站搜索引擎优化方法
  • 谁做违法网站怎么制作百度网页
  • 毕设做网站怎么命题seo积分系统
  • 用excel做网站快速提高网站关键词排名优化
  • 有没有专门做飞卢小说盗版的网站seo搜索引擎优化视频
  • 权威发布新闻的含义北京seo怎么优化
  • 做网站公司名字应该用图片吗360推广助手
  • wap网站的域名今日大新闻
  • 荣耀官方网站手机商城湖州网站建设制作
  • 网站开发工具有哪些企业网络营销方案设计
  • wordpress 新闻杂志主题搜索引擎优化的技巧有哪些
  • 成品网站源码68w68游戏百度趋势搜索大数据
  • 35开始学网站开发跨境电商平台注册开店流程
  • 企业官网模板 静态windows优化大师怎么用
  • friday wordpressseo网站关键词优化多少钱
  • 自己买空间做网站竞价托管sem服务
  • 潍坊市做网站免费下载百度
  • 房地产企业网站模板免费下载优化关键词排名工具
  • 滕州网站制作百度企业认证怎么认证
  • 入境美国前做登记叫啥网站设计网络营销方案
  • wordpress 安装 404灯塔seo
  • 昆明快速做网站百度竞价推广流程
  • 网站频繁改版如何建立网站平台的步骤