spring详解-循环依赖的解决

打印 上一主题 下一主题

主题 1847|帖子 1847|积分 5541

Spring循环依赖

重点提示: 本文都快写完了,发现“丈夫” 的英文是husband....... 在“②有AOP循环依赖” 改过来了,前面用到的位置太多了就没改。我是说怎么idea的hansband英文下面怎么有波浪线。各位能够明白意思就行,英文拼写不要过于在意.
1.案例引入

这篇文章中,"②容器刷新"这一小节,留下了如下如许一个疑问。【https://blog.csdn.net/okok__TXF/article/details/147009731】
  1. // DefaultSingletonBeanRegistry.java
  2. @Nullable
  3. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  4.     // Quick check for existing instance without full singleton lock
  5.     Object singletonObject = this.singletonObjects.get(beanName);
  6.     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  7.         singletonObject = this.earlySingletonObjects.get(beanName);
  8.         if (singletonObject == null && allowEarlyReference) {
  9.             synchronized (this.singletonObjects) {
  10.                 // Consistent creation of early reference within full singleton lock
  11.                 singletonObject = this.singletonObjects.get(beanName);
  12.                 if (singletonObject == null) {
  13.                     singletonObject = this.earlySingletonObjects.get(beanName);
  14.                     if (singletonObject == null) {
  15.                         ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  16.                         if (singletonFactory != null) {
  17.                             singletonObject = singletonFactory.getObject();
  18.                             this.earlySingletonObjects.put(beanName, singletonObject);
  19.                             this.singletonFactories.remove(beanName);
  20.                         }
  21.                     }
  22.                 }
  23.             }
  24.         }
  25.     }
  26.     return singletonObject;
  27. }
  28. ————————————————
  29. 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
  30. 原文链接:https://blog.csdn.net/okok__TXF/article/details/147009731
复制代码
getSingleton(String beanName, boolean allowEarlyReference) 假如allowEarlyReference是true的话,就用三级缓存来解决循环依赖。下面以Spring5.3.31再次来追踪一下源码。
本文章示例代码见该仓库:【spring】中的“spring”模块。
仓库所在:https://gitee.com/quercus-sp204/sourcecode-and-demos
2.循环依赖分析

①无AOP的循环依赖

下面举一个例子,一个丈夫和一个老婆的循环依赖。
  1. // 这是Main测试类
  2. @Configuration
  3. @ComponentScan("com.feng.myspring")
  4. public class Main {
  5.     public static void main(String[] args) {
  6.         ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
  7.         Hansband h = context.getBean(Hansband.class);
  8.         h.say();
  9.     }
  10. }
  11. //1.丈夫
  12. public class Hansband {
  13.     @Autowired
  14.     private Wife wife;
  15.     public void say() {
  16.         System.out.println("我是丈夫");
  17.         wife.eat();
  18.     }
  19.     public Hansband( ) {
  20.     }
  21.     public void eat() {
  22.         System.out.println("丈夫吃饭");
  23.     }
  24. }
  25. //2.妻子
  26. public class Wife {
  27.     @Autowired
  28.     private Hansband hansband;
  29.     public void say(){
  30.         System.out.println("我是妻子");
  31.         hansband.eat();
  32.     }
  33.     public Wife() {
  34.     }
  35.     public void eat() {
  36.         System.out.println("妻子吃饭");
  37.     }
  38. }
复制代码
然后再设置类内里创建了一个丈夫和老婆对象
  1. @Configuration
  2. public class Config01 {
  3.     @Bean
  4.     public Hansband hansband(){
  5.         return new Hansband();
  6.     }
  7.     @Bean
  8.     public Wife wife(){
  9.         return new Wife();
  10.     }
  11. }
复制代码
然后上面的项目创建好了之后,现在就开始分析如下方法
  1. // DefaultSingletonBeanRegistry.java
  2. @Nullable
  3. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  4.     // Quick check for existing instance without full singleton lock
  5.     Object singletonObject = this.singletonObjects.get(beanName);
  6.     if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  7.         singletonObject = this.earlySingletonObjects.get(beanName);
  8.         if (singletonObject == null && allowEarlyReference) {
  9.             synchronized (this.singletonObjects) {
  10.                 // Consistent creation of early reference within full singleton lock
  11.                 singletonObject = this.singletonObjects.get(beanName);
  12.                 if (singletonObject == null) {
  13.                     singletonObject = this.earlySingletonObjects.get(beanName);
  14.                     if (singletonObject == null) {
  15.                         ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  16.                         if (singletonFactory != null) {
  17.                             singletonObject = singletonFactory.getObject();
  18.                             this.earlySingletonObjects.put(beanName, singletonObject);
  19.                             this.singletonFactories.remove(beanName);
  20.                         }
  21.                     }
  22.                 }
  23.             }
  24.         }
  25.     }
  26.     return singletonObject;
  27. }
