1. 程式人生 > 其它 >Spring IOC---AOP代理物件生成的時機

Spring IOC---AOP代理物件生成的時機

技術標籤:# Spring原始碼解析springjavaaopioc

文章目錄

1.概述

Spring AOP可以採用註解或者xml配置的方式實現,那麼在spring的生命週期當中,是在什麼時候生成的代理物件呢?本文就AOP代理物件生成的時機進行介紹。不清楚spring生命週期的讀者可以先閱讀另一篇部落格《Spring IOC—Bean的生命週期》

2.前置知識

  • BeanPostProcessor介面的作用

簡單的講就是在一個物件初始化的前後做一些事情,裡面有兩個方法,一個是postProcessBeforeInitialization,例項化、依賴注入完畢,在呼叫顯示的初始化之前完成一些定製的初始化任務。另一個是postProcessAfterInitialization,例項化、依賴注入、初始化完畢時執行

3.Spring AOP代理物件生成的時機

可以分成兩類,提前和非提前生成代理物件。

下面分別介紹這兩類代理物件生成的時機,以及說明為什麼會有這兩種情況。

3.1非提前生成代理物件

下面給個例子,通過除錯程式碼來說明代理的時機,讀者可以跟著一邊除錯一邊閱讀。

//被代理類 A
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {

    public void f(){
        System.out.println("AAAAA");
    }
}
//切面類
package hdu.gongsenlin.
aoptest.aop; @Aspect @Component public class Aop { @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))") private void pointcut(){} @After("pointcut()") public void advice(){ System.out.println("之後增強------------"); } } //配置類 package hdu.
gongsenlin.aoptest; @Configuration @ComponentScan("hdu.gongsenlin.aoptest") @EnableAspectJAutoProxy public class Appconfig { } //啟動類 package hdu.gongsenlin.aoptest; public class AoptestApplication { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class); A a = ac.getBean(A.class); a.f(); } }

上面的程式碼AOP會作用在A物件上,也就是生成A的代理物件。

這一過程發生在A的生命週期當中,將程式碼定位到AbstractAutowireCapableBeanFactory#doCreateBean方法

瞭解springBean的生命週期的讀者都應該清楚,Bean生命週期中有一步是屬性填充 population,在屬性填充之後會執行initializeBean在這裡插入圖片描述

initializeBean方法中會執行applyBeanPostProcessorsAfterInitialization方法在這裡插入圖片描述

applyBeanPostProcessorsAfterInitialization具體的程式碼如下:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}

邏輯比較簡單,就是遍歷所有實現了BeanPostProcessor介面的類,這裡一共有這7個,一個一個去執行postProcessAfterInitialization方法。其中AnnotationAwareAspectJAutoProxyCreator就是會實現AOP動態代理,然後返回代理物件。在這裡插入圖片描述

AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization程式碼如下

根據bean型別和名字建立快取key,判斷earlyProxyReferences提前動態代理的集合當中存不存在這個快取key,若存在則說明已經進行過動態代理了,則不再進行動態代理,而本例子中,很明顯是沒有執行提前動態代理的,所以會執行wrapIfNecessary方法進行構建動態代理物件,本文僅介紹執行的時機,具體的動態代理的實現過程暫時先不考慮。

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
    }

    return bean;
}

所以非提前生成代理物件 是在屬性填充populateBean完成之後,執行了initializeBean方法的時候進行的動態代理。

3.2 提前生成代理物件

這種情況比較的複雜,涉及到了迴圈依賴的問題,對於Bean的迴圈依賴不瞭解的讀者,可以先閱讀《Spring IOC—迴圈依賴》這篇部落格再接著閱讀。

同樣的以舉例的方式說明

//切面類 不變
package hdu.gongsenlin.aoptest.aop;
@Aspect
@Component
public class Aop {


    @Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
    private void pointcut(){}
  
    @After("pointcut()")
    public void advice(){
        System.out.println("之後增強------------");
    }
}
//啟動類 不變
public class AoptestApplication {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
        A a = ac.getBean(A.class);
        a.f();
    }
}
//配置類 不變
@Configuration
@ComponentScan("hdu.gongsenlin.aoptest")
@EnableAspectJAutoProxy
public class Appconfig {
}
//被代理物件 A 依賴了 普通物件B
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {

    @Autowired
    B b;

    public void f(){
        System.out.println("AAAAA");
    }
}
// 普通物件B 依賴了 A
package hdu.gongsenlin.aoptest.service;
@Component
public class B {

