死磕Spring之AOP篇 - Spring AOP自動代理(三)建立代理物件

月圓吖發表於2021-04-21

該系列文章是本人在學習 Spring 的過程中總結下來的,裡面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼註釋 Spring 原始碼分析 GitHub 地址 進行閱讀。

Spring 版本:5.1.14.RELEASE

在開始閱讀 Spring AOP 原始碼之前,需要對 Spring IoC 有一定的瞭解,可檢視我的 《死磕Spring之IoC篇 - 文章導讀》 這一系列文章

瞭解 AOP 相關術語,可先檢視 《Spring AOP 常見面試題) 》 這篇文章

該系列其他文章請檢視:《死磕 Spring 之 AOP 篇 - 文章導讀》

在前面的《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的過程主要分為下面兩步:

  1. 篩選出能夠應用於當前 Bean 的 Advisor
  2. 找到了合適 Advisor 則建立一個代理物件, JDK 動態代理或者 CGLIB 動態代理

上一篇《Spring AOP 自動代理(二)篩選合適的通知器》文章分析了上面第 1 步的處理過程,先去解析出當前 IoC 容器所有 Advisor 物件,包括 Advisor 型別的 Bean 和從 @AspectJ 註解的 Bean 解析出來的 Advisor 物件;然後通過通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器篩選出能夠應用於這個 Bean 的 Advisor 們,最後進行排序;不同的 AspectJ 根據 @Order 排序,同一個 AspectJ 中不同 Advisor 的排序,優先順序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

本文將會分析上面的第 2 的建立過程,如果這個 Bean 有合適的 Advisor,那麼為這個 Bean 建立一個代理物件,我們一起來看看是如何建立代理物件的。

回顧

// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    /*
     * <1> 如果當前 Bean 已經建立過自定義 TargetSource 物件
     * 表示在上面的**例項化前置處理**中已經建立代理物件,那麼直接返回這個物件
     */
    if (StringUtils.hasLength(beanName)
            && this.targetSourcedBeans.contains(beanName))
    {
        return bean;
    }
    // <2> `advisedBeans` 儲存了這個 Bean 沒有必要建立代理物件,則直接返回
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    /*
     * <3> 不需要建立代理物件,則直接返回當前 Bean
     */
    if (isInfrastructureClass(bean.getClass()) // 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
            || shouldSkip(bean.getClass(), beanName)) // 應該跳過
    {
        // 將這個 Bean 不需要建立代理物件的結果儲存起來
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // <4> 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // <5> 如果有 Advisor,則進行下面的動態代理建立過程
    if (specificInterceptors != DO_NOT_PROXY) {
        // <5.1> 將這個 Bean 已建立代理物件的結果儲存至 `advisedBeans`
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // <5.2> 建立代理物件,JDK 動態代理或者 CGLIB 動態代理
        // 這裡傳入的是 SingletonTargetSource 物件,可獲取代理物件的目標物件(當前 Bean)
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // <5.3> 將代理物件的 Class 物件(目標類的子類)儲存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // <5.4> 返回代理物件
        return proxy;
    }

    // <6> 否則,將這個 Bean 不需要建立代理物件的結果儲存起來
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    // <7> 返回這個 Bean 物件
    return bean;
}

在建立代理物件的過程中,上面方法的第 4 步嘗試獲取能夠應用於當前 Bean 的 Advisor,該過程在上一篇文章中進行分析過,如果有 Advisor,則呼叫 createProxy(..) 方法建立代理物件

注意,這裡傳入的 TargetSource 是一個 SingletonTargetSource 物件,可獲取目標物件,如下:

public class SingletonTargetSource implements TargetSource, Serializable {
	private static final long serialVersionUID = 9031246629662423738L;

	private final Object target;

	public SingletonTargetSource(Object target) {
		Assert.notNull(target, "Target object must not be null");
		this.target = target;
	}

	@Override
	public Class<?> getTargetClass() {
		return this.target.getClass();
	}

	@Override
	public Object getTarget() {
		return this.target;
	}

	@Override
	public void releaseTarget(Object target) {
		// nothing to do
	}

	@Override
	public boolean isStatic() {
		return true;
	}
}

建立代理物件的流程

  1. 建立一個 ProxyFactory 代理工廠物件,設定需要建立的代理類的配置資訊,例如 Advisor 陣列和 TargetSource 目標類來源

  2. 藉助 DefaultAopProxyFactory 選擇 JdkDynamicAopProxy(JDK 動態代理)還是 ObjenesisCglibAopProxy(CGLIB 動態代理)

    • proxy-target-classfalse 時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理

    • 如果為 true,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理

  3. 通過 JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 建立一個代理物件

    • JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的 Proxy.newProxyInstance(..) 建立代理物件
    • ObjenesisCglibAopProxy 藉助 CGLIB 的 Enhancer 建立代理物件,會設定 Callback 陣列和 CallbackFilter 篩選器(選擇合適 Callback 處理對應的方法),整個過程相比於 JDK 動態代理更復雜點,主要的實現在 DynamicAdvisedInterceptor 方法攔截器中

AbstractAutoProxyCreator

org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator:AOP 自動代理的抽象類,完成主要的邏輯實現,提供一些骨架方法交由子類完成

1. createProxy 方法

createProxy(..) 方法,為目標物件建立一個代理物件,如下:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        // 為目標 Bean 的 BeanDefinition 物件設定一個屬性
        // org.springframework.aop.framework.autoproxy.AutoProxyUtils.originalTargetClass -> 目標 Bean 的 Class 物件
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // <1> 建立一個代理工廠
    ProxyFactory proxyFactory = new ProxyFactory();
    // <2> 複製當前 ProxyConfig 的一些屬性(例如 proxyTargetClass、exposeProxy)
    proxyFactory.copyFrom(this);

    /**
     * <3> 判斷是否類代理,也就是是否開啟 CGLIB 代理
     * 預設配置下為 `false`,參考 {@link org.springframework.context.annotation.EnableAspectJAutoProxy}
     */
    if (!proxyFactory.isProxyTargetClass()) {
        /*
         * <3.1> 如果這個 Bean 配置了進行類代理,則設定為 `proxyTargetClass` 為 `true`
         */
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            /*
             * <3.2> 檢測當前 Bean 實現的介面是否包含可代理的介面
             * 如沒有實現,則將 `proxyTargetClass` 設為 `true`,表示需要進行 CGLIB 提升
             */
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    /*
     * <4> 對入參的 Advisor 進一步處理,因為其中可能還存在 Advice 型別,需要將他們包裝成 DefaultPointcutAdvisor 物件
     * 如果配置了 `interceptorNames` 攔截器,也會新增進來
     */
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // <5> 代理工廠新增 Advisor 陣列
    proxyFactory.addAdvisors(advisors);
    // <6> 代理工廠設定 TargetSource 物件
    proxyFactory.setTargetSource(targetSource);
    // <7> 對 ProxyFactory 進行加工處理,抽象方法,目前沒有子類實現
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    // <8> 是否這個 AdvisedSupport 配置管理器已經過濾過目標類(預設為 false)
    if (advisorsPreFiltered()) {
        // 設定 `preFiltered` 為 `true`
        // 這樣 Advisor 們就不會根據 ClassFilter 進行過濾了,而直接通過 MethodMatcher 判斷是否處理被攔截方法
        proxyFactory.setPreFiltered(true);
    }

    // <9> 通過 ProxyFactory 代理工廠建立代理物件
    return proxyFactory.getProxy(getProxyClassLoader());
}