复制代码
首先明白一下这其中的变量都是啥。

  • singletonObjects:单例对象的缓存,bean名称对应一个bean实例。主要存放的是已经完成实例化、属性填充和初始化所有步骤的单例Bean实例,如许的Bean能够直接提供给用户使用,称之为终态Bean或叫成熟Bean。【一级缓存】
  • earlySingletonObjects:早期单例对象的缓存,bean名称对应一个bean实例。主要存放的已经完成实例化,但属性还没主动赋值的Bean,这些Bean还不能提供用户使用,只是用于提前暴露的Bean实例,把如许的Bean称之为临时Bean或早期的Bean(半成品Bean) 【二级缓存】
  • singletonFactories:单例工厂的缓存,bean名称对应一个对象工厂。存放的是ObjectFactory的匿名内部类实例,调用ObjectFactory.getObject()最终会调用getEarlyBeanReference方法,该方法可以获取提前暴露的单例bean引用。【三级缓存】

上图是getSingleton(String beanName, boolean allowEarlyReference)方法的大抵流程。然后就没了?肯定不是的。
回顾容器刷新

现在回顾一下容器刷新阶段,内里会调用如许的方法
  1. // AbstractApplicationContext.java
  2. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  3.         ...
  4.     // Instantiate all remaining (non-lazy-init) singletons.
  5.         beanFactory.preInstantiateSingletons();
  6. }
  7. // DefaultListableBeanFactory.java
  8. @Override
  9. public void preInstantiateSingletons() throws BeansException {
  10.         ...
  11.     // Trigger initialization of all non-lazy singleton beans...
  12.     for (String beanName : beanNames) {
  13.         RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  14.         if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  15.             ...
  16.             getBean(beanName); // 会调用这个
  17.             ...
  18.         }
  19.     }
  20.     ...
  21. }
  22. // AbstractBeanFactory.java
  23. @Override
  24. public Object getBean(String name) throws BeansException {
  25.     return doGetBean(name, null, null, false);
  26. }
  27. protected <T> T doGetBean(
  28.                         String name, @Nullable Class<T> requiredType,
  29.                     @Nullable Object[] args, boolean typeCheckOnly)
  30.                         throws BeansException {
  31.         .......... // ===== 【重要】   
  32. }
