歡迎光臨
每天分享高質量文章

深入分析 Spring 框架中 AOP 與動態代理的整合原理

(給ImportNew加星標,提高Java技能)

 

轉自:開源中國,

連結:my.oschina.net/guangshan/blog/1797461

 

Spring AOP動態代理支援的核心

 

1.jdk動態代理:java.lang.reflect.InvocationHandler

 

對應的方法攔截器:

 

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

 

呼叫時使用method.invoke(Object, args)

 

該動態代理是基於介面的動態代理,所以並沒有一個原始方法的呼叫過程,整個方法都是被攔截的。

 

2.透過cglib動態建立類進行動態代理。org.springframework.cglib.proxy包下的原生介面,同net.sf.cglib.proxy包下的介面,都是源自cglib庫。Spring內部的cglib動態代理使用了這種方式。

 

對應的方法攔截器:

 

org.springframework.cglib.proxy.Callback、 org.springframework.cglib.proxy.MethodInterceptor

 

public interface MethodInterceptor extends Callback {
    Object intercept(Object obj, Method m, Object[] args, MethodProxy mp) throws Throwable
}

 

呼叫時,使用mp.invoke(Object obj, Object[] args)呼叫其他同類物件的原方法或者mp.invokeSuper(Object obj, Object[] args)呼叫原始(父類)方法。

 

3.org.aopalliance的攔截體系

 

該包是AOP組織下的公用包,用於AOP中方法增強和呼叫。相當於一個jsr標準,只有介面和異常。在AspectJ、Spring等AOP框架中使用。

 

對應的方法攔截器org.aopalliance.intercept.MethodInterceptor:

 

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation inv) throws Throwable;
}

 

呼叫時使用inv.proceed()呼叫原始方法。

 

附aopalliance包介紹:

 

詳細介紹:

 

  1. Advice

    增強器標記介面

  2. Interceptor

    攔截器,Advice的子介面,標記攔截器。攔截器是增強器的一種。

  3. MethodInterceptor

    方法攔截器,Interceptor的子介面,攔截方法並處理。

  4. ConstructorInterceptor

    建構式攔截器,Interceptor的子介面,攔截建構式並處理。

  5. Joinpoint

    連線點。在攔截器中使用,封裝了原方法呼叫的相關資訊,如引數、原物件資訊,以及直接呼叫原方法的proceed方法。

  6. Invocation

    Joinpoint的子類,添加了獲取呼叫引數方法。

  7. MethodInvocation

    Invocation的子類,包含了獲取呼叫方法的方法。

  8. ConstructorInvocation

    Invocation的子類,包含了獲取建構式的方法。

 

Spring AOP框架的整合

 

先看一下透過Spring生成代理物件的程式碼:

 

// 生成基於實體的代理
public Object createProxy(Object bean, Advice advice) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(bean);
    proxyFactory.addAdvice(advice);
    return proxyFactory.getProxy();
}
// 生成基於介面的代理
public T createProxy(Class interface, Interceptor interceptor) {
    T proxy = new ProxyFactory(interface, interceptor).getProxy();
    // 或者ProxyFactory.getProxy(interface, interceptor).getProxy();
    return proxy;
}

 

針對上面的程式碼,結合原始碼進行分析,得到整個代理物件的建立過程。

 

準備:過程中出現的物件

 

  • AopProxyFactory介面

    AopProxy代理工廠類,用於生成代理物件AopProxy。

 

public interface AopProxyFactory {
    AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
}

 

  • AopProxy

    代表一個AopProxy代理物件,可以透過這個物件構造代理物件實體。

 

public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}

 

  • Advised介面

    代表被Advice增強的物件,包括新增advisor的方法、新增advice等的方法。

  • ProxyConfig類

    一個代理物件的配置資訊,包括代理的各種屬性,如基於介面還是基於類構造代理。

  • AdvisedSupport類

    對Advised的構建提供支援,Advised的實現類以及ProxyConfig的子類。

  • ProxyCreatorSupport類

    AdvisedSupport的子類,建立代理物件的支援類,內部包含AopProxyFactory工廠成員,可直接使用工廠成員建立Proxy。

  • ProxyFactory類

    ProxyCreatorSupport的子類,用於生成代理物件實體的工廠類,生成程式碼參考下麵。

  • Advisor介面

    代表一個增強器提供者的物件,內部包含getAdvice方法獲取增強器。

  • AdvisorChainFactory介面

    獲取增強器鏈的工廠介面。提供方法傳回所有增強器,以陣列傳回。

  • Pointcut介面

    切入點,用於匹配類與方法,滿足切入點的條件是才插入advice。相關介面:ClassFilter、MethodMatcher。

 

分析:結合原始碼分析建立過程

 