    @Autowired
    A a;

    public void f(){
        System.out.println("BBBBBBBB");

    }
}

此時給的例子中,A和B相互依賴,形成了迴圈依賴,不同的是A需要被動態代理,而B不需要。

繼續將程式碼定位到建立A的時候,還沒有執行屬性填充populateBean的位置,如下:

在這裡插入圖片描述

因為涉及到了迴圈依賴,所以準備三個框框,來代表迴圈依賴需要用到的三個集合。在這裡插入圖片描述

至於這三個集合的作用 在迴圈依賴的部落格中已經介紹了,這裡就不再贅述。

在上面程式碼執行之前,會將a的工廠物件放入到singletonFactories,此時三個集合的情況如下:

在這裡插入圖片描述

之後執行構建A的populateBean進行屬性填充,發現A依賴於B,而B又不存在於這三個集合當中,所以會遞迴的建立B,呼叫doCreateBean來構建B物件,和A相同,在執行populateBean填充屬性之前,會將b的工廠物件放入到singletonFactories當中,此時三個集合的情況如下:

在這裡插入圖片描述

接著會執行構建B的populateBean進行屬性填充,此時B依賴於A,所以會呼叫doGetBean的去找A物件在這裡插入圖片描述

然後呼叫getSingleton,該方法是解決迴圈依賴的關鍵,程式碼如下:

@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)) {//如果單例池中沒有,並且當前的bean正在建立
      singletonObject = this.earlySingletonObjects.get(beanName);// 看看有沒有提前暴露的不完整的Bean
      if (singletonObject == null && allowEarlyReference) {// 如果還沒有 且允許提前建立
         synchronized (this.singletonObjects) {
            // Consistent creation of early reference within full singleton lock
            singletonObject = this.singletonObjects.get(beanName);// 再檢查一次 singletonObjects 雙重檢查
            if (singletonObject == null) {
               singletonObject = this.earlySingletonObjects.get(beanName);// 再檢查一次 earlySingletonObjects 雙重檢查
               if (singletonObject == null) {//若都還沒有
                  // 則獲取beanName對應的單例工廠
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                     //通過工廠建立一個物件
                     singletonObject = singletonFactory.getObject();
                     //將這個例項化的物件放入到earlySingletonObjects
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     //從單例工廠中移除 這個工廠。
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

一級快取和二級快取中沒有找到A物件,而三級快取singletonFactories當中有A的工廠物件,所以會呼叫

singletonFactory.getObject()來獲得A物件。

這是之前添加工廠物件到singletonFactories的程式碼,所以其實執行getObject()也就是執行了getEarlyBeanReference方法

在這裡插入圖片描述

getEarlyBeanReference的方法程式碼如下:

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

此時又出現了AnnotationAwareAspectJAutoProxyCreator,這裡會執行它的getEarlyBeanReference方法

在這裡插入圖片描述

基於型別和名字 建立快取key,將其放入到earlyProxyReferences集合當中,用於表示進行了提前的動態代理。呼叫wrapIfNecessary來構建動態代理物件。

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean);
    return this.wrapIfNecessary(bean, beanName, cacheKey);
}

此時獲得到了被代理後的A物件,三個集合的結果如下:

在這裡插入圖片描述

之後doCreateBean 構建B的過程結束了,獲得了B物件

在這裡插入圖片描述

之後會將這個物件,新增到singletonObjects集合當中,三個集合的結果如下:

在這裡插入圖片描述

B物件構建完成了,那麼此時A就可以完成它的填充了,所以走完剩下的邏輯之後,三個集合的結果如下:

在這裡插入圖片描述

綜上提前動態代理,其實是在依賴注入的時候,也就是在populateBean屬性填充方法內完成的。

4. 為什麼需要兩個AOP動態代理的時機

沿用3.2的例子,假設動態代理不提前,那麼在構建B物件進行屬性填充的時候,填充的A物件是還沒有進行動態代理的A。

此時B就完成了它的生命週期到了單例池當中,而後A執行完屬性填充之後,再進行動態代理,生成一個被代理的A物件。放入到單例池當中。

此時B中的A和單例池中的被代理的A物件不是同一個物件,這就造成了問題。