复制代码
上面最后是到了AbstractBeanFactory::doGetBean(xxx)方法了。下面就来着重分析一下这个doGetBean. 这里只挑重点,并不是一行一行地来。
  1. // 一、AbstractBeanFactory.java
  2. protected <T> T doGetBean(
  3.         String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  4.         throws BeansException {
  5.     ...
  6.     // 1.检查单例缓存
  7.     Object sharedInstance = getSingleton(beanName);
  8.     if (sharedInstance != null && args == null) {
  9.         ....
  10.         beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  11.     }
  12.     else {
  13.         //2.原型作用域(prototype) 的 Bean 不支持循环依赖,
  14.         //因为每次获取都会创建新实例,Spring 无法通过缓存解决循环依赖。
  15.         if (isPrototypeCurrentlyInCreation(beanName)) {
  16.             throw new BeanCurrentlyInCreationException(beanName);
  17.         }
  18.         ....
  19.         try {
  20.             .....
  21.             // 3.Create bean instance.
  22.             if (mbd.isSingleton()) {
  23.                 // 4.调用了重载的getSingleton(xx,xx)方法===
  24.                 // 第二个参数是lambda表达式
  25.                 sharedInstance = getSingleton(beanName, () -> {
  26.                     try {
  27.                         return createBean(beanName, mbd, args);
  28.                     }
  29.                     catch (BeansException ex) {
  30.                         ....
  31.                     }
  32.                 });
  33.                 beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  34.             }
  35.             ...
  36.     }
  37.     return adaptBeanInstance(name, beanInstance, requiredType);
  38. }
  39.    
  40. // 二、DefaultSingletonBeanRegistry.java ----- 重载的getSingleton
  41. public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  42.     ....
  43.     synchronized (this.singletonObjects) {
  44.         Object singletonObject = this.singletonObjects.get(beanName);
  45.         if (singletonObject == null) {
  46.             .....
  47.             beforeSingletonCreation(beanName);
  48.             boolean newSingleton = false;
  49.             boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
  50.             if (recordSuppressedExceptions) {
  51.                 this.suppressedExceptions = new LinkedHashSet<>();
  52.             }
  53.             try {
  54.                 /* getObject()实际执行的是这个。上面的lambda
  55.                 () -> {
  56.                     try {
  57.                         return createBean(beanName, mbd, args);
  58.                     }
  59.                     catch (BeansException ex) {
  60.                         ....
  61.                     }
  62.                 }
  63.                 */
  64.                 // 实际调用的createBean(beanName, mbd, args);---见下面的三
  65.                 singletonObject = singletonFactory.getObject();
  66.                 newSingleton = true;
  67.             }
  68.             catch (IllegalStateException ex) {
  69.                 .....
  70.             }
  71.             .....
  72.             finally {
  73.                 if (recordSuppressedExceptions) {
  74.                     this.suppressedExceptions = null;
  75.                 }
  76.                 afterSingletonCreation(beanName);
  77.             }
  78.             if (newSingleton) {
  79.                 addSingleton(beanName, singletonObject);
  80.             }
  81.         }
  82.         return singletonObject;
  83.     }
  84. }
  85.    
  86. // 三、AbstractAutowireCapableBeanFactory.java
  87. @Override
  88. protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  89.         throws BeanCreationException {
  90.         ...
  91.     try {
  92.         // 创建实例bean
  93.         Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  94.         ...
  95.         return beanInstance;
  96.     }
  97.     catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
  98.         ...
  99.     }
  100.     ...
  101. }
  102. protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
  103.                         throws BeanCreationException {
  104.     // 1,得到bean实例,先从缓存中找,找不到就构造一个
  105.     BeanWrapper instanceWrapper = null;
  106.     //调用 createBeanInstance 方法根据 Bean 定义和构造函数参数创建一个新的 BeanWrapper 实例。
  107.     //从 BeanWrapper 中获取实际的 Bean 实例和 Bean 的类型,并将类型信息记录到 Bean 定义中。
  108.     instanceWrapper = createBeanInstance(beanName, mbd, args);
  109.     ....
  110.     // 2.low post-processors to modify the merged bean definition.
  111.     // 应用合并后的 Bean 定义后置处理器
  112.     synchronized (mbd.postProcessingLock) {
  113.         ...
  114.         applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  115.         ...
  116.     }
  117.     //3.提前暴露单例 Bean 以处理循环引用
  118.     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  119.             isSingletonCurrentlyInCreation(beanName));
  120.     if (earlySingletonExposure) {
  121.         ....
  122.         // 这里是不是就放到了三级缓存中去了,注意一下参数哦。。getEarlyBeanReference
  123.         // 将一个 ObjectFactory 放入单例工厂缓存中,该工厂会调用 getEarlyBeanReference 方法
  124.         // 该方法会返回一个早期的 Bean 引用,以便在循环依赖时可以提前获取到 Bean 的引用。
  125.         addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  126.     }
  127.     Object exposedObject = bean;
  128.     try {
  129.         // 4.填充bean属性
  130.         populateBean(beanName, mbd, instanceWrapper);
  131.         // 5.初始化bean【本文开头的上篇文章】
  132.         exposedObject = initializeBean(beanName, exposedObject, mbd);
  133.     }
  134.     catch (Throwable ex) {
  135.         ......
  136.     }
  137.     if (earlySingletonExposure) {
  138.         .....
  139.     }
  140.     ...
  141.     return exposedObject;
  142. }
复制代码
doCreateBean 方法是 Spring 框架中用于实际创建 Bean 实例的核心方法。它在 AbstractAutowireCapableBeanFactory 类中实现。该方法承担了创建 Bean 实例、属性填充、初始化以及处置惩罚循环依赖等重要任务。从最后一个方法我们可以大抵总结三个关于spring创建bean的核心步骤,从上到下按次序。

  • 实例化Bean : 可以明白为new一个空的bean对象 【createBeanInstance】
  • 填充Bean属性 : 对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用。【populateBean】
  • 调用Bean初始化方法 : @PostConstruct、init-metohd、等等【initializeBean】
