怎么用电脑做网站,吴江seo排名,嘉峪关市网站建设设计,国家企业信用信息系统公示查询官网一、Async 注解下的循环依赖问题
我们都知道 Spring IOC 单例模式下可以帮助我们解决循环依赖问题#xff0c;比如下面自己依赖自己循环依赖的场景#xff1a;
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 {ResourceLazyTestAsync 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 扩展类的继承树 AsyncAnnotationBeanPostProcessor 从 BeanPostProcessor 获得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 进行返回。
到这我们需要记住 AsyncAnnotationBeanPostProcessor 的postProcessBeforeInitialization 前通知没有做任何操作 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 方法中这时返回的就是代理类了。