Spring源碼分析之AOP代理創建過程

楓林晚粥 發佈 2022-12-01T23:25:42.794276+00:00

使用註解版的AOP實現切面功能時,AOP功能如果要生效,必須先在配置類上使用註解開啟AOP支持,使用到的註解就是**@EnableAspectJAutoProxy**,配置類如下:@Configuration@ComponentScan(basePackages = "com.

使用註解版的Aop實現切面功能時,AOP功能如果要生效,必須先在配置類上使用註解開啟AOP支持,使用到的註解就是**@EnableAspectJAutoProxy**,配置類如下:

@Configuration
@ComponentScan(basePackages = "com.wb.Spring.aop")
// 開啟AOP支持
@EnableAspectJAutoProxy
public class AopConfig {
}

一、AOP的前期初始化過程

1.1、後置處理器初始化過程分析

那麼**@EnableAspectJAutoProxy**註解究竟幹了什麼事呢?打開其源碼實現,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 通過Import註解給容器中導入AspectJAutoProxyRegistrar組件.
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
   /**
    * 用來指定是否使用CGLIB的方式來創建代理
    * 默認值為false,表示使用基於接口的JDK動態代理方式來創建
    */
   Boolean proxyTargetClass() default false;
   /**
    *用來設置是否將代理類暴露到AopContext中。如果將代理暴露在AopContext中,
    *代理類將會被保存在ThreadLocal中,在需要使用代理的時候直接從ThreadLocal中獲取。
    */
   boolean exposeProxy() default false;
}

通過源碼可以發現,在**@EnableAspectJAutoProxy註解中,又使用了@Import註解給容器中導入了一個AspectJAutoProxyRegistrar**組件(關於@Import註解之前文章中已經詳細介紹過,此處不再介紹),那麼這個組件又是幹什麼的呢?繼續打開其源碼,如下:

// 實現了ImportBeanDefinitionRegistrar接口,用來在Spring啟動時給bean註冊中心自定義註冊組件
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      // 如果有需要,則給容器中註冊一個AspectJAnnotationAutoProxyCreator組件.
      AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
      // 獲取EnableAspectJAutoProxy註解上標註的屬性值
      AnnotationAttributes enableAspectJAutoProxy =
            AnnotationConfigUtils.attributesFor(importingClassMetadata, 
                    EnableAspectJAutoProxy.class);
      // ... 其他源碼暫時省略
   }
}

這個組件是給容器的bean定義註冊中心自定義註冊一個bean組件,通過調用AOP工具類中AopConfigUtils中的registerAspectJAnnotationAutoProxyCreatorifNecessary方法完成。

繼續查看方法registerAspectJAnnotationAutoProxyCreatorIfNecessary的實現,內容總共分為如下的三步,最終的效果就是給容器中註冊一個名稱為internalAutoProxyCreator,類型為AnnotationAwareAspectJAutoProxyCreator的bean組件。

public Abstract class AopConfigUtils {
  // 第一步:調用該方法給容器中註冊一個bean組件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
     return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
  }
  // 第二步:再次調用該方法,傳入bean組件的類型為:AnnotationAwareAspectJAutoProxyCreator的bean組件
  @Nullable
  public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        BeanDefinitionRegistry registry, @Nullable Object source) {
      return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
  }
  /**
   * 第二步:最後調用這個方法給容器中註冊一個名稱為org.springframework.aop.config.internalAutoProxyCreator,
   *   類型為AnnotationAwareAspectJAutoProxyCreator的組件
   */
  @Nullable
  private static BeanDefinition registerOrEscalateApcAsRequired(
      Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    // cls = AnnotationAwareAspectJAutoProxyCreator.class
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 判斷容器中是否有名稱為org.springframework.aop.config.internalAutoProxyCreator的Bean定義,第一次運行的時候沒有.
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
      BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
      if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
        int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
        int requiredPriority = findPriorityForClass(cls);
        if (currentPriority < requiredPriority) {
          apcDefinition.setBeanClassName(cls.getName());
        }
      }
      return null;
    }
    // 創建一個AnnotationAwareAspectJAutoProxyCreator類型的Bean定義
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    // 設置最高優先級.
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    // 設置bean定義的名稱為:『org.springframework.aop.config.internalAutoProxyCreator』
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
  }
  // ... 工具類中的其他代碼暫時省略.
}