这一节我们从最开始的getBean(beanName);,一直往下,发现getBean似乎就是创建对象嚯。 【这句话后面会用到的。。。】
示例分析

那么案例中,我们是字段注入的,肯定就是发生在“填充Bean属性” 这个阶段才会产生的循环依赖问题!!接下来就以上面的“丈夫、老婆”为例子。

上图是正在创建Hansband的bean对象、到了属性填充这个步骤了。进入这个populateBean方法内里,

上图是populateBean内里,调用这个AutowiredAnnotationBeanPostProcessor :: postProcessProperties(xx)方法。

上图中,在这个方法内里,调用 findAutowiringMetadata 方法,根据 Bean 的名称、类型以及属性值集合来查找主动注入的元数据。主动注入元数据包罗了 Bean 中需要主动注入的字段、方法等信息,这些信息是通过 @Autowired、@Resource 等注解标记的。可以发现绿色箭头指向的是wife了。随后,调用metadata.inject(bean, beanName, pvs);来执行执行依赖注入操作。

上图是metadata.inject内里的内容,对目的对象执行依赖注入操作。for循环遍历注入元素并执行注入操作。 element.inject(target, beanName, pvs);。

上图中,最后假如value不是null,field.set(bean, value):将解析好的依赖值注入到目的 Bean 的字段中。以是重点应该是上面的resolveFieldValue 核心逻辑

上图是解析字段的核心逻辑。
首先, 创建依赖描述符(DependencyDescriptor),可以看到将field传了进去,【field很明显就是wife嘛,上图中也可以看出来】。
然后,准备依赖解析环境....这个就不说了。其次,调用了这一行代码 beanFactory.resolveDependency(xxx);,这个是Spring 依赖解析的核心方法,根据 DependencyDescriptor 查找匹配的依赖值【可以很明显感觉到,这个又是核心逻辑了】。 找到依赖值后,会缓存起来。
最后会返回这个value嘛。

上图是beanFactory.resolveDependency,先会检查是否需要延迟解析代理(处置惩罚 @Lazy 等场景),若无需延迟解析,返回 null,进入下一步。然后,调用核心解析方法 doResolveDependency
  1. // ContextAnnotationAutowireCandidateResolver.java
  2. // 检查是否需要延迟解析
  3. public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
  4.     return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
  5. }
复制代码
接着进入doResolveDependency方法内里看看。
  1. // DefaultListableBeanFactory.java
  2. @Nullable
  3. public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  4.         @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  5.     ...
  6.     // 一阵噼里啪啦
  7.     if (instanceCandidate instanceof Class) {
  8.         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  9.     }
  10. }
  11. // DependencyDescriptor.java
  12. public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
  13.                         throws BeansException {
  14.     return beanFactory.getBean(beanName); //
  15. }
复制代码
看到这里,明白了没,统统都回来了!!! 前面不是说过吗getBean似乎就是创建对象嚯。我们在创建Hansband对象的时候,这个时候并没有创建Wife对象啊,现在兜兜转转又回来了。doGetBean() 现在内里是wife了。!!!!【递归进去,Hansband对象的创建还是卡在populateBean方法的】

然后Wife又会走一遍上面的流程。我们在最上面创建好Hansband之后,在填充Hansband属性之前,有如许一段代码。
  1. //3.提前暴露单例 Bean 以处理循环引用
  2. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  3.         isSingletonCurrentlyInCreation(beanName));
  4. if (earlySingletonExposure) {
  5.     ....
  6.     // 这里是不是就放到了三级缓存中去了,注意一下参数哦。。getEarlyBeanReference
  7.     // 将一个 ObjectFactory 放入单例工厂缓存中,该工厂会调用 getEarlyBeanReference 方法
  8.     // 该方法会返回一个早期的 Bean 引用,以便在循环依赖时可以提前获取到 Bean 的引用。
  9.     // 在后续再缓存中查找Bean时会触发匿名内部类getEarlyBeanReference()方法回调
  10.     addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  11. }
  12. //4. 填充属性
  13. .....
  14. populateBean(beanName, mbd, instanceWrapper);
