• 8.5 Spring解决循环依赖的机理(AOP)


    MyAspect

    @Aspect
    public class MyAspect {
        @After(value = "execution(* com.cjf.bean.B.*(..))")
        public void myAfter(){
            System.out.println("最终通知的功能.........");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    SpringBean.xml

        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    	<bean id="myAspect" class="com.cjf.bean.MyAspect"/>
        <bean id="a" class="com.cjf.bean.A">
            <property name="b" ref="b"/>
        bean>
        <bean id="b" class="com.cjf.bean.B">
            <property name="a" ref="a"/>
        bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    1、大致流程

    1. 先去一级缓存寻找 A ,没有去创建 A, 然后 A 将自己的 ObjectFactory ( lambda )放入到三级缓存中,初始化的时候需要 B ,去创建 B

    2. B 实例化同理 AB 将自己的工厂对象( lambda )放入到了 三级缓存),B 初始化的时候发现需要 A ,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 AObjectFactory

    3. 然后调用 ObjectFactory.getObject() 方法最终会调用 getEarlyBeanReference() 方法,返回一个 A 的代理对象,然后把 proxyA 放入二级缓存里面,并删除三级缓存中的 ObjectFactoryA

    4. B 顺利赋值完毕,通过 populateBean 返回 ,然后调用 initializeBean 方法进行初始化操作

      • initializeBeanbean 的声明周期操作,以及下面所述的第二种 AOP 代理情况(代理 B 对象)
    5. 然后回来接着创建 A ,此时 B 已经创建结束,直接从一级缓存里面拿到 proxyB ,完成注入操作及初始化。

      • 由于完成注入操作及初始化返回的是 A 对象,不是 proxyA。再从二级缓存中获取到 proxyA 然后返回 proxyA
    6. 最后将 proxyA 放入到 一级缓存中,再从 二级缓存中删除 proxyA

    可以看出,更非 AOP 的循环依赖类似,只不过多了进行代理操作。

    2、Spring AOP 代理时机

    参考来自 Spring源码最难问题《当Spring AOP遇上循环依赖》_bugpool的博客-CSDN博客

    参考来自 Spring 为何需要三级缓存解决循环依赖,而不是二级缓存 - 半分、 - 博客园 (cnblogs.com)

    对于 Spring AOP 代理,Spring AOP代理时机有2个:

    • 当自定义了 TargetSource ,则在 bean 实例化前完成 Spring AOP 代理并且直接发生短路操作,返回 bean

    • 正常情况下,都是在 bean 初始化后进行 Spring AOP 代理 ②

    • 如果要加上提前曝光代理,getEarlyBeanReference 可以说 3 种 ③

    第二种,initializeBean (初始化 bean

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    		throws BeansException {
    
    	Object result = existingBean;
    	for (BeanPostProcessor processor : getBeanPostProcessors()) {
    		// 调用bean初始化后置处理器处理
    		Object current = processor.postProcessAfterInitialization(result, beanName);
    		if (current == null) {
    			return result;
    		}
    		result = current;
    	}
    	return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    调用 postProcessAfterInitialization (后置处理器的)方法进行代理

    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    	if (bean != null) {
    		// 获取缓存key
    		Object cacheKey = getCacheKey(bean.getClass(), beanName);
    		// 查看该bean是否被Spring AOP提前代理!而缓存的是原始的bean,因此如果bean被提前代理过,这此处会跳过
    		// 如果bean没有被提前代理过,则进入AOP代理
    		if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                //返回一个新的代理对象
    			return wrapIfNecessary(bean, beanName, cacheKey);
    		}
    	}
        return bean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第三种,提前曝光代理

    getEarlyBeanReference 方法

    //AbstractAutowireCapableBeanFactory.java
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
       Object exposedObject = bean;
       if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
          for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
              //返回一个生成的代理对象
             exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
          }
       }
       return exposedObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    getEarlyBeanReference 方法(不同的类),在这里 A 提前代理了。

    // AbstractAutoProxyCreator.java
    public Object getEarlyBeanReference(@Nullable Object bean, String beanName) {
    	// 获取缓存key
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        //将当前对象放入 earlyProxyReferences 中
        this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey);
    	return bean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    返回一个新的代理对象。

    image-20221121180613964


    3、提前曝光 A,创建 proxyA

    情节说明(我们代理 AB):

    1. 先去一级缓存寻找 A ,没有去创建 A, 然后 A 将自己的 ObjectFactory ( lambda )放入到三级缓存中,初始化的时候需要 B ,去创建 B
    2. B 实例化同理 AB 将自己的工厂对象( lambda )放入到了 三级缓存),B 初始化的时候发现需要 A ,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了 AObjectFactory
    3. 然后调用ObjectFactory.getObject() 方法最终会调用 getEarlyBeanReference() 方法,返回一个 A 的代理对象,然后把 proxyA 放入二级缓存里面,并删除三级缓存中的 ObjectFactoryA

    这里。通过上面第 3 3 3 种 提前曝光处理,创建 A 的代理对象

    image-20221121181849060

    一层一层的返回 ,sharedInstance 对象为 A 的代理对象( proxyA),然后对获取到的 proxyA 赋值处理,

    • 即: B ( a = A ( b = n u l l ) ) B(a=A(b=null)) B(a=A(b=null)),但是这里的 AproxyA

    image-20221121182246199


    4、后置处理器创建 proxyB

    此时:

    1. B 顺利赋值完毕,通过 populateBean 返回 ,然后调用 initializeBean 方法进行初始化操作
    • initializeBeanbean 的生命周期操作,以及上述说明的第二种 AOP 代理情况

    对 B 对象进行后置处理器创建代理,返回 proxyB

    image-20221121192307794

    image-20221121192856920

    1. B 对象已经初始化完成, 此时 exposedObjectproxyB

    proxyB 对象加入到一级缓存中( addSingleton(beanName, proxyB);

    image-20221121194111902

    1. 然后回来接着创建 A ,此时 B 已经创建结束,直接从一级缓存里面拿到 proxyB ,完成对 A 对象的注入,以及对 原始 A 对象初始化操作(initializeBean

    image-20221121184825150

    // 6. 存在提前曝光情况下
    if (earlySingletonExposure) {
    	// earlySingletonReference:二级缓存,缓存的是经过提前曝光提前Spring AOP代理的bean
    	Object earlySingletonReference = getSingleton(beanName, false);
        if (earlySingletonReference != null) {
            // exposedObject跟bean一样,
            //		说明初始化操作没用应用 getEarlyBeanReference (指AOP操作) 改变 exposedObject
            // 主要是因为exposedObject如果提前代理过,就会跳过Spring AOP代理,
            //		所以exposedObject没被改变,也就等于bean了
    		if (exposedObject == bean) {
                // 将二级缓存中的提前AOP代理的bean赋值给exposedObject,并返回
    			exposedObject = earlySingletonReference;
    		}
    		// 引用都不相等了,也就是现在的bean已经不是当时提前曝光的bean了
    		else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
    				// dependentBeans也就是B, C, D
    				String[] dependentBeans = getDependentBeans(beanName);
    				Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    				for (String dependentBean : dependentBeans) {
    					if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
    						actualDependentBeans.add(dependentBean);
    					}
    				}
    				// 被依赖检测异常
    				if (!actualDependentBeans.isEmpty()) {
    					throw new BeanCurrentlyInCreationException(...);
    					}
    				}
    			}
    		}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    对于我们这里:

    image-20221121202934389

    此时 exposedObjectproxyA

    最后将 proxyA 对象加入到一级缓存中( addSingleton(beanName, proxyB); )删除二级缓存中的 proxyA

    完成 proxyA 对象的创建

    5、注意事项

    通过三级缓存拿到 ObjectFactory 对象后,调用 ObjectFactory.getObject() 方法最终会调用getEarlyBeanReference() 方法,我们会发现再执行一遍 singletonFactory.getObject() 方法又是一个新的代理对象,这就会有问题了,

    • 因为 A 是单例的,每次执行 singletonFactory.getObject() 方法又会产生新的代理对象,
    • 假设这里只有一级和三级缓存的话,我每次从三级缓存中拿到 singleFactory 对象,执行 getObject() 方法又会产生新的代理对象,这是不行的,

    因为 A 是单例的,所有这里我们要借助二级缓存来解决这个问题,将执行 singletonFactory.getObject() 产生的对象放到二级缓存中去,后面去二级缓存中拿,没必要再执行一遍 singletonFactory.getObject() 方法再产生一个新的代理对象,保证始终只有一个代理对象。

    还有一个注意的点

    • 既然 singletonFactory.getObject() 返回的是代理对象,那么注入的也应该是代理对象,我们可以看到注入的确实是经过 CGLIB 代理的 A 对象。
    • 所以如果没有 AOP 的话确实可以两级缓存就可以解决循环依赖的问题,
    • 如果加上 AOP ,两级缓存是无法解决的,不可能每次执行 singleFactory.getObject() 方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。

    绝大部分情况下,都是在 initializeBean(初始化 bean)进行创建代理对象的。只有循环依赖的时候,才会用到三级缓存,进行提前代理对象,也就是 getEarlyBeanReference

    6、流程图

    beanAop

  • 相关阅读:
    Anaconda安装+Tensorflow2.0安装配置+Pycharm安装+GCN调试(Window 10)
    数据集笔记:GeoLife GPS 数据 (user guide)
    兴顺物流管理系统的设计
    flex的用法 代码6
    20.单例模式进阶
    GPT-4o有点坑
    【MyBatis Plus】初识 MyBatis Plus,在 Spring Boot 项目中集成 MyBatis Plus,理解常用注解以及常见配置
    堆的实现(C语言)
    《公共管理学》考试重点及答案
    Linux内核设计与实现 第三章 进程管理
  • 原文地址:https://blog.csdn.net/qq_67720621/article/details/127973988