以上準備做完之後,就可以看原始碼了,從獲取代理物件的實體入口ProxyFactory.getProxy()開始:

 

public Object getProxy() {
    // 建立AopProxy物件再獲取代理物件實體
  return createAopProxy().getProxy();
}
// createAopProxy方法在父類ProxyCreatorSupport中
protected final synchronized AopProxy createAopProxy() {
  if (!this.active) {
    activate();
  }
  // 先獲取AopProxy的工廠物件,再把自己作為createAopProxy的引數AdvisedSupport傳進去,用自己作為代理物件的配置
  return getAopProxyFactory().createAopProxy(this);
}

 

代理物件實體最終是使用AopProxy.getProxy()得到的,他的呼叫是在AopProxyFactory.createAopProxy(AdvisedSupport config,createAopProxy有兩個結果。一個是基於介面的JDK動態代理JdkDynamicAopProxy,一個是基於CGLib的生成類代理ObjenesisCglibAopProxy。原始碼如下:

 

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

  @Override
  public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    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.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
          // 如果標的類是介面,或者已經是Jdk的動態代理類,則建立jdk動態代理
        return new JdkDynamicAopProxy(config);
      }
      // 否則建立Cglib動態代理
      return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 如果宣告建立Jdk動態代理則傳回Jdk動態代理
        return new JdkDynamicAopProxy(config);
    }
  }
}

 

傳入的AdvisedSupport config中包含了需要註冊的Method攔截器,AopProxy會儲存這個config為advised物件。

 

基於JDK的動態代理

 

JdkDynamicAopProxy中getProxy會傳回:

 

public Object getProxy(ClassLoader classLoader) {
  if (logger.isDebugEnabled()) {
    logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
  }
  // 獲取所有需要代理的介面
  Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  // 傳回代理物件的實體
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

 

自己作為InvocationHandler註冊,看他的invoke方法

 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  MethodInvocation invocation;
  Object oldProxy = null;
  boolean setProxyContext = false;

  TargetSource targetSource = this.advised.targetSource;
  Class> targetClass = null;
  Object target = null;

  try {
    if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
      // The target does not implement the equals(Object) method itself.
      // 沒有宣告equals方法,呼叫equals方法時,委託呼叫。
      return equals(args[0]);
    }
    else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
      // The target does not implement the hashCode() method itself.
      // 沒有宣告hashCode方法,呼叫hashCode方法時,委託呼叫。
      return hashCode();
    }
    else if (method.getDeclaringClass() == DecoratingProxy.class) {
      // There is only getDecoratedClass() declared -> dispatch to proxy config.
      // 如果呼叫的方法是DecoratingProxy中的方法,因為其中只有一個getDecoratedClass方法,這裡直接傳回被裝飾的Class即可
      return AopProxyUtils.ultimateTargetClass(this.advised);
    }
    else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
        method.getDeclaringClass().isAssignableFrom(Advised.class)) {
      // 代理不是不透明的,且是介面中宣告的方法,且是Advised或其父介面的方法,則直接呼叫構造時傳入的advised物件的相應方法
      // Service invocations on ProxyConfig with the proxy config...
      return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
    }

    Object retVal;

    if (this.advised.exposeProxy) {
      // Make invocation available if necessary.
        // 如果暴露代理,則用AopContext儲存當前代理物件。用於多級代理時獲取當前的代理物件,一個有效應用是同類中呼叫方法,代理攔截器會無效。可以使用AopContext.currentProxy()獲得代理物件並呼叫。
      oldProxy = AopContext.setCurrentProxy(proxy);
      setProxyContext = true;
    }

    // May be null. Get as late as possible to minimize the time we "own" the target,
    // in case it comes from a pool.
    target = targetSource.getTarget();
    if (target != null) {
      targetClass = target.getClass();
    }

    // Get the interception chain for this
    // 這裡是關鍵,獲得攔截鏈chain,是透過advised物件,即config物件獲得的。
    method.
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    // Check whether we have any advice. If we don't, we can fallback on direct
    // reflective invocation of the target, and avoid creating a MethodInvocation.
    if (chain.isEmpty()) {
        // 如果鏈是空,則直接呼叫被代理物件的方法
      // We can skip creating a MethodInvocation: just invoke the target directly
      // Note that the final invoker must be an InvokerInterceptor so we know it does
      // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
      Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
      retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }
    else {
      // We need to create a method invocation...
      // 否則建立一個MethodInvocation物件,用於鏈式呼叫攔截器鏈chain中的攔截器。
      invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
      // Proceed to the joinpoint through the interceptor chain.
      // 開始執行鏈式呼叫,得到傳回結果
      retVal = invocation.proceed();
    }

    // Massage return value if necessary.
    Class> returnType = method.getReturnType();
    if (retVal != null && retVal == target &&
        returnType != Object.class && returnType.isInstance(proxy) &&
        !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
      // 處理傳回值
      // 如果傳回結果是this,即原始物件,且方法所在類沒有標記為RawTargetAccess(不是RawTargetAccess的實現類或者子介面),則傳回代理物件。
      // Special case: it returned "this" and the return type of the method
      // is type-compatible. Note that we can't help if the target sets
      // a reference to itself in another returned object.
      retVal = proxy;
    }
    else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
      throw new AopInvocationException(
          "Null return value from advice does not match primitive return type for: " + method);
    }
    return retVal;
  }
  finally {
    if (target != null && !targetSource.isStatic()) {
      // Must have come from TargetSource.
      targetSource.releaseTarget(target);
    }
    if (setProxyContext) {
      // Restore old proxy.
      AopContext.setCurrentProxy(oldProxy);
    }
  }
}

 