所以,重點就轉移到了這個類型為AnnotationAwareAspectJAutoProxyCreator的bean組件,這個組件的功能及運行時機搞清楚,整個AOP的原理就清楚了,後續的其他類似使用註解@Enablexxx實現的功能,例如註解版事務**@EnableTransactionManagement**,套路都是一樣,都是給容器中註冊一些組件,然後在某個時機執行對應的方法。深究其底層原理其實通過查看該註解給容器中注入的組件就可以了。然後繼續查看這個組件的源碼,發現這個類的繼承關係特別特別深,怎麼看呢?首先列出其繼承關係,如下:

從繼承關係圖中可以看到,這個類最終是實現了BeanPostProcessor和Aware接口的,在之前文章中介紹過Spring的擴展原理,Spring的一個最強大的功能就是可以支持靈活擴展,提供了很多擴展點,這兩個接口就是Spring中提供的兩個重要擴展點,而且這兩個擴展點對應的方法會在Bean的創建過程中被調用。

所以,重點再一次轉移到和這些後置處理器相關的方法上,查看方法的實現中都有什麼功能,對於以上的三個擴展接口,對應的擴展方法如下:

BeanFactoryAware接口的擴展方法如下:

void setBeanFactory(BeanFactory beanFactory) throws BeansException;

BeanClassLoaderAware接口的擴展方法如下:

void setBeanClassLoader(ClassLoader classLoader);

SmartInstantiationAwareBeanPostProcessor接口繼承了兩個接口,其包括的擴展方法如下:

// 下面前兩個方法是BeanPostProcessor中的兩個方法
// 該方法是Spring後置處理器中的方法
// 該方法是在Bean實例化(new)之後,初始化(設置各種屬性)之前會被調用
// 在調用afterPropertiesSet方法之前會被調用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   return bean;
}
// 在bean初始化之後會被調用
// 在調用InitializingBean的afterPropertiesSet方法或者init-method指定的方法執行之後調用.
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
   return bean;
}

// -------------------------
// 下面是InstantiationAwareBeanPostProcessor中的方法
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
  return true;
}
// 5.1版本之後新擴展的方法 
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
  throws BeansException {
  return null;
}
// 5.1之後要廢棄的方法
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
  PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
  return pvs;
}

// ------------------
// 下面是SmartInstantiationAwareBeanPostProcessor中的方法
@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
  return null;
}
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
    throws BeansException {
  return null;
}  
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
  return bean;
}

下面就從與後置處理器及Aware接口有關係的方法開始查看其整個執行流程。因為繼承關係太深,而父類是直接實現了Aware及後置處理器相關接口的,所以可以從父類開始向子類中分析,重點關注和後置處理器及Aware接口相關的方法,實際分析過程中可以在相關方法上加上斷點,然後逐步debug。

(1)首先查看最上層的實現類中AbstractAutoProxyCreator中和後置處理器及Aware接口有關而且帶有方法體的方法,有如下幾個:

setBeanFactory:通過Aware方法來注入bean工廠

AbstractAutoProxyCreator.setBeanFactory()

postProcessBeforeInstantiation:bean實例化之前調用

AbstractAutoProxyCreator.postProcessBeforeInstantiation()

postProcessAfterInitialization:在Bean初始化之後調用

AnnotationAwareAspectJAutoProxyCreator本質上是一個BeanPostProcessor,在其父類AbstractAutoProxyCreator中實現了postProcessAfterInitialization()方法

AbstractAutoProxyCreator.postProcessAfterInitialization()