該方法的處理過程如下:

  1. 建立一個 ProxyFactory 代理工廠 proxyFactory
  2. 複製當前物件的一些屬性給 proxyFactory(例如 proxyTargetClass、exposeProxy),當前 AbstractAutoProxyCreator 物件繼承了 ProxyConfig
  3. 判斷是否類代理,也就是是否開啟 CGLIB 代理,預設配置下為 false,如果沒有的話,進行下面處理
    1. 如果這個 Bean 配置了進行類代理,則設定為 proxyTargetClasstrue
    2. 否則,檢測當前 Bean 實現的介面是否包含可代理的介面,如沒有實現,則將 proxyTargetClass 設為 true,表示需要進行 CGLIB 提升
  4. 呼叫 buildAdvisors(..) 方法,對入參的 Advisor 陣列進一步處理,會將不是 Advisor 型別的物件包裝成 DefaultPointcutAdvisor 物件
  5. proxyFactory 代理工廠新增 Advisor 陣列
  6. proxyFactory 代理工廠設定 TargetSource 物件,使用者獲取目標物件
  7. proxyFactory 進行加工處理,抽象方法,目前沒有子類實現
  8. 是否這個 AdvisedSupport 配置管理器已經過濾過目標類(預設為 false
    1. 是的話設定 preFilteredtrue,這樣 Advisor 們就不會根據 ClassFilter 進行過濾了,而直接通過 MethodMatcher 判斷是否處理被攔截方法
  9. 呼叫 proxyFactory 代理工廠的 getProxy(@Nullable ClassLoader classLoader) 方法建立代理物件

這個過程不復雜,容易理解,其中第 4 步會再次對 Advisor 陣列進一步處理,在第 9 步根據 proxyFactory 建立代理物件

buildAdvisors 方法

buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) 方法,對 Advisor 陣列進一步處理,如下:

protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
    // Handle prototypes correctly...
    // <1> 將配置的 `interceptorNames` 轉換成 Advisor 型別(預設沒有)
    Advisor[] commonInterceptors = resolveInterceptorNames();

    // <2> 將 commonInterceptors 與 specificInterceptors 放入一個集合
    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
        allInterceptors.addAll(Arrays.asList(specificInterceptors));
        if (commonInterceptors.length > 0) {
            // 是否新增至最前面(預設為 true)
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    Advisor[] advisors = new Advisor[allInterceptors.size()];
    /*
     * <3> 遍歷 `specificInterceptors` 陣列
     */
    for (int i = 0; i < allInterceptors.size(); i++) {
        // <3.1> 將不是 Advisor 型別的 Advice 或者 MethodInterceptor 包裝成 DefaultPointcutAdvisor 物件
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    // <4> 返回構建好的 Advisor 陣列
    return advisors;
}

該方法的處理過程如下:

  1. 將配置的 interceptorNames 轉換成 Advisor 型別(預設沒有),得到 commonInterceptors 陣列,如下:

    private Advisor[] resolveInterceptorNames() {
        BeanFactory bf = this.beanFactory;
        ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
        List<Advisor> advisors = new ArrayList<>();
        for (String beanName : this.interceptorNames) {
            if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
                Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
                Object next = bf.getBean(beanName);
                advisors.add(this.advisorAdapterRegistry.wrap(next));
            }
        }
        return advisors.toArray(new Advisor[0]);
    }
    

    解析 interceptorNames 中對應的 Bean,幷包裝成 Advisor 型別(如果需要的話)

  2. 將上一步獲取到的 commonInterceptors 陣列放入入參中的 specificInterceptors 陣列中

  3. 遍歷 specificInterceptors 陣列

    1. 將不是 Advisor 型別的 Advice 或者 MethodInterceptor 包裝成 DefaultPointcutAdvisor 物件

      // DefaultAdvisorAdapterRegistry.java
      @Override
      public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
          if (adviceObject instanceof Advisor) { // Advisor 型別,直接返回
              return (Advisor) adviceObject;
          }
          if (!(adviceObject instanceof Advice)) { // 非 Advice 介面,丟擲異常
              throw new UnknownAdviceTypeException(adviceObject);
          }
          Advice advice = (Advice) adviceObject;
          if (advice instanceof MethodInterceptor) { // MethodInterceptor 型別,包裝成 DefaultPointcutAdvisor 物件
              // So well-known it doesn't even need an adapter.
              return new DefaultPointcutAdvisor(advice);
          }
          for (AdvisorAdapter adapter : this.adapters) {
              // Check that it is supported.
              // 檢查該 Advice 型別是否支援
              if (adapter.supportsAdvice(advice)) {
                  // 包裝成 DefaultPointcutAdvisor 物件 返回
                  return new DefaultPointcutAdvisor(advice);
              }
          }
          throw new UnknownAdviceTypeException(advice);
      }
      
  4. 返回構建好的 Advisor 陣列

能夠應用於這個 Bean 的 Advisor 們已經準備好了,那麼接下來我們來看看 ProxyFactory 是如何建立代理物件的

2. ProxyFactory

org.springframework.aop.framework.ProxyFactory,代理工廠,如下:

public Object getProxy(@Nullable ClassLoader classLoader) {
    // <1> 先建立一個 AOP 代理類(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy)
    // <2> 根據 AOP 代理為目標 Bean 建立一個代理物件,並返回
    return createAopProxy().getProxy(classLoader);
}