复制代码
创建Wife的时候同理啊,又会调用一遍addSingletonFactory,此时是把wife放到singletonFactories内里了。此时singletonFactories内里就会有两个键值对。
  1. hansband -- getEarlyBeanReference("hansband", mbd, bean)
  2. wife -- getEarlyBeanReference("wife", mbd, bean)
复制代码
然后就轮到执行Wife的属性填充了,发现需要Hansband的类型的bean,顺着上面梳理的流程,最后又会回到下面这里,只不过此时的参数:descriptor就是hansband,beanName是Wife了。
  1. // DefaultListableBeanFactory.java
  2. @Nullable
  3. public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  4.         @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  5.     ...
  6.     // 一阵噼里啪啦
  7.     if (instanceCandidate instanceof Class) {
  8.         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  9.         // beanFactory.getBean("hansband");  递归进去
  10.     }
  11. }
复制代码
在Wife的属性填充的时候,递归进去拿Hansband对象,会有如下显示【这个时候,三级缓存内里就可以看到有两个键值对了,在这里将Hansband的早期对象拿到,并把它从三级缓存移动到二级缓存中去】

如许在Wife属性填充的时候,实现了提前将Hansband曝光给Wife完成属性依赖注入。紧接着,Wife就可以继续完成后面的初始化逻辑,产生一个成熟的Wife。
创建好之后,会addSingleton("wife",  xxxx)
  1. protected void addSingleton(String beanName, Object singletonObject) {
  2.     synchronized (this.singletonObjects) {
  3.         this.singletonObjects.put(beanName, singletonObject); // 放入一级缓存
  4.         this.singletonFactories.remove(beanName); // 移除二三级缓存
  5.         this.earlySingletonObjects.remove(beanName);
  6.         this.registeredSingletons.add(beanName);
  7.     }
  8. }
复制代码
请看下面的流程图表明。

上图流程中,创建wife的时候,提前注入了一个hansband还没有初始化好的对象【注入了一个早期bean】,如许不会有什么问题吗?
wife创建完全之后,会返回到hansband的“bean初始化阶段”,然后hansband就会初始化ok并放入单例池。由于wife中的早期bean和 创建hansband中的bean是同一个引用,故没有啥问题的。
嘶,看了一下,要三级缓存有这个必要吗?图中二级缓存干啥的,getEarlyBeanReference经过调试,发现就是返回了一个bean。看来还是疑点重重,请看下一节。
②有AOP的循环依赖

在上面普通bean的循环依赖场景下,可以看出三级缓存貌似并没有什么卵用。【实际上确实是的,在普通的循环依赖的情况下,三级缓存没有任何作用。
】经过反复参考求证,发现三级缓存是和spring 的 AOP挂钩的!
AOP  CSDN所在:https://blog.csdn.net/okok__TXF/article/details/147397816
AOP 博客园所在:https://www.cnblogs.com/jackjavacpp/p/18838920
看一下上一小节的getEarlyBeanReference(beanName, mbd, bean) 到底做了什么
  1. protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
  2.     Object exposedObject = bean;
  3.     if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  4.         for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
  5.             exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
  6.         }
  7.     }
  8.     return exposedObject;
  9. }
复制代码
在未启动AOP代理之前,这个方法中的SmartInstantiationAwareBeanPostProcessor如下所示【AutowiredAnnotationBeanPostProcessor::getEarlyBeanReference()方法就是单纯的返回bean】