具體相關方法:

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
                implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
        /**
         * **Create a proxy** with the configured interceptors if the bean is
         * identified as one to proxy by the subclass.
         * @see #getAdvicesAndAdvisorsForBean
         */
        @Override
        public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
                if (bean != null) {
                        Object cacheKey = getCacheKey(bean.getClass(), beanName);
                        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                                // 判斷是否需要被代理
                                return wrapIfNecessary(bean, beanName, cacheKey);
                        }
                }
                return bean;
        }

        /**
         * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
         * @param bean the raw bean instance
         * @param beanName the name of the bean
         * @param cacheKey the cache key for metadata access
         * @return a proxy wrapping the bean, or the raw bean instance as-is
         */
        protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
                if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
                        return bean;
                }
                if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
                        return bean;
                }
                if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
                        this.advisedBeans.put(cacheKey, Boolean.FALSE);
                        return bean;
                }

                // Create proxy if we have advice.
                Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
                if (specificInterceptors != DO_NOT_PROXY) {
                        this.advisedBeans.put(cacheKey, Boolean.TRUE);
                        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;
        }

        
}

(2)然後,查看AbstractAutoProxyCreator的子類AbstractAdvisorAutoProxyCreator中和後置處理器及Aware接口有關的方法,如下:

setBeanFactory:注入bean工廠,重寫了父類的方法,所以調用的時候,會調用本類中的setBeanFactory方法,而且裡面調用了initBeanFactory

AbstractAdvisorAutoProxyCreator.setBeanFactory()

(3)然後再查看AbstractAdvisorAutoProxyCreator的子類AspectJAwareAdvisorAutoProxyCreator中和後置處理器及Aware接口有關的方法,如下:

AspectJAwareAdvisorAutoProxyCreator.initBeanFactory()

發現其重寫了父類AbstractAdvisorAutoProxyCreator中的initBeanfactory方法,所以在父類中調用initBeanFactory的時候,調用的其實還是子類的initBeanFactory方法。

1.2、後置處理器初始化的調用過程

整個後置處理器調用的方法分析完成之後,那麼後置處理器在Spring容器創建過程中是如何完成初始化創建動作並加入到容器中的單實例Bean集合中的呢?調用鏈如下:

(1)在TestMain測試類中,通過AnnotationConfigApplicationContext加載AopConfig配置類,創建IOC容器對象;

(2)在AnnotationConfigApplicationContext中將配置類AopConfig解析為一個bean定義,並註冊到容器中,然後調用容器的refresh刷新方法(注意:其他的初始化操作本篇文章中暫時省略,後面會繼續詳解)

(3)在refresh方法中調用registerBeanPostProcessors方法給容器中註冊後置處理器,這個過程中,就會註冊與Aop相關的後置處理器AnnotationAwareAspectJAutoProxyCreator

(4)在註冊AnnotationAwareAspectJAutoProxyCreator的過程中,會通過Spring的Bean創建過程去完成後置處理器Bean組件的實例化,屬性賦值及初始化的操作。實例化即通過反射的方式去創建對象;填充屬性,即通過內省操作對象的setter方法完成;初始化操作中,又分為如下幾個小步驟:

① invokeAwareMethods(beanName, bean),主要是用來調用與Aware接口有關的方法,因為這個AOP對應的後置處理器實現了BeanFactoryAware接口,所以會調用其setBeanFactory方法;

② applyBeanPostProcessorsBeforeInitialization,在bean初始化之前調用,可以再bean創建之前做一些自定義的操作,因為AOP對應的後置處理器實現了BeanPostProcessor接口,所以會調用該方法;

③ invokeInitMethods,執行自定義的初始化方法,例如:init-method指定的方法或者如果bean實現了InitializingBean接口,則在該步驟中會執行afterPropertiesSet方法;

④ applyBeanPostProcessorAfterInitialization,在bean初始化之後調用,可以再bean初始化之後,再做一些自定義操作,由於AOP對應的後置處理器實現了BeanPostProcessor接口,所以該方法在Bean初始化完成之後也會被調用,Spring創建AOP的代理對象就是在該步驟中完成的,通常有兩種方式:cglib和JDK動態代理;