過程分為兩步:

  1. 呼叫 createAopProxy() 方法,建立一個 AOP 代理類(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy),如下:

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        // <1> 先獲取 AOP 代理工廠,預設為 DefaultAopProxyFactory,只有這個實現
        // <2> 然後通過它根據建立當前 AdvisedSupport 配置管理器建立一個 AOP 代理(JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy)
        return getAopProxyFactory().createAopProxy(this);
    }
    
  2. 然後呼叫 AOP 代理類的 getProxy(@Nullable ClassLoader classLoader) 方法獲取代理物件

可以看到選擇 JDK 動態代理還是選擇 CGLIB 動態代理在 DefaultAopProxyFactory 中可以找到答案

3. DefaultAopProxyFactory

org.springframework.aop.framework.DefaultAopProxyFactory,預設的 AOP 代理工廠,如下:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		/*
		 * <1> 判斷是否滿足下面三個條件的其中一個
		 */
		if (config.isOptimize() // 需要優化,預設為 `false`
				|| config.isProxyTargetClass() // 使用類代理,也就是使用 CGLIB 動態代理
				|| hasNoUserSuppliedProxyInterfaces(config) // 目標類沒有實現介面
		) {
			// <1.1> 獲取目標類
			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.");
			}
			/*
			 * <1.2> 如果目標類是一個介面或者是 java.lang.reflect.Proxy 的子類
			 * 則還是使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
			 */
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// <1.3> 使用 CGLIB 動態代理,建立一個  ObjenesisCglibAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
			return new ObjenesisCglibAopProxy(config);
		}
		// <2> 否則
		else {
			// 使用 JDK 動態代理,建立一個 JdkDynamicAopProxy 物件,傳入 AdvisedSupport 配置管理器,並返回
			return new JdkDynamicAopProxy(config);
		}
	}

	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}

}

建立 AOP 代理類的過程如下:

  1. 判斷是否滿足下面三個條件的其中一個,則進行下面的處理

    需要優化,預設為 false

    使用類代理,也就是使用 CGLIB 動態代理,在前面的 AbstractAutoProxyCreator#createProxy(..) 方法中有提到過;

    目標類沒有實現介面;

    1. 獲取目標類
    2. 如果目標類是一個介面或者是 java.lang.reflect.Proxy 的子類,則還是使用JDK 動態代理,建立一個 JdkDynamicAopProxy 物件
    3. 否則,使用 CGLIB 動態代理,建立一個 ObjenesisCglibAopProxy 物件
  2. 否則,使用JDK 動態代理,建立一個 JdkDynamicAopProxy 物件

我們可以看到JDK 動態代理對應 JdkDynamicAopProxy 物件,CGLIB 動態代理對應 ObjenesisCglibAopProxy(繼承 CglibAopProxy)物件,在建立這兩個物件的時候都傳入了一個引數,就是 AdvisedSupport 配置管理器物件。

回到前面的 ProxyFactory#createAopProxy() 方法中,這個 AdvisedSupport 物件就是這個 ProxyFactory 物件,它繼承了 AdvisedSupport

再回到 AbstractAutoProxyCreator#createProxy(..) 方法中,這個 ProxyFactory 物件是在這建立的,包含了 TargetSource 目標類來源和能夠應用於當前目標物件的所有 Advisor

所以,我們得到的JdkDynamicAopProxy或者ObjenesisCglibAopProxy都包含了建立 AOP 代理物件的所有配置資訊,可以建立代理物件了。

通過上面的這個方法,我們還可以得出一個結論,關於 proxy-target-class 配置的含義是什麼?

首先這個配置表示是否進行類代理,也就是 CGLIB 動態代理,預設是 false

當為 false 時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理

如果為 true,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理

到這裡,我們知道了使用哪種方式建立代理物件,那麼接下里我們一起來看看兩種方式建立代理物件的過程。

4. JdkDynamicAopProxy

org.springframework.aop.framework.JdkDynamicAopProxy,JDK 動態代理類,實現了 InvocationHandler 介面,可建立代理物件

建構函式

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 5531744639992436476L;

	/** We use a static Log to avoid serialization issues. */
	private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

	/** 代理物件的配置資訊,例如儲存了 TargetSource 目標類來源、能夠應用於目標類的所有 Advisor */
	private final AdvisedSupport advised;

	/** 目標物件是否重寫了 equals 方法 */
	private boolean equalsDefined;

	/** 目標物件是否重寫了 hashCode 方法 */
	private boolean hashCodeDefined;

	public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 // 沒有 Advisor,表示沒有任何動作
				&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) // 沒有來源
		{
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
	}
}

4.1 getProxy 方法

getProxy() 方法,獲取一個 JDK 動態代理物件,如下:

@Override
public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); }

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    // <1> 獲取需要代理的介面(目標類實現的介面,會加上 Spring 內部的幾個介面,例如 SpringProxy)
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    // <2> 判斷目標類是否重寫了 `equals` 或者 `hashCode` 方法
    // 沒有重寫在攔截到這兩個方法的時候,會呼叫當前類的實現
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // <3> 呼叫 JDK 的 Proxy#newProxyInstance(..) 方法建立代理物件
    // 傳入的引數就是當前 ClassLoader 類載入器、需要代理的介面、InvocationHandler 實現類
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

獲取代理物件的過程如下:

  1. 獲取需要代理的介面(目標類實現的介面,會加上 Spring 內部的幾個介面,例如 SpringProxy)

  2. 判斷目標類是否重寫了 equals 或者 hashCode 方法,沒有重寫在攔截到這兩個方法的時候,會呼叫當前類的實現

    private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
        for (Class<?> proxiedInterface : proxiedInterfaces) {
            Method[] methods = proxiedInterface.getDeclaredMethods();
            for (Method method : methods) {
                if (AopUtils.isEqualsMethod(method)) {
                    this.equalsDefined = true;
                }
                if (AopUtils.isHashCodeMethod(method)) {
                    this.hashCodeDefined = true;
                }
                if (this.equalsDefined && this.hashCodeDefined) {
                    return;
                }
            }
        }
    }
    
  3. 呼叫 JDK 的 Proxy#newProxyInstance(..) 方法建立代理物件,入參的 InvocationHandler 實現了就是當前物件