註冊的Method攔截器都是透過AdvisedSupport這個config物件的addAdvice或者addAdvisor註冊進去的。

 

public void addAdvice(int pos, Advice advice) throws AopConfigException {
  Assert.notNull(advice, "Advice must not be null");
  if (advice instanceof IntroductionInfo) {
    // We don't need an IntroductionAdvisor for this kind of introduction:
    // It's fully self-describing.
    // 如果是引介,則加入引介advisor。(新增功能)
    addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
  }
  else if (advice instanceof DynamicIntroductionAdvice) {
    // We need an IntroductionAdvisor for this kind of introduction.
    // jdk動態代理不支援動態引介
    throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
  }
  else {
      // 把advice轉換為advisor並新增,標的是DefaultPointcutAdvisor。
    addAdvisor(pos, new DefaultPointcutAdvisor(advice));
  }
}

 

其實也是把advice轉成了advisor註冊的。 看下最上面invoke方法中有一個方法呼叫:

 

Listchain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

 

 

public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class> targetClass) {
  MethodCacheKey cacheKey = new MethodCacheKey(method);
  List cached = this.methodCache.get(cacheKey);
  if (cached == null) {
      // 其實是透過advisorChainFactory工廠物件獲得的
    cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
        this, method, targetClass);
    this.methodCache.put(cacheKey, cached);
  }
  return cached;
}

 

是透過AdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法獲取的,也把config物件傳入了,且加的有快取。其實是透過method獲取該method對應的advisor。下麵是他的唯一實現:

 

public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

  @Override
  public List getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, Class> targetClass) {

    // This is somewhat tricky... We have to process introductions first,
    // but we need to preserve order in the ultimate list.
    List interceptorList = new ArrayList(config.getAdvisors().length);
    Class> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

    for (Advisor advisor : config.getAdvisors()) {
      if (advisor instanceof PointcutAdvisor) {
        // Add it conditionally.
        PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
        if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
          MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
          MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
          if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
            if (mm.isRuntime()) {
              // Creating a new object instance in the getInterceptors() method
              // isn't a problem as we normally cache created chains.
              for (MethodInterceptor interceptor : interceptors) {
                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
              }
            }
            else {
              interceptorList.addAll(Arrays.asList(interceptors));
            }
          }
        }
      }
      else if (advisor instanceof IntroductionAdvisor) {
        IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
        if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
          Interceptor[] interceptors = registry.getInterceptors(advisor);
          interceptorList.addAll(Arrays.asList(interceptors));
        }
      }
      else {
        Interceptor[] interceptors = registry.getInterceptors(advisor);
        interceptorList.addAll(Arrays.asList(interceptors));
      }
    }

    return interceptorList;
  }

  /**
   * Determine whether the Advisors contain matching introductions.
   */
  private static boolean hasMatchingIntroductions(Advised config, Class> actualClass) {
    for (int i = 0; i < config.getAdvisors().length; i++) {
      Advisor advisor = config.getAdvisors()[i];
      if (advisor instanceof IntroductionAdvisor) {
        IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
        if (ia.getClassFilter().matches(actualClass)) {
          return true;
        }
      }
    }
    return false;
  }

}

 

上麵包括了各種對Advisor包裝,透過Pointcut等的判斷把Advisor中的Advice包裝成MethodInterceptor、InterceptorAndDynamicMethodMatcher或者Interceptor。

 

之後在呼叫方法前,又把chain轉換為了aopalliance體系的的MethodInvocation。

 

invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

 

最終執行的是retVal = invocation.proceed()。 在ReflectiveMethodInvocation的proceed方法中,有整個攔截器鏈的責任鏈樣式的執行過程,可以仔細看看,透過責任鏈序號方式執行的。

 