(5)經過上述一系列操作之後,AnnotationAwareAspectJAutoProxyCreator後置處理器就被創建完成,然後將創建完成的後置處理器組件加入到Spring容器中的beanFactory對象中。

上述整個過程調用鏈比較深,代碼也比較多。篇幅有限,就不貼代碼了,在看源碼的過程中,可以根據上述的過程去一步步查看。

1.3、容器中單實例Bean的創建過程

上述過程完成之後,只會給容器中註冊一個AnnotationAwareAspectJAutoProxyCreator類型的後置處理器。但是容器中需要被增強的Bean示例還未創建出來,例如上篇文章中所演示的MathCalculator對應的Bean實例,那麼這個Bean示例是在什麼時候被創建的呢?下面分析一下其創建過程,由於代碼太長,此處就不貼代碼了。

(1)通過斷點可以看到,在容器刷新refresh過程的倒數第二步(容器刷新的核心過程總共分為了12大步,後面會詳細介紹每一步的功能)中會完成容器中剩餘的單實例Bean的創建及初始化。即:finishBeanFactoryInitialization(beanFactory)方法。

(2)而在創建bean的過程中,會受到容器中後置處理器的攔截操作,包括上述第一步給容器中註冊的AnnotationAwareAspectJAutoProxyCreator後置處理器,會在Bean創建前後及初始化前後執行後置處理器方法,對Bean做一些自定義的攔截操作,包括對bean進行包裝,生成對應的代理對象;

(3)在創建MathCalculator和LogAspect對應的bean時,被後置處理器攔截到之後,會執行如下的處理過程,具體創建代理對象的過程在postProcessAfterInitialization方法中:

① 判斷當前bean是否已經被增強過,如果已經被增強,則會直接返回這個bean;判斷依據就是根據bean的名稱判斷已經增強的bean對應的Map集合中是否包括當前需要創建的bean;

② 如果當前bean未被增強過,則去判斷當前bean是否需要被包裝,如果不需要被包裝,則直接返回原來的bean,如果需要被包裝,則會通過AbstractAutoProxyCreator的wrapIfNecessary方法進行包裝;

③ 如果需要被包裝,即執行了wrapIfNecessary方法,則會先去獲取所有的增強,如果能夠獲取到增強器,則會調用AbstractAutoProxyCreator的createProxy方法去創建代理對象。

AbstractAutoProxyCreator.createProxy方法的創建邏輯:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.copyFrom(this);
   // 其他不相關的代碼略...
   
   // 默認不指定生成代理的方式,則proxyTargetClass為false,使用的是jdk動態代理
   // 如果未強制指定使用cglib生成動態代理,則會去校驗當前接口是否能夠正常生成代理對象
   if (!proxyFactory.isProxyTargetClass()) {
      // 當前bean定義中指定了preserveTargetClass屬性為true,則會強制使用cglib動態代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         // 判斷當前bean實現的接口是否為標記類的接口
         //   如果為標記類的接口,例如:Aware接口,則還是會強制使用cglib去生成代理
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   /** 找到所有的advisors增強點,即:環繞,前置,後置,返回,異常等通知方法 */
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   /** 設置增強點 */
   proxyFactory.addAdvisors(advisors);
   /** 設置需要代理的目標類 */
   proxyFactory.setTargetSource(targetSource);
   /** 定製代理工廠,可以使用自定義的代理工廠,此處使用的還是默認的DefaultAopProxyFactory工廠 */
   customizeProxyFactory(proxyFactory);
   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
   /** 創建代理對象,裡面會判斷使用JDK動態代理還是CGLIB動態代理 */
   return proxyFactory.getProxy(getProxyClassLoader());
}