打开AOP之后,变成了AnnotationAwareAspectJAutoProxyCreator~ 那么它的getEarlyBeanReference()方法有变化吗?
  1. // AbstractAutoProxyCreator.java
  2. @Override
  3. public Object getEarlyBeanReference(Object bean, String beanName) {
  4.     Object cacheKey = getCacheKey(bean.getClass(), beanName);
  5.     /*========== earlyProxyReferences【重要】
  6.     跟踪哪些 Bean 的代理对象已在 提前暴露阶段 生成,他的主要作用大概如下
  7.     1. 防止重复代理:避免在 Bean 初始化阶段重复创建代理对象,
  8.         如果有循环依赖,那么该代理对象在属性填充阶段被创建过了
  9.     2. 保证代理对象一致性:确保循环依赖注入的代理对象与最终暴露的代理对象是同一实例
  10.     */
  11.     this.earlyProxyReferences.put(cacheKey, bean); // ======
  12.     return wrapIfNecessary(bean, beanName, cacheKey); // 下一层
  13. }
  14. // 到这里来了
  15. // 决定是否为给定的 Bean 创建代理对象
  16. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  17.     ....
  18.     // Create proxy if we have advice.
  19.     // getAdvicesAndAdvisorsForBean获取适用于该 Bean 的通知和增强器
  20.     // 该方法会根据 Bean 的类型和名称,从 Spring 容器中查找所有匹配的通知和增强器,并返回一个数组
  21.     Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  22.     // DO_NOT_PROXY 就是 null
  23.     if (specificInterceptors != DO_NOT_PROXY) { // 不为null
  24.         this.advisedBeans.put(cacheKey, Boolean.TRUE);
  25.         //调用 createProxy 方法创建代理对象
  26.         Object proxy = createProxy(
  27.                 bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  28.         this.proxyTypes.put(cacheKey, proxy.getClass());
  29.         return proxy;
  30.     }
  31.     this.advisedBeans.put(cacheKey, Boolean.FALSE);
  32.     return bean;
  33. }
复制代码
可以大抵相识到这个是返回了一个代理对象,可见开启AOP之前和开启AOP之后,果然有不一样。现在我们将案例变成如下有AOP的循环依赖。
  1. // 接口
  2. public interface AopMan {
  3.     void aopManSay();
  4. }
  5. @Component("husband")
  6. public class AopHusband implements AopMan{
  7.     @Autowired
  8.     private AopWoman wife;
  9.     @Override
  10.     public void aopManSay() {
  11.         System.out.println("【AOP】Husband say 哦吼");
  12.     }
  13. }
  14. // 接口
  15. public interface AopWoman {
  16.     void aopWomanSay();
  17. }
  18. @Component("wife")
  19. public class AopWife implements AopWoman{
  20.     @Autowired
  21.     private AopMan husband;
  22.     @Override
  23.     public void aopWomanSay() {
  24.         System.out.println("【Aop】Wife say 哈哈");
  25.         husband.aopManSay();
  26.     }
  27. }
  28. // 创建切面
  29. @Component
  30. @Aspect
  31. public class ManAdvice {
  32.     private static final String manExpression = "execution(* com.feng.myspring.aopobj.*.aopManSay*(..))";
  33.     //环绕通知
  34.     @Around(manExpression)
  35.     public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
  36.         System.out.println("##########【环绕通知中的前置通知】##########");
  37.         Object returnVale = joinPoint.proceed();
  38.         System.out.println("##########【环绕通知中的后置通知】##########");
  39.         return returnVale;
  40.     }
  41. }
  42. // 主启动测试类
  43. @Configuration
  44. @ComponentScan("com.feng.myspring")
  45. @EnableAspectJAutoProxy(proxyTargetClass = false)
  46. public class Main {
  47.     public static void main(String[] args) {
  48.         ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
  49.         // ②有Aop的循环依赖
  50.         AopMan husband = context.getBean("husband", AopMan.class);
  51.         husband.aopManSay();
  52.         System.out.println(husband.getClass()); // 打印一下类型
  53.         AopWoman wife = context.getBean("wife", AopWoman.class);
  54.         System.out.println(wife.getClass()); // 打印一下类型
  55.     }
  56. }
复制代码
上述例子中,wife的aopWomanSay()方法内里会调用husband的aopManSay方法,但是此时我是对husband的aopManSay设置了环绕通知的,那么请各位想一下此时wife内里注入的husband是原来的对象吗?肯定不是的,是代理对象,注入的是husband的代理对象,可以运行一下Main测试类看看效果【效果如下】
  1. 【Aop】Wife say 哈哈
  2. ##########【环绕通知中的前置通知】##########
  3. 【AOP】Husband say 哦吼
  4. ##########【环绕通知中的后置通知】##########
  5. class com.sun.proxy.$Proxy18 // husband是一个代理对象
  6. class com.feng.myspring.aopobj.AopWife// wife仍然是原来