public Object proceed() throws Throwable {
  //  We start with an index of -1 and increment early.
  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      // 鏈全部執行完,再次呼叫proceed時,傳回原始物件方法呼叫執行結果。遞迴的終止。
    return invokeJoinpoint();
  }

  Object interceptorOrInterceptionAdvice =
      this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  // 用currentInterceptorIndex記錄當前的interceptor位置,初值-1,先++再獲取。當再攔截器中呼叫invocation.proceed()時,遞迴進入此方法,索引向下移位,獲取下一個攔截器。
  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // 如果是InterceptorAndDynamicMethodMatcher則再執行一次動態匹配
    // Evaluate dynamic method matcher here: static part will already have
    // been evaluated and found to match.
    InterceptorAndDynamicMethodMatcher dm =
        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
        // 匹配成功,執行
      return dm.interceptor.invoke(this);
    }
    else {
      // Dynamic matching failed.
      // Skip this interceptor and invoke the next in the chain.
      // 匹配失敗,跳過該攔截器,遞迴呼叫本方法,執行下一個攔截器。
      return proceed();
    }
  }
  else {
    // It's an interceptor, so we just invoke it: The pointcut will have
    // been evaluated statically before this object was constructed.
    // 如果是interceptor,則直接呼叫invoke。把自己作為invocation,以便在invoke方法中,呼叫invocation.proceed()來執行遞迴。或者invoke中也可以不執行invocation.proceed(),強制結束遞迴,傳回指定物件作為結果。
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
}

 

基於Cglib的動態代理

 

看另外一種情況,Cglib的代理類:

 

ObjenesisCglibAopProxy繼承自CglibAopProxy,整體對外暴露的介面和方法是與上面一致的,只有其真實實現換成了Cglib而已。

 

