ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

一张图彻底搞懂Spring循环依赖,idea创建java项目教程

2021-12-07 14:03:15  阅读:124  来源: 互联网

标签:缓存 java 依赖 对象 Spring beanName 代理 循环 搞懂


  • 判断是否是循环引用的出口.

  • @param beanName

  • @return

*/

private Object getSingleton(String beanName,GPBeanDefinition beanDefinition) {

//先去一级缓存里拿,

Object bean = singletonObjects.get(beanName);

// 一级缓存中没有, 但是正在创建的bean标识中有, 说明是循环依赖

if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {

bean = earlySingletonObjects.get(beanName);

// 如果二级缓存中没有, 就从三级缓存中拿

if (bean == null) {

// 从三级缓存中取

Object object = instantiateBean(beanName,beanDefinition);

// 然后将其放入到二级缓存中. 因为如果有多次依赖, 就去二级缓存中判断. 已经有了就不在再次创建了

earlySingletonObjects.put(beanName, object);

}

}

return bean;

}

3.3 添加缓存

修改getBean()方法,在getBean()方法中添加如下代码:

//Bean的实例化,DI是从而这个方法开始的

public Object getBean(String beanName){

//1、先拿到BeanDefinition配置信息

GPBeanDefinition beanDefinition = regitry.beanDefinitionMap.get(beanName);

// 增加一个出口. 判断实体类是否已经被加载过了

Object singleton = getSingleton(beanName,beanDefinition);

if (singleton != null) { return singleton; }

// 标记bean正在创建

if (!singletonsCurrentlyInCreation.contains(beanName)) {

singletonsCurrentlyInCreation.add(beanName);

}

//2、反射实例化newInstance();

Object instance = instantiateBean(beanName,beanDefinition);

//放入一级缓存

this.singletonObjects.put(beanName, instance);

//3、封装成一个叫做BeanWrapper

GPBeanWrapper beanWrapper = new GPBeanWrapper(instance);

//4、执行依赖注入

populateBean(beanName,beanDefinition,beanWrapper);

//5、保存到IoC容器

factoryBeanInstanceCache.put(beanName,beanWrapper);

return beanWrapper.getWrapperInstance();

}

3.4 添加依赖注入

修改populateBean()方法,代码如下:

private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {

try {

//ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例

field.set(instance,getBean

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

(autowiredBeanName));

} catch (IllegalAccessException e) {

e.printStackTrace();

continue;

}

}

4 循环依赖对AOP创建代理对象的影响


4.1 循环依赖下的代理对象创建过程

我们都知道Spring AOP、事务等都是通过代理对象来实现的,而事务的代理对象是由自动代理创建器来自动完成的。也就是说Spring最终给我们放进容器里面的是一个代理对象,而非原始对象。

这里我们结合循环依赖,再分析一下AOP代理对象的创建过程和最终放进容器内的动作,看如下代码:

@Service

public class MyServiceImpl implements MyService {

@Autowired

private MyService myService;

@Transactional

@Override

public Object hello(Integer id) {

return “service hello”;

}

}

此Service类使用到了事务,所以最终会生成一个JDK动态代理对象Proxy。刚好它又存在自己引用自己的循环依赖的情况。跟进到Spring创建Bean的源码部分,来看doCreateBean()方法:

protected Object doCreateBean( … ){

// 如果允许循环依赖,此处会添加一个ObjectFactory到三级缓存里面,以备创建对象并且提前暴露引用

// 此处Tips:getEarlyBeanReference是后置处理器SmartInstantiationAwareBeanPostProcessor的一个方法,

// 主要是保证自己被循环依赖的时候,即使被别的Bean @Autowire进去的也是代理对象

// AOP自动代理创建器此方法里会创建的代理对象

// Eagerly cache singletons to be able to resolve circular references

// even when triggered by lifecycle interfaces like BeanFactoryAware.

boolean earlySingletonExposure = (mbd.isSingleton() &&

this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));

if (earlySingletonExposure) { // 需要提前暴露(支持循环依赖),注册一个ObjectFactory到三级缓存

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

}

// 如果发现自己被循环依赖,会执行上面的getEarlyBeanReference()方法,从而创建一个代理对象从三级缓存转移到二级缓存里

// 注意此时候对象还在二级缓存里,并没有在一级缓存。并且此时可以知道exposedObject仍旧是原始对象 populateBean(beanName, mbd, instanceWrapper);

exposedObject = initializeBean(beanName, exposedObject, mbd);

// 经过这两大步后,exposedObject还是原始对象

// 注意:此处是以事务的AOP为例

// 因为事务的AOP自动代理创建器在getEarlyBeanReference()创建代理后,

// initializeBean() 就不会再重复创建了,二选一,下面会有详细描述)

// 循环依赖校验(非常重要)