复制代码
我们都知道springbean的创建过程,是 1.缓存查询;2.创建对象;3.属性填充、注入依赖;4.执行初始化操作;5.这个bean创建好了。大抵如许五个过程,在没有循环依赖的条件下,开启aop会生成bean的代理对象,这个生成代理对象的时机是在 “4.执行初始化操作” 这一步的,但是本文讨论的都是在第三步哦~~【AOP见下面文章】。以是,三级缓存的存在就是为了提前用对象工厂获取代理对象,并赋值给wife的husband属性【代理对象】依赖注入。
AOP  CSDN所在:https://blog.csdn.net/okok__TXF/article/details/147397816
AOP 博客园所在:https://www.cnblogs.com/jackjavacpp/p/18838920
回到本小节开始的时候,开启aop之后,AnnotationAwareAspectJAutoProxyCreator通过getEarlyBeanReference内里的wrapIfNecessary拿到的代理实例,我们对husband进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,如许就意味着wife中注入的husband将是一个代理对象而不是husband的实例化阶段创建后的对象。
Spring是在何处将husband的代理对象放进去的呢?

在完成husband初始化后,Spring又调用了一次getSingleton方法,允许早期引用为false。在前面分析的时候在为wife中注入husband时已经将三级缓存中的工厂取出,并从工厂中获取到了一个husband代理对象放入到了二级缓存中,并且从三级缓存中移除掉,以是这里的这个getSingleton方法做的时间就是从二级缓存中获取到这个代理后的husband对象。
3.疑问

1.为什么三级缓存要弄一个对象工厂添加进去,我直接往三级缓存放入对象的代理对象不行吗?
这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提宿世成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。
读者在此处思考一下,假如看过AOP,那么肯定就会知道,代理对象的生成是在bean的初始化操作中的后置处置惩罚器的postProcessAfterInitialization这一步的,而我们循环依赖发生在属性填充这一步。发生循环依赖的时候,需要注入代理对象,但是还没到代理对象生成那一步
  1. try {
  2.     // 4.填充bean属性
  3.     populateBean(beanName, mbd, instanceWrapper);
  4.     // 5.初始化bean【本文开头的上篇文章】
  5.     exposedObject = initializeBean(beanName, exposedObject, mbd);
  6. } .....
复制代码
在 Spring 框架中,三级缓存(singletonFactories)存储的是 ObjectFactory,而非直接存储代理对象,这一计划是为相识决循环依赖中代理对象生成的 时机问题
2.用二级缓存不行吗?
循环依赖的简单场景(无代理) : 是没问题的
假设有两个 Bean:AB,它们相互依赖。Spring 的解决流程如下:

  • 创建 A:实例化 A(调用构造方法),此时 A 还未完成属性填充和初始化。
  • 暴露早期对象:将 A 的原始对象包装成 ObjectFactory,存入 三级缓存(singletonFactories)。
  • 填充 A 的属性:发现 A 依赖 B,开始创建 B。
  • 创建 B:实例化 B,填充 B 的属性时发现依赖 A。
  • 从三级缓存获取 A:通过 ObjectFactory.getObject() 获取 A 的早期引用(原始对象),注入到 B 中。
  • 完成 B 的初始化:B 初始化完成后,存入一级缓存(singletonObjects)。
  • 完成 A 的初始化:将 A 的最终对象存入一级缓存,更换三级缓存中的临时对象。
若没有代理需求,二级缓存(earlySingletonObjects)似乎可以直接存储原始对象,无需三级缓存。但问题在于:当 Bean 需要被代理时,必须确保注入的是代理对象而非原始对象
循环依赖 + 代理的复杂场景
假设 A 和 B 都需要被 AOP 代理(例如被 @Transactional 标记),此时若仅用二级缓存,会引发以下问题:

  • A 的创建流程

    • 实例化 A(原始对象)。
    • 将 A 的原始对象存入二级缓存(earlySingletonObjects)。
    • 填充属性时发现依赖 B,开始创建 B。

  • B 的创建流程

    • 实例化 B(原始对象)。
    • 填充 B 的属性时,从二级缓存获取 A 的原始对象(未代理)。
    • 完成 B 的初始化后,生成 B 的代理对象,存入一级缓存。

  • 完成 A 的初始化

    • 在 A 的初始化后阶段(postProcessAfterInitialization),生成 A 的代理对象。
    • 最终缓存中的 A 是代理对象,但 B 中注入的 A 是原始对象,导致不一致

说实话,我自己都不能说服我自己。写的好勉强。。。这些疑问还是看这篇文章吧。【参考内里的第二篇文章】
end.参考


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

种地

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表