public Object getProxy(ClassLoader classLoader) {
  if (logger.isDebugEnabled()) {
    logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
  }

  try {
    Class> rootClass = this.advised.getTargetClass();
    Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

    Class> proxySuperClass = rootClass;
    if (ClassUtils.isCglibProxyClass(rootClass)) {
      proxySuperClass = rootClass.getSuperclass();
      Class>[] additionalInterfaces = rootClass.getInterfaces();
      for (Class> additionalInterface : additionalInterfaces) {
        this.advised.addInterface(additionalInterface);
      }
    }

    // Validate the class, writing log messages as necessary.
    validateClassIfNecessary(proxySuperClass, classLoader);

    // Configure CGLIB Enhancer...
    // 使用cglib庫的enhancer,配置之後生成代理物件實體
    Enhancer enhancer = createEnhancer();
    if (classLoader != null) {
      enhancer.setClassLoader(classLoader);
      if (classLoader instanceof SmartClassLoader &&
          ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
        enhancer.setUseCache(false);
      }
    }
    enhancer.setSuperclass(proxySuperClass);
    enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
    // 命名策略是類名中加$$
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    // 設定類生成策略,直接生成類的位元組碼byte[],可以深入研究
    enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
        
        // 獲取所有的callback,此時callback是cglib 的,getCallbacks中會把advisors封裝成callback傳入
    Callback[] callbacks = getCallbacks(rootClass);
    Class>[] types = new Class>[callbacks.length];
    // 生成callback型別陣列
    for (int x = 0; x < types.length; x++) {
      types[x] = callbacks[x].getClass();
    }
    // fixedInterceptorMap only populated at this point, after getCallbacks call above
    // 加入是否需要進行callback的過濾器,根據filter的傳回的int值,cglib會執行不同的callback,索引分別對應上面的callback陣列的索引:
    // 0:AOP_PROXY、1:INVOKE_TARGET、2:NO_OVERRIDE、3:DISPATCH_TARGET、4:DISPATCH_ADVISED、5:INVOKE_EQUALS、6:INVOKE_HASHCODE
    enhancer.setCallbackFilter(new ProxyCallbackFilter(
        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
    enhancer.setCallbackTypes(types);

    // Generate the proxy class and create a proxy instance.
    return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of class [" +
        this.advised.getTargetClass() + "]: " +
        "Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (IllegalArgumentException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of class [" +
        this.advised.getTargetClass() + "]: " +
        "Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (Throwable ex) {
    // TargetSource.getTarget() failed
    throw new AopConfigException("Unexpected AOP exception", ex);
  }
}

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    // 不攔截構造方法
  enhancer.setInterceptDuringConstruction(false);
  // 設定攔截器callback
  enhancer.setCallbacks(callbacks);
  // 建立代理物件實體
  return (this.constructorArgs != null ?
      enhancer.create(this.constructorArgTypes, this.constructorArgs) :
      enhancer.create());
}

 

直接看方法攔截器部分。註冊攔截器是在getProxy方法中,註冊進去的是cglib中的callback:

 

Callback[] callbacks = getCallbacks(rootClass)
enhancer.setCallbacks(callbacks);

private Callback[] getCallbacks(Class> rootClass) throws Exception {
  // Parameters used for optimization choices...
  boolean exposeProxy = this.advised.isExposeProxy();
  boolean isFrozen = this.advised.isFrozen();
  boolean isStatic = this.advised.getTargetSource().isStatic();

  // Choose an "aop" interceptor (used for AOP calls).
  // 生成aopInterceptor,用於AOP呼叫,這是呼叫攔截器鏈的核心,詳看後面。
  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.
  Callback targetInterceptor;
  // 下麵根據不同情況,傳回不同的Callback。
  // targetSource的isStatic為true表示targetSource中的target是靜態的不改變的,故直接快取target即可。
  // 為false則代表是動態的target,每次都需要getTarget來獲取,這兩種傳回不同的callback,以便後續執行時使用不同情況的target。
  // 而exposeProxy代表是否暴露代理物件到AopProxyContext中。
  // 為true代表暴露,false不暴露。都需要傳回不同的callback。
  // 故總共有四種callback,且四種callback都有一個processReturnType的過程,同JdkDynamicAopProxy中的處理傳回值。前面的操作也與JdkDynamicAopProxy中開始的目的相同。
  if (exposeProxy) {
    targetInterceptor = isStatic ?
        new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
        new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
  }
  else {
    targetInterceptor = isStatic ?
        new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
        new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
  }

  // Choose a "direct to target" dispatcher (used for
  // unadvised calls to static targets that cannot return this).
  // 直接呼叫target的callback
  Callback targetDispatcher = isStatic ?
      new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
    // callbackFilter傳回的是callback的索引,用於呼叫這裡的索引值對應的callback。
  Callback[] mainCallbacks = new Callback[] {
      aopInterceptor,  // for normal advice
      // 經過callbackFilter後,不需要被advice的物件,直接呼叫這個interceptor,效能最高。
      targetInterceptor,  // invoke target without considering advice, if optimized
      new SerializableNoOp(),  // no override for methods mapped to this
      targetDispatcher,
      // 呼叫advised中方法時,直接分配到advised中。
      this.advisedDispatcher,
      // equals方法
      new EqualsInterceptor(this.advised),
      // 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.
  // 這是一個最佳化,如果target是個不可變的靜態物件,且advice鏈是固定不變的,則進行最佳化。內容見後面。
  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)
    for (int x = 0; x < methods.length; x++) {
        // 遍歷所有方法,傳回每個方法的攔截器鏈,併為每個方法生成一個包含攔截器鏈的callback。
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
      fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
          chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
      // 註意,這個fixedInterceptorMap還與callbackFilter關聯,以便達到filter的目的。
      // 同時儲存索引到map中,以用於callbackFilter中傳回索引。
      this.fixedInterceptorMap.put(methods[x].toString(), x);
    }

    // Now copy both the callbacks from mainCallbacks
    // and fixedCallbacks into the callbacks array.
    // 聚合所有callback
    callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
    System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
    System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
    // 標記fixedInterceptor的偏移量,也會傳入filter。
    this.fixedInterceptorOffset = mainCallbacks.length;
  }
  else {
    callbacks = mainCallbacks;
  }
  return callbacks;
}

 

上面提到callbackFilter的作用是傳回需要呼叫的callback的序號,與上面的getCallbacks有比較大的關聯,原始碼如下

 

// aopInterceptor: DynamicAdvisedInterceptor
private static final int AOP_PROXY = 0;
// targetInterceptor: 沒有advise的方法
private static final int INVOKE_TARGET = 1;
// noOp: SerializableNoOp
private static final int NO_OVERRIDE = 2;
// targetDispatcher: isStatic ? StaticDispatcher : SerializableNoOp
private static final int DISPATCH_TARGET = 3;
// advisedDispatcher: AdvisedDispatcher
private static final int DISPATCH_ADVISED = 4;
// EqualsInterceptor
private static final int INVOKE_EQUALS = 5;
// HashCodeInterceptor
private static final int INVOKE_HASHCODE = 6;
// 其他索引直接透過fixedInterceptorMap獲得
// 下麵邏輯基本對應JdkDynamicAopProxy中判斷邏輯
public int accept(Method method) {
  if (AopUtils.isFinalizeMethod(method)) {
      // 如果是final的方法,則傳回NO_OVERRIDE
    logger.debug("Found finalize() method - using NO_OVERRIDE");
    return NO_OVERRIDE;
  }
  if (!this.advised.isOpaque() && method.getDeclaringClass().isInterface() &&
      method.getDeclaringClass().isAssignableFrom(Advised.class)) {
    if (logger.isDebugEnabled()) {
      logger.debug("Method is declared on Advised interface: " + method);
    }
    // advised上的方法,直接呼叫advised物件的對應方法
    return DISPATCH_ADVISED;
  }
  // We must always proxy equals, to direct calls to this.
  if (AopUtils.isEqualsMethod(method)) {
      // 傳回呼叫equals
    logger.debug("Found 'equals' method: " + method);
    return INVOKE_EQUALS;
  }
  // We must always calculate hashCode based on the proxy.
  if (AopUtils.isHashCodeMethod(method)) {
      // 傳回呼叫hashCode
    logger.debug("Found 'hashCode' method: " + method);
    return INVOKE_HASHCODE;
  }
  Class> targetClass = this.advised.getTargetClass();
  // Proxy is not yet available, but that shouldn't matter.
  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();
  if (haveAdvice || !isFrozen) {
      // 如果有advice或者不是凍結的(不可改變的)
    // If exposing the proxy, then AOP_PROXY must be used.
    if (exposeProxy) {
      if (logger.isDebugEnabled()) {
        logger.debug("Must expose proxy on advised method: " + method);
      }
      // 如果需要暴露Proxy則傳回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.
    if (isStatic && isFrozen && this.fixedInterceptorMap.containsKey(key)) {
        // 透過fixedInterceptorMap獲得對應索引,傳回callback。
      if (logger.isDebugEnabled()) {
        logger.debug("Method has advice and optimizations are enabled: " + method);
      }
      // We know that we are optimizing so we can use the FixedStaticChainInterceptors.
      int index = this.fixedInterceptorMap.get(key);
      return (index + this.fixedInterceptorOffset);
    }
    else {
      if (logger.isDebugEnabled()) {
        logger.debug("Unable to apply any optimizations to advised method: " + method);
      }
      return AOP_PROXY;
    }
  }
  else {
    // See if the return type of the method is outside the class hierarchy of the target type.
    // If so we know it never needs to have return type massage and can use a dispatcher.
    // If the proxy is being exposed, then must use the interceptor the correct one is already
    // configured. If the target is not static, then we cannot use a dispatcher because the
    // target needs to be explicitly released after the invocation.
    if (exposeProxy || !isStatic) {
        // 如果需要暴露,則要使用targetInterceptor
      return INVOKE_TARGET;
    }
    Class> returnType = method.getReturnType();
    if (returnType.isAssignableFrom(targetClass)) {
        // 如果傳回型別是被代理型別的父類或者介面,有可能是傳回this取用,需要用INVOKE_TARGET對傳回值做處理
      if (logger.isDebugEnabled()) {
        logger.debug("Method return type is assignable from target type and " +
            "may therefore return 'this' - using INVOKE_TARGET: " + method);
      }
      return INVOKE_TARGET;
    }
    else {
      if (logger.isDebugEnabled()) {
        logger.debug("Method return type ensures 'this' cannot be returned - " +
            "using DISPATCH_TARGET: " + method);
      }
      // 不需要攔截,直接傳回標的呼叫
      return DISPATCH_TARGET;
    }
  }
}

 

這裡的核心是把advised物件轉成了Callback,註冊到Enhancer中,那麼攔截器鏈的執行應該是在

 

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

 

// 下麵這段程式碼在我們除錯Spring的時候回經常進來,特別是進入一個Bean的方法後再傳回上一級呼叫時,最常見的就是這裡。
// 這段程式碼基本與JdkDynamicAopProxy的invoke方法一致
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  Object oldProxy = null;
  boolean setProxyContext = false;
  Class> targetClass = null;
  Object target = null;
  try {
    if (this.advised.exposeProxy) {
        // 需要則暴露
      // Make invocation available if necessary.
      oldProxy = AopContext.setCurrentProxy(proxy);
      setProxyContext = true;
    }
    // May be null. Get as late as possible to minimize the time we
    // "own" the target, in case it comes from a pool...
    target = getTarget();
    if (target != null) {
      targetClass = target.getClass();
    }
    // 獲取攔截器鏈
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    Object retVal;
    // Check whether we only have one InvokerInterceptor: that is,
    // no real advice, but just reflective invocation of the target.
    if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
      // We can skip creating a MethodInvocation: just invoke the target directly.
      // Note that the final invoker must be an InvokerInterceptor, so we know
      // it does nothing but a reflective operation on the target, and no hot
      // swapping or fancy proxying.
      // 如果鏈是空且是public方法,則直接呼叫
      Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
      retVal = methodProxy.invoke(target, argsToUse);
    }
    else {
      // We need to create a method invocation...
      // 否則建立一個CglibMethodInvocation以便驅動攔截器鏈
      retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    }
    // 處理傳回值,同JDK動態代理
    retVal = processReturnType(proxy, target, method, retVal);
    return retVal;
  }
  finally {
    if (target != null) {
      releaseTarget(target);
    }
    if (setProxyContext) {
      // Restore old proxy.
      AopContext.setCurrentProxy(oldProxy);
    }
  }
}

 