創建代理的時候會根據條件去決定是使用cglib創建代理還是根據jdk去創建代理,如下DefaultAopProxyFactory的createAopProxy方法,該方法源碼如下:

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // isOptimize:表示是否需要對生成代理的策略進行優化,可以在配置中指定
   // isProxyTargetClass:表示是否需要強制使用cglib來生成代理,默認為false,通常都會指定強制使用cglib,即將該值設置為true
   // (當前被代理的類是否未實現接口,實現的接口數為0)或者(是否實現了SpringProxy接口)
   if (config.isOptimize() || config.isProxyTargetClass() 
            || hasNoUserSuppliedProxyInterfaces(config)) {
      // 獲取需要被代理的目標類
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      // 如果要代理的目標類為接口,則直接使用jdk動態代理
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      // 否則使用cglib動態代理創建對象
      return new ObjenesisCglibAopProxy(config);
   }
   // 如果創建代理時不需要優化,未指定使用cglib,而且存在接口,則直接使用jdk創建代理對象
   else {
      return new JdkDynamicAopProxy(config);
   }
}

此處返回的就是一個通過策略類創建的代理對象,使用了【策略設計模式】,根據不同的條件使用不同的實現策略去創建代理對象,包括ObjenesisCglibAopProxy對應的策略實現以及JdkDynamicAopProxy對應的策略實現,他們都是AopProxy策略接口的實現類。

1.4、Spring生成代理的方式

Spring底層同時支持了兩種生成代理的方式,那麼cglib動態代理和jdk的動態代理究竟有什麼區別呢?如果選擇呢?

(1)使用方式不同

JDK動態代理的前提是被代理的類必須實現了某一個接口,而cglib不需要強制要求被代理類實現接口,可以是接口或者實現類。

(2)生成字節碼的方式不同

JDK動態代理和cglib動態代理都是運行期間為被代理對象生成字節碼,JDK是直接操作字節碼,而cglib是使用了asm框架操作字節碼。

(3)生成代理的效率不同

JDK生成字節碼是直接使用接口,邏輯比較簡單,效率稍高。而cglib生成字節碼的邏輯比較複雜,所以生成代理的效率比較低。

(4)執行效率不同

JDK調用代理方法時,是需要通過反射調用,而cglib是通過fastClass機制直接調用方法,執行效率更高。

那麼什麼又是FastClass機制呢?為什麼FastClass就這麼快呢?

cglib執行代理方法的效率比jdk高,是因為cglib採用了fastClass機制,而JDK是通過反射調用的。

FastClass的原理:生成代理類時,為代理類和被代理類各生成一個Class,這個Class會為代理類或者被代理類的方法分配一個int類型的索引index,調用的時候將這個index當做一個入參,FastClass就可以直接通過索引定位到要調用的方法直接進行調用,所以省略了反射調用,執行效率高於JDK。使用類似於資料庫索引的設計思想。

1.5、整體初始化過程小結

Spring的AOP初始化過程比較複雜,為了防止過程中暈車,將整體的大思路可以總結為如下幾個步驟:

(1)註冊後置處理器

給容器中導入一個用於攔截Bean床架的後置處理器,利用了Spring的擴展接口Aware及BeanPostProcessor接口。在註解版中,是通過@EnableAspectJAutoProxy來導入的,而在傳統的xml中類似,只不過是通過配置的方式,但最終還是通過Spring的xml解析器將xml中配置的內容解析為了bean定義,並註冊到Spring容器中了;

(2)創建增強之後的Bean

在容器啟動的時候,會去創建所有使用註解標註的Bean組件,例如:@Component註解,而且會解析出標有@Aspect註解的切面類,並解析出類中定義的切點表達式。然後在初始化其他bean的時候,會根據切點表達式去匹配當前類是否需要增強,如果需要增強,則會對當前的類創建代理對象,創建代理對象是在Bean初始化完成之後做的;

(3)創建增強Bean的方式

在後置處理器中創建增強Bean時,會根據當前類是否實現了接口,代理類是否需要被優化,實現的接口是否為Spring原生的標記類接口,是否強制使用cglib方式等等條件去決定是使用cglib的方式還是jdk的方式,最後通過AopProxy策略接口的具體策略實現類去創建對應的代理類,然後加入到Spring容器中,整個初始化過程就執行完畢。

關鍵字: