<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      spring詳解-循環依賴的解決

      Spring循環依賴

      重點提示: 本文都快寫完了,發現“丈夫” 的英文是husband....... 在“②有AOP循環依賴” 改過來了,前面用到的位置太多了就沒改。我是說怎么idea的hansband英文下面怎么有波浪線。各位能夠理解意思就行,英文拼寫不要過于在意.

      1.案例引入

      這篇文章中,"②容器刷新"這一小節,留下了如下這樣一個疑問。【https://blog.csdn.net/okok__TXF/article/details/147009731】

      // DefaultSingletonBeanRegistry.java
      @Nullable
      protected Object getSingleton(String beanName, boolean allowEarlyReference) {
          // Quick check for existing instance without full singleton lock
          Object singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
              singletonObject = this.earlySingletonObjects.get(beanName);
              if (singletonObject == null && allowEarlyReference) {
                  synchronized (this.singletonObjects) {
                      // Consistent creation of early reference within full singleton lock
                      singletonObject = this.singletonObjects.get(beanName);
                      if (singletonObject == null) {
                          singletonObject = this.earlySingletonObjects.get(beanName);
                          if (singletonObject == null) {
                              ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                              if (singletonFactory != null) {
                                  singletonObject = singletonFactory.getObject();
                                  this.earlySingletonObjects.put(beanName, singletonObject);
                                  this.singletonFactories.remove(beanName);
                              }
                          }
                      }
                  }
              }
          }
          return singletonObject;
      }
      ————————————————
      版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
      原文鏈接: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的循環依賴

      下面舉一個例子,一個丈夫和一個妻子的循環依賴。

      // 這是Main測試類
      @Configuration
      @ComponentScan("com.feng.myspring")
      public class Main {
          public static void main(String[] args) {
              ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
              Hansband h = context.getBean(Hansband.class);
              h.say();
          }
      }
      
      //1.丈夫
      public class Hansband {
          @Autowired
          private Wife wife;
          public void say() {
              System.out.println("我是丈夫");
              wife.eat();
          }
          public Hansband( ) {
          }
          public void eat() {
              System.out.println("丈夫吃飯");
          }
      }
      //2.妻子
      public class Wife {
          @Autowired
          private Hansband hansband;
          public void say(){
              System.out.println("我是妻子");
              hansband.eat();
          }
          public Wife() {
          }
          public void eat() {
              System.out.println("妻子吃飯");
          }
      }
      

      然后再配置類里面創建了一個丈夫和妻子對象

      @Configuration
      public class Config01 {
          @Bean
          public Hansband hansband(){
              return new Hansband();
          }
          @Bean
          public Wife wife(){
              return new Wife();
          }
      }
      

      然后上面的項目創建好了之后,現在就開始分析如下方法

      // DefaultSingletonBeanRegistry.java
      @Nullable
      protected Object getSingleton(String beanName, boolean allowEarlyReference) {
          // Quick check for existing instance without full singleton lock
          Object singletonObject = this.singletonObjects.get(beanName);
          if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
              singletonObject = this.earlySingletonObjects.get(beanName);
              if (singletonObject == null && allowEarlyReference) {
                  synchronized (this.singletonObjects) {
                      // Consistent creation of early reference within full singleton lock
                      singletonObject = this.singletonObjects.get(beanName);
                      if (singletonObject == null) {
                          singletonObject = this.earlySingletonObjects.get(beanName);
                          if (singletonObject == null) {
                              ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                              if (singletonFactory != null) {
                                  singletonObject = singletonFactory.getObject();
                                  this.earlySingletonObjects.put(beanName, singletonObject);
                                  this.singletonFactories.remove(beanName);
                              }
                          }
                      }
                  }
              }
          }
          return singletonObject;
      }
      

      首先理解一下這其中的變量都是啥。

      • 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)方法的大致流程。然后就沒了?肯定不是的。

      回顧容器刷新

      現在回顧一下容器刷新階段,里面會調用這樣的方法

      // AbstractApplicationContext.java
      protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
      	...
          // Instantiate all remaining (non-lazy-init) singletons.
      	beanFactory.preInstantiateSingletons();
      }
      
      // DefaultListableBeanFactory.java
      @Override
      public void preInstantiateSingletons() throws BeansException {
      	...
          // Trigger initialization of all non-lazy singleton beans...
          for (String beanName : beanNames) {
              RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
              if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                  ...
                  getBean(beanName); // 會調用這個
                  ...
              }
          }
          ...
      }
      
      // AbstractBeanFactory.java
      @Override
      public Object getBean(String name) throws BeansException {
          return doGetBean(name, null, null, false);
      }
      protected <T> T doGetBean(
      			String name, @Nullable Class<T> requiredType, 
          		@Nullable Object[] args, boolean typeCheckOnly)
      			throws BeansException {
       	.......... // ===== 【重要】   
      }
      

      上面最后是到了AbstractBeanFactory::doGetBean(xxx)方法了。下面就來著重分析一下這個doGetBean. 這里只挑重點,并不是一行一行地來。

      // 一、AbstractBeanFactory.java
      protected <T> T doGetBean(
              String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
              throws BeansException {
          ...
          // 1.檢查單例緩存
          Object sharedInstance = getSingleton(beanName);
          if (sharedInstance != null && args == null) {
              ....
              beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
          }
          else {
              //2.原型作用域(prototype) 的 Bean 不支持循環依賴,
              //因為每次獲取都會創建新實例,Spring 無法通過緩存解決循環依賴。
              if (isPrototypeCurrentlyInCreation(beanName)) {
                  throw new BeanCurrentlyInCreationException(beanName);
              }
              ....
              try {
                  .....
                  // 3.Create bean instance.
                  if (mbd.isSingleton()) {
                      // 4.調用了重載的getSingleton(xx,xx)方法===
                      // 第二個參數是lambda表達式
                      sharedInstance = getSingleton(beanName, () -> {
                          try {
                              return createBean(beanName, mbd, args);
                          }
                          catch (BeansException ex) {
                              ....
                          }
                      });
                      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                  }
                  ...
          }
          return adaptBeanInstance(name, beanInstance, requiredType);
      }
          
      // 二、DefaultSingletonBeanRegistry.java ----- 重載的getSingleton
      public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
          ....
          synchronized (this.singletonObjects) {
              Object singletonObject = this.singletonObjects.get(beanName);
              if (singletonObject == null) {
                  .....
                  beforeSingletonCreation(beanName);
                  boolean newSingleton = false;
                  boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
                  if (recordSuppressedExceptions) {
                      this.suppressedExceptions = new LinkedHashSet<>();
                  }
                  try {
                      /* getObject()實際執行的是這個。上面的lambda
                      () -> {
                          try {
                              return createBean(beanName, mbd, args);
                          }
                          catch (BeansException ex) {
                              ....
                          }
                      }
                      */
                      // 實際調用的createBean(beanName, mbd, args);---見下面的三
                      singletonObject = singletonFactory.getObject();
                      newSingleton = true;
                  }
                  catch (IllegalStateException ex) {
                      .....
                  }
                  .....
                  finally {
                      if (recordSuppressedExceptions) {
                          this.suppressedExceptions = null;
                      }
                      afterSingletonCreation(beanName);
                  }
                  if (newSingleton) {
                      addSingleton(beanName, singletonObject);
                  }
              }
              return singletonObject;
          }
      }
          
      // 三、AbstractAutowireCapableBeanFactory.java
      @Override
      protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
              throws BeanCreationException {
      	...
          try {
              // 創建實例bean
              Object beanInstance = doCreateBean(beanName, mbdToUse, args);
              ...
              return beanInstance;
          }
          catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
              ...
          }
          ...
      }
      protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      			throws BeanCreationException {
          // 1,得到bean實例,先從緩存中找,找不到就構造一個
          BeanWrapper instanceWrapper = null;
          //調用 createBeanInstance 方法根據 Bean 定義和構造函數參數創建一個新的 BeanWrapper 實例。
          //從 BeanWrapper 中獲取實際的 Bean 實例和 Bean 的類型,并將類型信息記錄到 Bean 定義中。
          instanceWrapper = createBeanInstance(beanName, mbd, args);
          ....
      
          // 2.low post-processors to modify the merged bean definition.
          // 應用合并后的 Bean 定義后置處理器
          synchronized (mbd.postProcessingLock) {
              ...
              applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
              ...
          }
      
          //3.提前暴露單例 Bean 以處理循環引用
          boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                  isSingletonCurrentlyInCreation(beanName));
          if (earlySingletonExposure) {
              ....
              // 這里是不是就放到了三級緩存中去了,注意一下參數哦。。getEarlyBeanReference
              // 將一個 ObjectFactory 放入單例工廠緩存中,該工廠會調用 getEarlyBeanReference 方法
              // 該方法會返回一個早期的 Bean 引用,以便在循環依賴時可以提前獲取到 Bean 的引用。
              addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
          }
      
          Object exposedObject = bean;
          try {
              // 4.填充bean屬性
              populateBean(beanName, mbd, instanceWrapper);
              // 5.初始化bean【本文開頭的上篇文章】
              exposedObject = initializeBean(beanName, exposedObject, mbd);
          }
          catch (Throwable ex) {
              ......
          }
      
          if (earlySingletonExposure) {
              .....
          }
          ...
          return exposedObject;
      }
      

      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

      // ContextAnnotationAutowireCandidateResolver.java
      // 檢查是否需要延遲解析
      public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
          return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
      }
      

      接著進入doResolveDependency方法里面看看。

      // DefaultListableBeanFactory.java
      @Nullable
      public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
              @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
          ...
          // 一陣噼里啪啦
          if (instanceCandidate instanceof Class) {
              instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
          }
      }
      
      // DependencyDescriptor.java
      public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
      			throws BeansException {
          return beanFactory.getBean(beanName); //
      }
      

      看到這里,明白了沒,一切都回來了!!! 前面不是說過嗎getBean好像就是創建對象嚯。我們在創建Hansband對象的時候,這個時候并沒有創建Wife對象啊,現在兜兜轉轉又回來了。doGetBean() 現在里面是wife了。!!!!【遞歸進去,Hansband對象的創建還是卡在populateBean方法的】

      然后Wife又會走一遍上面的流程。我們在最上面創建好Hansband之后,在填充Hansband屬性之前,有這樣一段代碼。

      //3.提前暴露單例 Bean 以處理循環引用
      boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
              isSingletonCurrentlyInCreation(beanName));
      if (earlySingletonExposure) {
          ....
          // 這里是不是就放到了三級緩存中去了,注意一下參數哦。。getEarlyBeanReference
          // 將一個 ObjectFactory 放入單例工廠緩存中,該工廠會調用 getEarlyBeanReference 方法
          // 該方法會返回一個早期的 Bean 引用,以便在循環依賴時可以提前獲取到 Bean 的引用。
          // 在后續再緩存中查找Bean時會觸發匿名內部類getEarlyBeanReference()方法回調
          addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
      }
      
      //4. 填充屬性
      ..... 
      populateBean(beanName, mbd, instanceWrapper);
      

      創建Wife的時候同理啊,又會調用一遍addSingletonFactory,此時是把wife放到singletonFactories里面了。此時singletonFactories里面就會有兩個鍵值對。

      hansband -- getEarlyBeanReference("hansband", mbd, bean)
      wife -- getEarlyBeanReference("wife", mbd, bean)
      

      然后就輪到執行Wife的屬性填充了,發現需要Hansband的類型的bean,順著上面梳理的流程,最后又會回到下面這里,只不過此時的參數:descriptor就是hansband,beanName是Wife了。

      // DefaultListableBeanFactory.java
      @Nullable
      public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
              @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
          ...
          // 一陣噼里啪啦
          if (instanceCandidate instanceof Class) {
              instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
              // beanFactory.getBean("hansband");  遞歸進去
          }
      }
      

      在Wife的屬性填充的時候,遞歸進去拿Hansband對象,會有如下顯示【這個時候,三級緩存里面就可以看到有兩個鍵值對了,在這里將Hansband的早期對象拿到,并把它從三級緩存移動到二級緩存中去】

      這樣在Wife屬性填充的時候,實現了提前將Hansband曝光給Wife完成屬性依賴注入。緊接著,Wife就可以繼續完成后面的初始化邏輯,產生一個成熟的Wife。

      創建好之后,會addSingleton("wife", xxxx)

      protected void addSingleton(String beanName, Object singletonObject) {
          synchronized (this.singletonObjects) {
              this.singletonObjects.put(beanName, singletonObject); // 放入一級緩存
              this.singletonFactories.remove(beanName); // 移除二三級緩存
              this.earlySingletonObjects.remove(beanName);
              this.registeredSingletons.add(beanName);
          }
      }
      

      請看下面的流程圖解釋。

      上圖流程中,創建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 博客園地址:http://www.rzrgm.cn/jackjavacpp/p/18838920

      看一下上一小節的getEarlyBeanReference(beanName, mbd, bean) 到底做了什么

      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;
      }
      

      在未啟動AOP代理之前,這個方法中的SmartInstantiationAwareBeanPostProcessor如下所示【AutowiredAnnotationBeanPostProcessor::getEarlyBeanReference()方法就是單純的返回bean】

      打開AOP之后,變成了AnnotationAwareAspectJAutoProxyCreator~ 那么它的getEarlyBeanReference()方法有變化嗎?

      // AbstractAutoProxyCreator.java
      @Override
      public Object getEarlyBeanReference(Object bean, String beanName) {
          Object cacheKey = getCacheKey(bean.getClass(), beanName);
          /*========== earlyProxyReferences【重要】
          跟蹤哪些 Bean 的代理對象已在 提前暴露階段 生成,他的主要作用大概如下
          1. 防止重復代理:避免在 Bean 初始化階段重復創建代理對象,
              如果有循環依賴,那么該代理對象在屬性填充階段被創建過了
          2. 保證代理對象一致性:確保循環依賴注入的代理對象與最終暴露的代理對象是同一實例
          */
          this.earlyProxyReferences.put(cacheKey, bean); // ======
          return wrapIfNecessary(bean, beanName, cacheKey); // 下一層
      }
      // 到這里來了
      // 決定是否為給定的 Bean 創建代理對象
      protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
          ....
          // Create proxy if we have advice.
          // getAdvicesAndAdvisorsForBean獲取適用于該 Bean 的通知和增強器
          // 該方法會根據 Bean 的類型和名稱,從 Spring 容器中查找所有匹配的通知和增強器,并返回一個數組
          Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
          // DO_NOT_PROXY 就是 null
          if (specificInterceptors != DO_NOT_PROXY) { // 不為null
              this.advisedBeans.put(cacheKey, Boolean.TRUE);
              //調用 createProxy 方法創建代理對象
              Object proxy = createProxy(
                      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
              this.proxyTypes.put(cacheKey, proxy.getClass());
              return proxy;
          }
      
          this.advisedBeans.put(cacheKey, Boolean.FALSE);
          return bean;
      }
      

      可以大致了解到這個是返回了一個代理對象,可見開啟AOP之前和開啟AOP之后,果然有不一樣。現在我們將案例變成如下有AOP的循環依賴。

      // 接口
      public interface AopMan {
          void aopManSay();
      }
      @Component("husband")
      public class AopHusband implements AopMan{
          @Autowired
          private AopWoman wife;
          @Override
          public void aopManSay() {
              System.out.println("【AOP】Husband say 哦吼");
          }
      }
      
      // 接口
      public interface AopWoman { 
          void aopWomanSay();
      }
      @Component("wife")
      public class AopWife implements AopWoman{
          @Autowired
          private AopMan husband;
          @Override
          public void aopWomanSay() {
              System.out.println("【Aop】Wife say 哈哈");
              husband.aopManSay();
          }
      }
      
      // 創建切面
      @Component
      @Aspect
      public class ManAdvice {
          private static final String manExpression = "execution(* com.feng.myspring.aopobj.*.aopManSay*(..))";
          //環繞通知
          @Around(manExpression)
          public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
              System.out.println("##########【環繞通知中的前置通知】##########");
              Object returnVale = joinPoint.proceed();
              System.out.println("##########【環繞通知中的后置通知】##########");
              return returnVale;
          }
      }
      
      // 主啟動測試類
      @Configuration
      @ComponentScan("com.feng.myspring")
      @EnableAspectJAutoProxy(proxyTargetClass = false)
      public class Main {
          public static void main(String[] args) {
              ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
              // ②有Aop的循環依賴
              AopMan husband = context.getBean("husband", AopMan.class);
              husband.aopManSay();
              System.out.println(husband.getClass()); // 打印一下類型
      
              AopWoman wife = context.getBean("wife", AopWoman.class);
              System.out.println(wife.getClass()); // 打印一下類型
          }
      }
      

      上述例子中,wife的aopWomanSay()方法里面會調用husband的aopManSay方法,但是此時我是對husband的aopManSay配置了環繞通知的,那么請各位想一下此時wife里面注入的husband是原來的對象嗎?肯定不是的,是代理對象,注入的是husband的代理對象,可以運行一下Main測試類看看結果【結果如下】

      【Aop】Wife say 哈哈
      ##########【環繞通知中的前置通知】##########
      【AOP】Husband say 哦吼
      ##########【環繞通知中的后置通知】##########
      class com.sun.proxy.$Proxy18 // husband是一個代理對象
      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 博客園地址:http://www.rzrgm.cn/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這一步的,而我們循環依賴發生在屬性填充這一步。發生循環依賴的時候,需要注入代理對象,但是還沒到代理對象生成那一步

      try {
          // 4.填充bean屬性
          populateBean(beanName, mbd, instanceWrapper);
          // 5.初始化bean【本文開頭的上篇文章】
          exposedObject = initializeBean(beanName, exposedObject, mbd);
      } .....
      

      在 Spring 框架中,三級緩存(singletonFactories)存儲的是 ObjectFactory,而非直接存儲代理對象,這一設計是為了解決循環依賴中代理對象生成的 時機問題

      2.用二級緩存不行嗎?

      循環依賴的簡單場景(無代理) : 是沒問題的

      假設有兩個 Bean:AB,它們互相依賴。Spring 的解決流程如下:

      1. 創建 A:實例化 A(調用構造方法),此時 A 還未完成屬性填充和初始化。
      2. 暴露早期對象:將 A 的原始對象包裝成 ObjectFactory,存入 三級緩存singletonFactories)。
      3. 填充 A 的屬性:發現 A 依賴 B,開始創建 B。
      4. 創建 B:實例化 B,填充 B 的屬性時發現依賴 A。
      5. 從三級緩存獲取 A:通過 ObjectFactory.getObject() 獲取 A 的早期引用(原始對象),注入到 B 中。
      6. 完成 B 的初始化:B 初始化完成后,存入一級緩存(singletonObjects)。
      7. 完成 A 的初始化:將 A 的最終對象存入一級緩存,替換三級緩存中的臨時對象。

      若沒有代理需求,二級緩存(earlySingletonObjects)似乎可以直接存儲原始對象,無需三級緩存。但問題在于:當 Bean 需要被代理時,必須確保注入的是代理對象而非原始對象


      循環依賴 + 代理的復雜場景

      假設 A 和 B 都需要被 AOP 代理(例如被 @Transactional 標記),此時若僅用二級緩存,會引發以下問題:

      1. A 的創建流程
        • 實例化 A(原始對象)。
        • 將 A 的原始對象存入二級緩存(earlySingletonObjects)。
        • 填充屬性時發現依賴 B,開始創建 B。
      2. B 的創建流程
        • 實例化 B(原始對象)。
        • 填充 B 的屬性時,從二級緩存獲取 A 的原始對象(未代理)。
        • 完成 B 的初始化后,生成 B 的代理對象,存入一級緩存。
      3. 完成 A 的初始化
        • 在 A 的初始化后階段(postProcessAfterInitialization),生成 A 的代理對象。
        • 最終緩存中的 A 是代理對象,但 B 中注入的 A 是原始對象,導致不一致

      說實話,我自己都不能說服我自己。寫的好勉強。。。這些疑問還是看這篇文章吧。【參考里面的第二篇文章】

      end.參考

      1. https://mp.weixin.qq.com/s/dSRQBSG42MYNa992PvtnJA 【阿里云開發者 -- 一文詳解Spring Bean循環依賴】

      2. http://www.rzrgm.cn/daimzh/p/13256413.html 【博客園 面試必殺技,講一講Spring中的循環依賴 】質量很高

      3. 原文鏈接:https://blog.csdn.net/chaitoudaren/article/details/105060882 【CSDN Spring源碼最難問題】

      posted @ 2025-05-06 22:03  別來無恙?  閱讀(413)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 色偷偷女人的天堂亚洲网| 亚洲欧洲一区二区免费| 久久久久国精品产熟女久色| av天堂久久精品影音先锋| a∨变态另类天堂无码专区 | 精品乱人伦一区二区三区| 亚洲精品久久无码av片软件 | 国产av仑乱内谢| 延安市| 亚洲一区二区三区啪啪| 欧美日本在线一区二区三区| 成在线人永久免费视频播放 | 最新亚洲国产手机在线| 亚洲激情在线一区二区三区| 果冻传媒色av国产在线播放| 国产精品老熟女乱一区二区| 国产精品欧美福利久久| 国产不卡av一区二区| 青青草国产精品日韩欧美| 最新亚洲精品国偷自产在线| 国内自拍av在线免费| 国产真实野战在线视频| 国产一区二区三区激情视频| 男女爽爽无遮挡午夜视频| 伊人成伊人成综合网222| 国产欧美一区二区精品性色| 99精品国产兔费观看久久99| 爱性久久久久久久久| 榆树市| 亚洲精品漫画一二三区| 亚洲中文一区二区av| 国产精品福利自产拍久久| 狠狠躁夜夜躁无码中文字幕| 影音先锋男人站| 在线a亚洲老鸭窝天堂| 亚洲区综合区小说区激情区| 国内精品伊人久久久久AV一坑| 成人午夜福利视频一区二区| 无码va在线观看| 亚洲综合一区二区精品导航 | 亚洲香蕉网久久综合影视|