這個和上面的jdk動態代理的invoke就比較像了,一樣有Listchain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

 

最終的執行則是:

 

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

 

一樣是轉換為了aopalliance的invocation。註意CglibMethodInvocation是ReflectiveMethodInvocation的子類。區別在於CglibMethodInvocation內維護了cglib的MethodProxy,呼叫鏈執行完進行最終真實呼叫時,是呼叫了methodProxy.invoke(this.target, this.arguments)。

 

protected Object invokeJoinpoint() throws Throwable {
  if (this.publicMethod) {
    return this.methodProxy.invoke(this.target, this.arguments);
  }
  else {
    return super.invokeJoinpoint();
  }
}

 

其父類ReflectiveMethodInvocation的該方法是:

 

protected Object invokeJoinpoint() throws Throwable {
  return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}

 

到這裡基本問題都已經解決,還差最後一個最佳化點沒有說,上面的FixedChainStaticTargetInterceptor與DynamicAdvisedInterceptor的區別在哪?程式碼如下,一看便知

 

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    // 直接生成了CglibMethodInvocation,因為adviceChain是固定不變的,故在建立時已經傳入,不需要每次執行時動態再計算了。
    // 省略了暴露代理判斷,無需進行context設定
  MethodInvocation invocation = new CglibMethodInvocation(proxy, this.target, method, args,
      this.targetClass, this.adviceChain, methodProxy);
  // If we get here, we need to create a MethodInvocation.
  Object retVal = invocation.proceed();
  retVal = processReturnType(proxy, this.target, method, retVal);
  return retVal;
}

 