if (earlySingletonExposure) {

// 前面讲到因为自己被循环依赖了,所以此时候代理对象还存放在二级缓存中

// 因此,此处getSingleton(),就会把代理对象拿出来

// 然后赋值给exposedObject对象并返回,最终被addSingleton()添加进一级缓存中

// 这样就保证了我们容器里缓存的对象实际上是代理对象,而非原始对象

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

// 这个判断不可少(因为initializeBean()方法中给exposedObject对象重新赋过值,否则就是是两个不同的对象实例)

if (exposedObject == bean) {

exposedObject = earlySingletonReference;

}

}

}

}

以上代码分析的是代理对象有自己存在循环依赖的情况,Spring用三级缓存很巧妙的进行解决了这个问题。

4.2 非循环依赖下的代理对象创建过程

如果自己并不存在循环依赖的情况,Spring的处理过程就稍微不同,继续跟进源码:

protected Object doCreateBean( … ) {

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

// 此处注意,因为没有循环引用,所以上面getEarlyBeanReference()方法不会执行

// 也就是说此时二级缓存里并不会存在

populateBean(beanName, mbd, instanceWrapper);

// 重点在此

//AnnotationAwareAspectJAutoProxyCreator自动代理创建器此处的postProcessAfterInitialization()方法里,会给创建一个代理对象返回

// 所以此部分执行完成后,exposedObject() 容器中缓存的已经是代理对象,不再是原始对象

// 此时二级缓存里依旧无它,更别提一级缓存了

exposedObject = initializeBean(beanName, exposedObject, mbd);

// 循环依赖校验

if (earlySingletonExposure) {

// 前面讲到一级、二级缓存里都没有缓存,然后这里传参数是false,表示不从三级缓存中取值

// 因此,此时earlySingletonReference = null ,并直接返回

// 然后执行addSingleton()方法,由此可知,容器里最终存在的也还是代理对象

Object earlySingletonReference = getSingleton(beanName, false);

if (earlySingletonReference != null) {

if (exposedObject == bean) {

exposedObject = earlySingletonReference;

}

}

}

根据以上代码分析可知,只要用到代理,没有被循环引用的,最终存在Spring容器里缓存的仍旧是代理对象。如果我们关闭Spring容器的循环依赖,也就是把allowCircularReferences设值为false,那么会不会出现问题呢?先关闭循环依赖开关。

// 它用于关闭循环引用(关闭后只要有循环引用现象将报错)

@Component

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(false);

}

}

关闭循环依赖后,上面代码中存在A、B循环依赖的情况,运行程序会出现如下异常:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name ‘a’: Requested bean is currently in creation: Is there an unresolvable circular reference?

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:339)

at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:215)

此处异常类型也是BeanCurrentlyInCreationException异常,但报错位置在DefaultSingletonBeanRegistry.beforeSingletonCreation

我们来分析一下,在实例化A后给其属性赋值时,Spring会去实例化B。B实例化完成后会继续给B属性赋值,由于我们关闭了循环依赖,所以不存在提前暴露引用。因此B无法直接拿到A的引用地址,只能又去创建A的实例。而此时我们知道A其实已经正在创建中了,不能再创建了。所有就出现了异常。对照演示代码,来分析一下程序运行过程:

@Service

public class MyServiceImpl implements MyService {

// 因为关闭了循环依赖,所以此处不能再依赖自己

// 但是MyService需要创建AOP代理对象

//@Autowired

//private MyService myService;

@Transactional

@Override

public Object hello(Integer id) {

return “service hello”;

}

}

其大致运行步骤如下:

protected Object doCreateBean( … ) {

// earlySingletonExposure = false 也就是Bean都不会提前暴露引用,因此不能被循环依赖

boolean earlySingletonExposure = (mbd.isSingleton() &&

this.allowCircularReferences &&

isSingletonCurrentlyInCreation(beanName));

populateBean(beanName, mbd, instanceWrapper);

// 若是开启事务,此处会为原生Bean创建代理对象

exposedObject = initializeBean(beanName, exposedObject, mbd);

if (earlySingletonExposure) {

// 因为上面没有提前暴露代理对象,所以上面的代理对象exposedObject直接返回。

}

}

由上面代码可知,即使关闭循环依赖开关,最终缓存到容器中的对象仍旧是代理对象,显然@Autowired给属性赋值的也一定是代理对象。

最后,以AbstractAutoProxyCreator为例看看自动代理创建器实现循环依赖代理对象的细节。

AbstractAutoProxyCreator是抽象类,它的三大实现子类InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator小伙伴们应该比较熟悉,该抽象类实现了创建代理的动作:

// 该类实现了SmartInstantiationAwareBeanPostProcessor接口 ,通过getEarlyBeanReference()方法解决循环引用问题

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {

// 下面两个方法是自动代理创建器创建代理对象的唯二的两个节点:

标签:缓存,java,依赖,对象,Spring,beanName,代理,循环,搞懂
来源: https://blog.csdn.net/m0_64867220/article/details/121767679

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有