對於上面 JDK 建立代理物件的過程是不是很熟悉,不清楚的小夥伴可以檢視我前面 《初識 JDK、CGLIB 兩種動態代理》 這篇文章

AopProxyUtils

org.springframework.aop.framework.AopProxyUtils 工具類,獲取 JDK 建立代理物件時需要實現哪些介面,如下:

static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
    // <1> 獲取需要代理的介面(目標類實現的介面),放入 `specifiedInterfaces` 中
    Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
    if (specifiedInterfaces.length == 0) {
        // No user-specified interfaces: check whether target class is an interface.
        Class<?> targetClass = advised.getTargetClass();
        if (targetClass != null) {
            if (targetClass.isInterface()) {
                advised.setInterfaces(targetClass);
            }
            else if (Proxy.isProxyClass(targetClass)) {
                advised.setInterfaces(targetClass.getInterfaces());
            }
            specifiedInterfaces = advised.getProxiedInterfaces();
        }
    }
    // <2> 判斷目標類實現的介面是否存在 SpringProxy|Advised|DecoratingProxy 介面,不存在需要新增一個
    boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
    boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
    boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
    int nonUserIfcCount = 0;
    if (addSpringProxy) {
        nonUserIfcCount++;
    }
    if (addAdvised) {
        nonUserIfcCount++;
    }
    if (addDecoratingProxy) {
        nonUserIfcCount++;
    }
    Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
    System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
    int index = specifiedInterfaces.length;
    // <3> 如果目標類實現的介面沒有 SpringProxy,則新增一個
    // 這樣建立的代理物件就會實現這個介面,就能知道這個代理物件是否由 Spring 建立
    if (addSpringProxy) {
        proxiedInterfaces[index] = SpringProxy.class;
        index++;
    }
    // 新增一個 Advised 介面
    if (addAdvised) {
        proxiedInterfaces[index] = Advised.class;
        index++;
    }
    // 新增一個 DecoratingProxy 介面
    if (addDecoratingProxy) {
        proxiedInterfaces[index] = DecoratingProxy.class;
    }
    // <4> 返回需要代理的介面
    return proxiedInterfaces;
}

該方法的處理過程如下:

  1. 獲取需要代理的介面(目標類實現的介面),放入 specifiedInterfaces
  2. 判斷目標類實現的介面是否存在 SpringProxy|Advised|DecoratingProxy 介面,不存在需要新增一個
  3. 通常情況下,上面三個介面都會新增到 specifiedInterfaces 的後面
  4. 返回 specifiedInterfaces 陣列

通過上面這個方法得到的結論:在 Spring 內部通過 JDK 建立的代理物件會實現額外的介面,包括 SpringProxy 標記介面,可區分代理物件是否為 Spring 建立的

4 CglibAopProxy

org.springframework.aop.framework.CglibAopProxy,CGLIB 動態代理類,可建立代理物件

CGLIB 動態代理比 JDK 動態代理可要複雜得多~

建構函式

class CglibAopProxy implements AopProxy, Serializable {

	// Constants for CGLIB callback array indices
	// 因為 CGLIB 設定的 Callback 是一個陣列,下面定義了陣列中固定幾個攔截器的位置
	// 進行 AOP 代理的通用攔截器
	private static final int AOP_PROXY = 0;
	// 執行目標方法的攔截器
	private static final int INVOKE_TARGET = 1;
	// 空的 Callback 物件,對於 finalize() 方法,不需要進行任何處理
	private static final int NO_OVERRIDE = 2;
	// 目標物件排程器,用於獲取目標物件
	private static final int DISPATCH_TARGET = 3;
	// 配置管理器的排程器,會返回一個 AdvisedSupport 物件
	private static final int DISPATCH_ADVISED = 4;
	// 處理 `equals(Object)` 方法的攔截器
	private static final int INVOKE_EQUALS = 5;
	// 處理 `hashCode()` 方法的攔截器
	private static final int INVOKE_HASHCODE = 6;

	/** Logger available to subclasses; static to optimize serialization. */
	protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);

	/** Keeps track of the Classes that we have validated for final methods. */
	private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();

	/** 代理物件的配置資訊,例如儲存了 TargetSource 目標類來源、能夠應用於目標類的所有 Advisor */
	protected final AdvisedSupport advised;

	/** 建立代理物件的構造方法入參 */
	@Nullable
	protected Object[] constructorArgs;

	/** 建立代理物件的構造方法的入參型別 */
	@Nullable
	protected Class<?>[] constructorArgTypes;

	/** Dispatcher used for methods on Advised. 配置管理器的排程器,會返回一個 AdvisedSupport 物件 */
	private final transient AdvisedDispatcher advisedDispatcher;

	/**
	 * 快取方法的呼叫器陣列索引
	 * key:方法名稱
	 * value:方法對應的方法呼叫器在 Callback 陣列中的位置
	 */
	private transient Map<String, Integer> fixedInterceptorMap = Collections.emptyMap();

	// 方法呼叫器在 Callback 陣列中的偏移量
	private transient int fixedInterceptorOffset;

	public CglibAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisors().length == 0 // 沒有 Advisor,表示沒有任何動作
				&& config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) // 沒有來源
		{
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
		this.advisedDispatcher = new AdvisedDispatcher(this.advised);
	}
}

上面的每一個屬性都有註釋,都非常重要,在 Spring 的 CGLIB 動態代理建立代理物件時,會設定一個 Callback 陣列,上面定義了陣列中固定幾個攔截器的位置

陣列中前面的幾個 Callback 如下:

  • 0:進行 AOP 代理的通用攔截器【重點關注】
  • 1:執行目標方法的攔截器
  • 2:空的 Callback 物件,例如 finalize() 方法,不需要進行任何處理
  • 3:目標物件排程器,用於獲取目標物件
  • 4:配置管理器的排程器,會返回一個 AdvisedSupport 物件
  • 5:處理 equals(Object) 方法的攔截器
  • 6:處理 hashCode() 方法的攔截器

4.1 getProxy 方法

getProxy() 方法,獲取一個 CGLIB 動態代理物件,如下:

@Override
public Object getProxy() { return getProxy(null); }

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    try {
        // <1> 獲取目標類
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        // <2> 將目標類作為被代理的類
        Class<?> proxySuperClass = rootClass;
        // <3> 如果目標類已經被 CGLIB 提升(名稱包含 `$$` 符號)
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            // <3.1> 獲取目標類的父類作為被代理的類
            proxySuperClass = rootClass.getSuperclass();
            // <3.2> 獲取目標類實現的介面,並新增至當前 AdvisedSupport 配置管理器中
            // 例如 `@Configuration` 註解的 Bean 會被 CGLIB 提升,實現了 EnhancedConfiguration 介面
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }

        // Validate the class, writing log messages as necessary.
        // <4> 進行校驗,僅列印日誌,例如 final 修飾的方法不能被 CGLIB 提升
        validateClassIfNecessary(proxySuperClass, classLoader);

        // Configure CGLIB Enhancer...
        // <5> 建立 CGLIB 的增強類,並進行接下來的配置
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            // `useCache` 預設為 `true`,可通過 `cglib.useCache` 系統引數指定
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        // <5.1> 設定被代理的類
        enhancer.setSuperclass(proxySuperClass);
        // <5.2> 設定需要代理的介面(可能沒有,不過都會加上 Spring 內部的幾個介面,例如 SpringProxy)
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        // <5.3> 設定命名策略,預設生成的代理物件的名稱中包含 '$$' 和 'BySpringCGLIB'
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

        // <5.4> 獲取回撥介面,也就是 MethodInterceptor 方法攔截器
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        // <5.5> 設定 Callback 過濾器,用於篩選出方法對應的 Callback 回撥介面
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);

        // Generate the proxy class and create a proxy instance.
        // <6> 建立代理物件,CGLIG 位元組碼替身,建立目標類的子類
        return createProxyClassAndInstance(enhancer, callbacks);
    }
    catch (CodeGenerationException | IllegalArgumentException ex) {
        // 丟擲 AopConfigException 異常
    }
    catch (Throwable ex) {
        // 丟擲 AopConfigException 異常
    }
}

該方法的處理過程如下:

  1. 獲取目標類 rootClass
  2. 將目標類作為被代理的類 proxySuperClass
  3. 如果目標類已經被 CGLIB 提升(名稱包含 $$ 符號)
    1. 獲取目標類的父類作為被代理的類
    2. 獲取目標類實現的介面,並新增至當前 AdvisedSupport 配置管理器中,例如 @Configuration 註解的 Bean 會被 CGLIB 提升,實現了 EnhancedConfiguration 介面
  4. 進行校驗,僅列印日誌,例如 final 修飾的方法不能被 CGLIB 提升
  5. 建立 CGLIB 的增強類 Enhancer 物件,並進行接下來的配置
    1. 設定被代理的類為 proxySuperClass
    2. 設定需要代理的介面(例如 SpringProxy),在前面講述 JDK 動態代理建立代理物件時已經講過 AopProxyUtils 這個工具類,不同的是 CGLIB 不會新增 DecoratingProxy 這個介面
    3. 設定命名策略,預設生成的代理物件的名稱中包含 $$BySpringCGLIB
    4. 呼叫 getCallbacks(Class<?>) 方法,獲取回撥陣列 Callback[] callbacks,也就是 MethodInterceptor 方法攔截器
    5. 設定 Callback 過濾器為 ProxyCallbackFilter,用於篩選出方法使用 callbacks 中的哪個 Callback
  6. 呼叫 createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) 方法,建立代理物件,並返回

CGLIB 建立代理物件的過程整體上並不複雜,其中第 5.45.56 步我們接下來逐步分析

4.2 getCallbacks 方法

getCallbacks(Class<?> rootClass) 方法,獲取代理物件的 Callback 回撥陣列,如下:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
    // Parameters used for optimization choices...
    // <1> 獲取代理物件的三個配置資訊
    // `exposeProxy` 是否暴露代理物件,通常為 `false`
    boolean exposeProxy = this.advised.isExposeProxy();
    // `isFrozen` 配置管理器是否被凍結,通常為 `false`
    boolean isFrozen = this.advised.isFrozen();
    // `isStatic` 是否是靜態的目標物件,也就是說目標物件是否每次都需要建立,通常為 `true`
    boolean isStatic = this.advised.getTargetSource().isStatic();

    // Choose an "aop" interceptor (used for AOP calls).
    // <2> 【重點】建立目標物件的攔截器,MethodInterceptor 方法攔截器,會對目標物件的方法進行攔截處理,之前篩選出來的 Advisor 會在這個裡面被呼叫
    Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

    // Choose a "straight to target" interceptor. (used for calls that are
    // unadvised but can return this). May be required to expose the proxy.
    // <3> 建立目標物件的執行器,用於執行目標方法
    Callback targetInterceptor;
    // <3.1> 如果需要暴露當前代理物件,則通過 AopContext 進行暴露,放入 ThreadLocal 中,其他的和下面都相同
    if (exposeProxy) {
        targetInterceptor = (isStatic ?
                new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
    }
    // <3.2> 否則,不暴露
    else {
        targetInterceptor = (isStatic ?
                // <3.2.1> 用於執行方法代理物件,並對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)
                new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                // <3.2.2> 和上者的區別就是,每次攔截都會重新獲取目標物件,結束後釋放該目標物件
                new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
    }

    // Choose a "direct to target" dispatcher (used for
    // unadvised calls to static targets that cannot return this).
    // <4> 目標物件排程器,用於獲取目標物件
    Callback targetDispatcher = (isStatic ?
            // <4.1> 目標物件的排程器,用於獲取當前目標物件
            new StaticDispatcher(this.advised.getTargetSource().getTarget()) :
            // <4.2> 目標物件的排程器,空的 Callback,不做任何處理
            new SerializableNoOp());

    // <5> 生成主要的幾個回撥介面,也就是上面建立的幾個 Callback,放入 `mainCallbacks` 陣列
    Callback[] mainCallbacks = new Callback[] {
            // 0:進行 AOP 代理的通用攔截器
            aopInterceptor,  // for normal advice
            // 1:執行目標方法的攔截器
            targetInterceptor,  // invoke target without considering advice, if optimized
            // 2:空的 Callback 物件,例如 `finalize()` 方法,不需要進行任何處理
            new SerializableNoOp(),  // no override for methods mapped to this
            // 3:目標物件排程器,用於獲取目標物件
            targetDispatcher,
            // 4:配置管理器的排程器,會返回一個 AdvisedSupport 物件
            // 因為代理物件會實現 Advised 介面,攔截到其裡面的方法時,需要呼叫當前 AdvisedSupport 的方法
            this.advisedDispatcher,
            // 5:處理 `equals(Object)` 方法的攔截器
            new EqualsInterceptor(this.advised),
            // 6:處理 `hashCode()` 方法的攔截器
            new HashCodeInterceptor(this.advised)
    };

    Callback[] callbacks;

    // If the target is a static one and the advice chain is frozen,
    // then we can make some optimizations by sending the AOP calls
    // direct to the target using the fixed chain for that method.
    /*
     * <6> 如果目標物件不需要每次都建立,且當前 AdvisedSupport 配置管理器被凍結了,效能優化,可暫時忽略
     * 那麼在建立代理物件的時候,就可以先將目標物件的每個方法對應的方法呼叫器解析出來,該過程有點效能損耗,這樣在代理物件執行方法的時候效能有所提升
     * 不過由於這裡會解析出許多方法呼叫器,會佔有一定的記憶體,以空間換時間
     */
    if (isStatic && isFrozen) {
        Method[] methods = rootClass.getMethods();
        Callback[] fixedCallbacks = new Callback[methods.length];
        this.fixedInterceptorMap = new HashMap<>(methods.length);

        // TODO: small memory optimization here (can skip creation for methods with no advice)
        /*
         * <6.1> 遍歷目標物件所有的方法
         */
        for (int x = 0; x < methods.length; x++) {
            Method method = methods[x];
            // <6.1.1> 獲取能夠應用於該方法的所有攔截器(有序)
            // 不同的 AspectJ 根據 @Order 排序
            // 同一個 AspectJ 中的 Advice 排序:AspectJAfterThrowingAdvice > AfterReturningAdviceInterceptor > AspectJAfterAdvice > AspectJAroundAdvice > MethodBeforeAdviceInterceptor
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
            // <6.1.2> 建立一個方法呼叫器
            fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                    chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
            // <6.1.3> 將每個方法的對應的呼叫器的位置索引快取起來
            this.fixedInterceptorMap.put(methods.toString(), x);
        }

        // Now copy both the callbacks from mainCallbacks
        // and fixedCallbacks into the callbacks array.
        callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
        // <6.2> 將 `mainCallbacks` 複製到 `callbacks` 陣列
        System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
        // <6.3> 將 `fixedCallbacks` 複製到 `callbacks` 陣列(拼接在後面)
        System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
        // <6.4> 記錄一個已生成的方法呼叫器的偏移量
        this.fixedInterceptorOffset = mainCallbacks.length;
    }
    // <7> 否則,不進行解析,取上面的幾個主要的 Callback
    else {
        callbacks = mainCallbacks;
    }
    // <8> 返回這個目標物件對應的 Callback 陣列
    return callbacks;
}

該方法的處理過程如下:

  1. 是獲取代理物件的三個配置資訊

    • exposeProxy 是否暴露代理物件,通常為 false
    • isFrozen 配置管理器是否被凍結,通常為 false
    • isStatic 是否是靜態的目標物件,也就是說目標物件是否每次都需要建立,通常為 true
  2. 【重點】建立目標物件的攔截器,DynamicAdvisedInterceptor 方法攔截器,會對目標物件的方法進行攔截處理,代理物件的處理邏輯都在這裡面完成

  3. 建立目標物件的執行器,用於執行目標方法

    1. 如果需要暴露當前代理物件,則通過 AopContext 進行暴露,放入 ThreadLocal 中,其他的和下面都相同
    2. 否則,不暴露
      1. isStatic 為 true,建立 StaticUnadvisedInterceptor 物件,用於執行方法代理物件,並對最終的返回結果進一步處理(返回結果是否需要為代理物件,返回結果是否不能為空)
      2. 否則,建立 DynamicUnadvisedInterceptor 物件,和上者的區別就是,每次攔截都會重新獲取目標物件,結束後釋放該目標物件
  4. 目標物件排程器,用於獲取目標物件

    1. isStatic 為 true,建立 StaticDispatcher 物件,目標物件的排程器,用於獲取當前目標物件
    2. 否則,建立 SerializableNoOp 物件,空的 Callback,不做任何處理
  5. 生成主要的幾個回撥介面,也就是上面建立的幾個 Callback,放入 mainCallbacks 陣列,可以回到上面的建構函式看看哦?

    0:進行 AOP 代理的通用攔截器

    1:執行目標方法的攔截器

    2:空的 Callback 物件,例如 finalize() 方法,不需要進行任何處理

    3:目標物件排程器,用於獲取目標物件

    4:配置管理器的排程器,會返回一個 AdvisedSupport 物件,因為代理物件會實現 Advised 介面,攔截到其裡面的方法時,需要呼叫當前 AdvisedSupport 的方法

    5:處理 equals(Object) 方法的攔截器

    6:處理 hashCode() 方法的攔截器

  6. 如果目標物件不需要每次都建立,且當前 AdvisedSupport 配置管理器被凍結了(預設不會),這一步做了一個效能優化,具體檢視上面的程式碼

    在建立代理物件的時候,就可以先將目標物件的每個方法對應的方法呼叫器解析出來,該過程有點效能損耗,這樣在代理物件執行方法的時候效能有所提升;不過由於這裡會解析出許多方法呼叫器,會佔有一定的記憶體,以空間換時間

  7. 否則,不進行解析,取上面 mainCallbacks

  8. 返回這個目標物件對應的 Callback 陣列

獲取代理物件的 Callback 過程稍微有點複雜,因為代理物件處理不同的方法使用到的 Callback 也是不同的。不過你要知道的是上面第 2 步,第一個 Callback 就是 DynamicAdvisedInterceptor 物件,該物件用於進行 AOP 代理的通用攔截器,目標類的方法在這個攔截器中進行處理。這個物件和 JDK 動態代理建立的代理物件的實現差不多,所以都放入下一篇文章進行分析?

4.3 ProxyCallbackFilter

CglibAopProxy 的私有內部靜態類,用於篩選方法出對應的 Callback 物件

private static class ProxyCallbackFilter implements CallbackFilter {

    private final AdvisedSupport advised;

    private final Map<String, Integer> fixedInterceptorMap;

    private final int fixedInterceptorOffset;

    public ProxyCallbackFilter(
            AdvisedSupport advised, Map<String, Integer> fixedInterceptorMap, int fixedInterceptorOffset) {
        this.advised = advised;
        this.fixedInterceptorMap = fixedInterceptorMap;
        this.fixedInterceptorOffset = fixedInterceptorOffset;
    }

    /**
     * 根據 Method 返回我們需要的 Callback 在陣列中的位置
     */
    @Override
    public int accept(Method method) {
        // <1> 如果該方法是 `finalize()`
        if (AopUtils.isFinalizeMethod(method)) {
            // 返回一個空 Callback 物件,不對該方法做任何處理
            return NO_OVERRIDE;
        }
        // <2> 如果該方法是 Advised 中的方法,也就是需要 AdvisedSupport 來執行
        if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 返回一個配置管理器的排程器,會返回一個 AdvisedSupport 物件去執行這個方法
            return DISPATCH_ADVISED;
        }
        // We must always proxy equals, to direct calls to this.
        // <3> 如果該方法是 `equals(Object)` 方法
        if (AopUtils.isEqualsMethod(method)) {
            // 返回處理 `equals(Object)` 方法的攔截器去執行該方法
            return INVOKE_EQUALS;
        }
        // We must always calculate hashCode based on the proxy.
        // <4> 如果該方法是 `hashCode()` 方法
        if (AopUtils.isHashCodeMethod(method)) {
            // 返回處理 `hashCode()` 方法的攔截器去執行該方法
            return INVOKE_HASHCODE;
        }
        // <5> 獲取目標類 Class 物件
        Class<?> targetClass = this.advised.getTargetClass();
        // Proxy is not yet available, but that shouldn't matter.
        // <6> 獲取能夠應用於該方法的所有攔截器,僅判斷是否存在 Advice
        List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        boolean haveAdvice = !chain.isEmpty();
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isStatic = this.advised.getTargetSource().isStatic();
        boolean isFrozen = this.advised.isFrozen();
        // <7> 如果有 Advice 或者配置沒有被凍結,通常情況都會進入這裡
        if (haveAdvice || !isFrozen) {
            // If exposing the proxy, then AOP_PROXY must be used.
            // <7.1> 如果需要暴露這個代理物件,預設為 `false`
            if (exposeProxy) {
                // 返回處理 AOP 代理的通用攔截器去執行該方法
                return AOP_PROXY;
            }
            String key = method.toString();
            // Check to see if we have fixed interceptor to serve this method.
            // Else use the AOP_PROXY.
            // <7.2> 如果目標物件是單例的,且配置被凍結,且存在對應的方法呼叫器
            if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
                // We know that we are optimizing so we can use the FixedStaticChainInterceptors.
                // 返回已經存在的方法呼叫器
                int index = this.fixedInterceptorMap.get(key);
                return (index + this.fixedInterceptorOffset);
            }
            // <7.3> 否則(通常會走到這一步)
            else {
                // 返回處理 AOP 代理的通用攔截器去執行該方法
                return AOP_PROXY;
            }
        }
        // <8> 否則
        else {
            // <8.1> 如果需要暴露代理物件,或者不是單例模式,預設這兩種情況都不滿足
            if (exposeProxy || !isStatic) {
                // 則返回執行目標方法的攔截器
                return INVOKE_TARGET;
            }
            Class<?> returnType = method.getReturnType();
            // <8.2> 如果該方法需要返回的就是目標類,暫時不清楚這種情況
            if (targetClass != null && returnType.isAssignableFrom(targetClass)) {
                // 那麼返回執行目標方法的攔截器,返回當前物件
                return INVOKE_TARGET;
            }
            // <8.3> 否則,返回目標物件排程器,用於獲取目標物件,讓目標物件自己去執行這個方法
            else {
                return DISPATCH_TARGET;
            }
        }
    }
}

我們直接看到實現的 accept(Method method) 方法的處理邏輯:

  1. 如果該方法是 finalize(),返回一個空 Callback 物件,不對該方法做任何處理
  2. 如果該方法是 Advised 中的方法,也就是需要 AdvisedSupport 來執行,返回一個配置管理器的排程器,會返回一個 AdvisedSupport 物件去執行這個方法
  3. 如果該方法是 equals(Object) 方法,返回處理 equals(Object) 方法的攔截器去執行該方法
  4. 如果該方法是 hashCode() 方法,返回處理 hashCode() 方法的攔截器去執行該方法
  5. 獲取目標類 Class 物件
  6. 獲取能夠應用於該方法的所有攔截器,僅判斷是否存在 Advice
  7. 如果有 Advice 或者配置沒有被凍結,通常情況都會進入這裡
    1. 如果需要暴露這個代理物件,預設為 false,返回處理 AOP 代理的通用攔截器去執行該方法
    2. 如果目標物件是單例的,且配置被凍結,且存在對應的方法呼叫器,返回已經存在的方法呼叫器
    3. 否則(通常會走到這一步),返回進行 AOP 代理的通用攔截器去執行該方法
  8. 否則
    1. 如果需要暴露代理物件,或者不是單例模式,預設這兩種情況都不滿足,返回執行目標方法的攔截器
    2. 如果該方法需要返回的就是目標類,暫時不清楚這種情況,那麼返回執行目標方法的攔截器,返回當前物件
    3. 否則,返回目標物件排程器,用於獲取目標物件,讓目標物件自己去執行這個方法

上面的處理過程基本上都根據 Method 進行判斷,然後返回一個 int 數值,這個值代表去取 Callback 陣列中的那個 Callback,回看上面講到的幾個方法你就全部理解了。

我們來看到上面的第 7.3 步,返回的是一個 0,表示使用第一個 Callback 進行處理,對應的就是 DynamicAdvisedInterceptor 物件,這個物件和 JDK 動態代理建立的代理物件的實現差不多,所以都放入下一篇文章進行分析?

4.4 createProxyClassAndInstance 方法

createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) 方法,建立代理物件,如下:

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    // 設定使用構造器期間不進行攔截
    enhancer.setInterceptDuringConstruction(false);
    // 設定 Callback 陣列
    enhancer.setCallbacks(callbacks);
    // 建立一個代理物件(目標類的子類)
    return (this.constructorArgs != null && this.constructorArgTypes != null ?
            // 使用指定的構造方法
            enhancer.create(this.constructorArgTypes, this.constructorArgs) :
            // 使用預設的構造方法
            enhancer.create());
}

這個方法在 ObjenesisCglibAopProxy 子類中被重寫了,Spring 內部也是使用 ObjenesisCglibAopProxy 這個類,因為 CGLIB 建立的子類考慮到通過構造器例項化代理物件,可能會存在構造器不合適的情況,所以 Spring 採用 Objenesis 例項化物件,不通過構造器建立例項物件