致此結束,後面進行總結。

 

總結

 

再回顧一下上面的config物件,config為AdvisedSupport物件,其實整體是個代理配置物件

 

public class ProxyConfig implements Serializable
- public class AdvisedSupport extends ProxyConfig implements Advised
-- public class ProxyCreatorSupport extends AdvisedSupport
--- public class ProxyFactory extends ProxyCreatorSupport

 

最下層的ProxyFactory包含了特殊的功能,只能建立proxy物件,他是透過AopProxyFactory來做的:

 

public Object getProxy() {
  return createAopProxy().getProxy();
}

 

createAopProxy()方法是ProxyCreatorSupport類支援的:

 

protected final synchronized AopProxy createAopProxy() {
  if (!this.active) {
    activate();
  }
  return getAopProxyFactory().createAopProxy(this);
}
  
public AopProxyFactory getAopProxyFactory() {
  return this.aopProxyFactory;
}  

public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}

 

這樣,ProxyFactory(AdvisedSupport config)就和AopProxyFactory有了聯絡,AopProxyFactory和AopProxy也有了聯絡,AopProxy和Proxy也有了聯絡。

 

結論

 

那麼最終的結論是,Spring使用了Jdk動態代理和Cglib做代理,但是會把兩種代理的攔截器轉換為aopalliance這種標準形式進行處理。但是在公開給外部時,其實使用的是advisor這種形式,都註冊為advisor或者advised即可。這樣就統一了入口。

 

AspectJ框架也使用了aopalliance這種標準形式進行AOP代理,Spring對AspectJ在上層也有包裝,可以研究一下。

 

相關研究

 

  1. Spring與aopalliance標準相關的類圖 SpringAopAlliance

    Advice實現類中有一些adaptor,以及org.springframework.aop.framework.adapter包下一些其他配接器,他們的作用是把advice包裝為advisor,把advice包裝為MethodInterceptor。

  2. Spring其他宣告代理物件Bean的方法

    1.BeanNameAutoProxyCreator

 

// org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
    BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
    beanNameAutoProxyCreator.setBeanNames("test");
    beanNameAutoProxyCreator.setInterceptorNames("testInterceptor");
    return beanNameAutoProxyCreator;
}

 

基本原理是使用BeanNameAutoProxyCreator自動對特定的Bean轉換為特定攔截包裝後的代理Bean,依然是使用了BeanPostProcessor,相關類參考下麵。

 

public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor

 

ii. 使用@Aspect自動織入攔截器

 

@Component
@Aspect
public class LogAspect {
      
    @Pointcut("execution(* com.demo.service..*.*(..))" )
    public void inServiceLayer() { }
      
    @Before(value = "execution(public * com.demo.service..*.*(..))" )
    public void beforeShow() {
            System.out.println("before show." );
      }
      
    @After(value = "execution(public * com.demo.service..*.*(..))" )
    public void afterShow() {
        System.out.println("after show." );
    }
    
    @Around("inServiceLayer()")
    public Object around(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        return thisJoinPoint.proceed();
    }
      
}

 

詳細原理參考3

 

Spring中@Aspect註解宣告的各種接入點是如何代理到Bean裡面的,以及@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing、@Around被包裝為了什麼。

 

4. 其他各種註解如何影響物件,如@Async、@Transactional(參考5)等。

 

基本原理是在@Async等的bpp(BeanPostProcessor,詳細作用參考後續文章)的處理階段中,檢查標的bean是否被標記Advised介面,如果是則直接新增自己宣告的實現特定功能的advisor,否則則使用ProxyFactory生成代理並新增自己的advisor。最終傳回代理物件實體。

 

5.Transaction 在 Controller 層的探索

 

補充案例

 

Spring中一個動態代理實體,一個類中的一個方法,直接呼叫本類的@Async方法不觸發代理,而@Configuration註解配置類卻可以進行代理。為什麼?

 

@Component
public class AsyncService {

    AsyncService asyncService;

    public void setCurrent(AsyncService asyncService) {
        this.asyncService  = asyncService;
    }

    public void async1() {
        System.out.println("1:" + Thread.currentThread().getName());
        // 透過代理物件呼叫
        asyncService.async3();
        // 透過this呼叫,不透過代理。因為this不是代理物件,而是真實物件。
        this.async3();
        // 即使建立了代理,最後一層的呼叫仍然是呼叫原始物件的對應方法,而不是使用代理物件的super.method()來進行呼叫的。
    }


    public void async2() {
        System.out.println("2:" + Thread.currentThread().getName());
        async3();
    }

    @Async
    public void async3() {
        System.out.println("3:" + Thread.currentThread().getName());
    }
}

 

當基於介面進行代理時,又是怎樣的一種情況?

 

欲知詳情,可參考本人下一篇文章

 

小技巧

 

1.如何匯出被cglib代理的類:

 

Spring呼叫Enhance生成類時,最終總會生成類的位元組碼,在生成的地方匯出即可,除錯下發現是在這裡:

 

org.springframework.cglib.core.DefaultGeneratorStrategy.generate(ClassGenerator cg)方法。

 

cglib生成類時,callbackFilter是生成時已經確定的,而不是動態呼叫filter來獲得要呼叫的callback。即類生成時,每個方法要呼叫的callback已經自動織入到被代理方法的呼叫中了。在callbackFilter的accept方法中打斷點,就能看到整個類的生成過程了。找到生成類的位元組碼也是在這裡打斷點找到的。

 

匯出類檔案:

 

File file = new File("E:\\Test.class");
// 建立輸出位元組流
FileOutputStream fos = null;
try {
    fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
// 用FileOutputStream 的write方法寫入位元組陣列
try {
    fos.write(b);
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println("寫入成功");
// 為了節省IO流的開銷,需要關閉
try {
    fos.close();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

 

也可使用javaagent來匯出,見後面。

 

擴充套件知識

 

Advice:增強,在標的方法範圍內織入

 

Introduction: 引介,直接在類級別上新增標的未實現的介面方法

 

上面提到的動態代理有兩種:

 

  1. Jdk動態代理

    Jdk自帶的動態代理,使用Sun內部實現動態生成類,限制是只能對介面進行動態代理。

  2. CGLIB動態位元組碼生成

    動態生成現有類的子類並載入,可代理非介面。缺點是對於final宣告的類以及private的方法無法進行代理。

 

除了上述兩種方法外,還有以下三種代理方式

 

  1. 自定義類載入器

    透過自定義的類載入器,在類載入前對原類位元組碼進行替換,傳回加入動態代理後的類的位元組碼載入到記憶體中。這種方式可代理任意類、介面。包括private方法和final類。但是當專案中使用了一些其他類載入器時,會導致使用其他類載入器的類代理無效化。參考FakeClassloader。

  2. 使用java agent進行位元組碼轉換

    使用java.lang.Instrumentation介面提供的方法,在java agent的方法中進行動態位元組碼轉換。

    對於預先載入的agent,可使用premain在main方法執行前進行類替換。對於動態attach的java agent,可在agentmain方法中動態替換當前記憶體中的類。

    對於預先載入的agent來說,這種方式幾乎是萬能的,除了需要額外加入啟動引數外,幾乎沒有缺點。而對於動態attach的,則略微有些限制,只能替換方法中的實現,而不能修改方法簽名。

  3. 靜態AOP

    例如AspectJ框架,就是個靜態代理的框架,在編譯期直接編譯進入位元組碼。對系統效能幾乎無損耗,但是靈活度略微有些欠缺。

    已同步到看一看
    贊(0)

    分享創造快樂