4.4 ObjenesisCglibAopProxy

org.springframework.aop.framework.ObjenesisCglibAopProxy,繼承 CglibAopProxy 類,重寫 createProxyClassAndInstance(..) 方法

class ObjenesisCglibAopProxy extends CglibAopProxy {

	/**
	 * 建立一個 Objenesis 物件
	 * [**Objenesis**](http://objenesis.org/) 是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件
	 * 應用場景:
	 * 1. 序列化,遠端呼叫和持久化 - 物件需要例項化並儲存為到一個特殊的狀態,而沒有呼叫程式碼
	 * 2. 代理,AOP 庫和 Mock 物件 - 類可以被子類繼承而子類不用擔心父類的構造器
	 * 3. 容器框架 - 物件可以以非標準的方式被動態例項化
	 */
	private static final SpringObjenesis objenesis = new SpringObjenesis();

	public ObjenesisCglibAopProxy(AdvisedSupport config) {
		super(config);
	}

	@Override
	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		// <1> 先建立代理物件的 Class 物件(目標類的子類)
		Class<?> proxyClass = enhancer.createClass();
		Object proxyInstance = null;

		// <2> 是否使用 Objenesis 來例項化代理物件,預設會
		// 可通過 在 `spring.properties` 檔案中新增 `spring.objenesis.ignore=false` 來禁止
		if (objenesis.isWorthTrying()) {
			try {
				// <2.1> 通過 Objenesis 例項化代理物件(非標準方式,不使用構造方法進行例項化)
				proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
			}
			catch (Throwable ex) {
				logger.debug("Unable to instantiate proxy using Objenesis, " +
						"falling back to regular proxy construction", ex);
			}
		}

		// <3> 如果藉助 Objenesis 例項化代理物件失敗
		if (proxyInstance == null) {
			// Regular instantiation via default constructor...
			try {
				// <3.1> 選擇構造器,指定了引數則使用對應的構造器,否則使用預設構造器
				Constructor<?> ctor = (this.constructorArgs != null ?
						proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
						proxyClass.getDeclaredConstructor());
				ReflectionUtils.makeAccessible(ctor);
				// <3.2> 通過構造器例項化代理物件(反射)
				proxyInstance = (this.constructorArgs != null ?
						ctor.newInstance(this.constructorArgs) : ctor.newInstance());
			}
			catch (Throwable ex) {
				throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
						"and regular proxy instantiation via default constructor fails as well", ex);
			}
		}

		// <4> 設定 Callback 陣列
		((Factory) proxyInstance).setCallbacks(callbacks);
		// <5> 返回代理物件
		return proxyInstance;
	}
}

首先我們來看到 SpringObjenesis 這個物件,它實現了 Objenesis 介面,Objenesis 是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件,應用場景:

  • 序列化,遠端呼叫和持久化 - 物件需要例項化並儲存為到一個特殊的狀態,而沒有呼叫程式碼
  • 代理,AOP 庫和 Mock 物件 - 類可以被子類繼承而子類不用擔心父類的構造器
  • 容器框架 - 物件可以以非標準的方式被動態例項化

建立代理物件的過程如下:

  1. 先通過 Enhancer 建立代理物件的 Class 物件(目標類的子類)
  2. 是否使用 Objenesis 來例項化代理物件,預設會
    1. 通過 Objenesis 例項化代理物件(非標準方式,不使用構造方法進行例項化)
  3. 如果藉助 Objenesis 例項化代理物件失敗
    1. 選擇構造器,指定了引數則使用對應的構造器,否則使用預設構造器
    2. 通過構造器例項化代理物件(反射)
  4. 設定 Callback 陣列
  5. 返回代理物件

總結

在前面的《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的過程主要分為下面兩步:

  1. 篩選出能夠應用於當前 Bean 的 Advisor
  2. 找到了合適 Advisor 則建立一個代理物件, JDK 動態代理或者 CGLIB 動態代理

上一篇《Spring AOP 自動代理(二)篩選合適的通知器》文章分析了上面第 1 步的處理過程。本文是接著前面兩篇文章分析上面第 2 個過程,Spring 是如何建立代理物件的,大致流程如下:

  1. 建立一個 ProxyFactory 代理工廠物件,設定需要建立的代理類的配置資訊,例如 Advisor 陣列和 TargetSource 目標類來源

  2. 藉助 DefaultAopProxyFactory 選擇 JdkDynamicAopProxy(JDK 動態代理)還是 ObjenesisCglibAopProxy(CGLIB 動態代理)

    • proxy-target-classfalse 時,優先使用 JDK 動態代理,如果目標類沒有實現可代理的介面,那麼還是使用 CGLIB 動態代理

    • 如果為 true,優先使用 CGLIB 動態代理,如果目標類本身是一個介面,那麼還是使用 JDK 動態代理

  3. 通過 JdkDynamicAopProxy 或者 ObjenesisCglibAopProxy 建立一個代理物件

    • JdkDynamicAopProxy 本身是一個 InvocationHandler 實現類,通過 JDK 的 Proxy.newProxyInstance(..) 建立代理物件
    • ObjenesisCglibAopProxy 藉助 CGLIB 的 Enhancer 建立代理物件,會設定 Callback 陣列和 CallbackFilter 篩選器(選擇合適 Callback 處理對應的方法),整個過程相比於 JDK 動態代理更復雜點,主要的實現在 DynamicAdvisedInterceptor 方法攔截器中

其中 CGLIB 例項化代理物件的過程使用到了 Objenesis,它是一個小型 Java 庫,目的是為一些特殊的 Class 物件例項化一個物件。因為 CGLIB 建立的子類考慮到通過構造器例項化代理物件,可能會存在構造器不合適的情況,所以 Spring 採用 Objenesis 例項化物件,不通過構造器建立例項物件。

好了,本篇文章就到這裡了,Spring AOP 中整個自動代理過程到這裡也就結束了。對於 JDK 動態代理和 CGLIB 動態代理建立的代理物件,它們的具體實現(或者說是方法攔截處理)在這裡並沒有體現出來,前者的實現在 JdkDynamicAopProxy 中,因為它實現了 InvocationHandler 介面,後者的實現則在 DynamicAdvisedInterceptor 方法攔截器中,這些內容將在下篇文章進行分析。

相關文章