spring-AOP(一) 手動代理

spring-AOP(一) 手動代理

spring的設(shè)計原理是構(gòu)造一個個原子功能,然后不斷的通過設(shè)計模式在外圍進行包裝躺涝、組合調(diào)用厨钻,最后實現(xiàn)復(fù)雜的邏輯功能。

知識導(dǎo)讀

  • 了解Aop聯(lián)盟定義的幾個概念坚嗜,連接點夯膀、通知、攔截苍蔬、切點诱建、增強
  • ProxyFactory 定義了創(chuàng)建代理對象的工廠方法,不過該類主要用于提供代理配置
  • AopProxy 定義創(chuàng)建代理對象的接口碟绑,實現(xiàn)類有 jdk代理 和 cglib代理
  • 代理的本質(zhì)就是對目標方法執(zhí)行的攔截增強
  • 創(chuàng)建代理最主要是提供 被代理對象 和 增強列表 AdvisorList
  • Advisor封裝了通知和切點俺猿,實質(zhì)就是封裝了一個可以決定在什么類的什么方法上進行增強的通知
  • 代理的最終目的就是在方法的執(zhí)行前后添加邏輯,最終構(gòu)建 ReflectiveMethodInvocation格仲,創(chuàng)建代理類的目的就是為了實現(xiàn)選擇性的對目標對象方法的攔截押袍,然后將方法調(diào)用封裝為ReflectiveMethodInvocation
  • Pointcut提供了怎么篩選攔截哪些對象的哪些方法
  • 切面是通知的載體,通知是個方法抓狭,切面就是定義這些方法的對象伯病。攔截器將這些通知按照順序串成一個鏈

AOP聯(lián)盟和Spring擴展

AOP的實質(zhì)對連接點的增強造烁,一般通俗點講是對對方法執(zhí)行攔截增強
AOP聯(lián)盟定義了AOP的規(guī)范接口否过,聲明了兩個最基礎(chǔ)的接口,連接點 和 增強 惭蟋。

連接點 Joinpoint

public interface Joinpoint {
     //連接點的執(zhí)行
   Object proceed() throws Throwable;
  //一般用于返回被代理對象
   Object getThis();
  //一般用于返回目標方法的Method對象
   AccessibleObject getStaticPart();
}
fZwewP

Joinpoint(連接點) 代表一個對象可以切入的地方苗桂,一般來說就是一個方法的執(zhí)行,MehtodInvocation將方法的調(diào)用過程進行了封裝告组。spring實現(xiàn)擴展了MethodInvocation煤伟,在ReflectiveMethodInvocation中既實現(xiàn)了方法調(diào)用的封裝,又定義了攔截器鏈,用于在方法執(zhí)行過程中進行攔截增強便锨。

通知 Advice

public interface Advice {

}

AOP聯(lián)盟聲明了通知的接口围辙,用于標記通知類型,最主要的接口實現(xiàn)是攔截器Interceptor

public interface Interceptor extends Advice {

}
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
  //定義了對 方法調(diào)用 進行攔截的接口
   Object invoke(MethodInvocation invocation) throws Throwable;
}

在MethodInterceptor接口中聲明了invoke方法放案,需要一個MethodInvocation對象參數(shù)姚建。用于對方法調(diào)用進行攔截,然后執(zhí)行增強邏輯吱殉,在合適的時機回調(diào)MethodInvocation掸冤,實現(xiàn)增強的功能

spring對MethodInterceptor接口進行了具體實現(xiàn),例如提供了前置通知友雳、后置通知稿湿、最終通知、異常通知押赊、環(huán)繞通知的實現(xiàn)類饺藤。

BfRb68
d9juQF

切點 Pointcut

Pointcut是spring定義的一個接口,用于過濾目標類和匹配目標方法(連接點)

public interface Pointcut {
   //用于過濾目標類
   ClassFilter getClassFilter();
   //用于匹配目標方法
   MethodMatcher getMethodMatcher();
  
   Pointcut TRUE = TruePointcut.INSTANCE;
}

Pointcut 的具體實現(xiàn)考杉,spring中Pointcut是很重的一塊邏輯策精,可以單獨出一篇文檔

NscLC2

增強 Advisor

spring中定義的增強類,用于封裝一個Pointcut和一個Advice崇棠,實質(zhì)就是封裝了一個可以決定在什么類的什么方法上應(yīng)用的通知

public interface Advisor {
   Advice EMPTY_ADVICE = new Advice() {};
   //封裝的通知
   Advice getAdvice();
   boolean isPerInstance();
}
public interface PointcutAdvisor extends Advisor {
   //切點咽袜,用于匹配連接點,是否應(yīng)用Advice通知
   Pointcut getPointcut();
}
bgWnaP

工廠方式創(chuàng)建代理對象

ProxyFactory

通過ProxyFactory創(chuàng)建代理對象枕稀,需要配置以下信息

  1. 被代理對象
  2. 增強Advisor列表
  3. 被代理接口(非必須)
  4. 其他一些非必須配置(非必須)

下圖是ProxyFactory的繼承關(guān)系圖询刹,通過看各個層級的字段,可以看出ProxyFactory主要定義的是一些創(chuàng)建代理的配置信息萎坷。

dXcJii

AopProxy

已知的可以創(chuàng)建代理對象的方式

  1. 使用jdk Proxy凹联,需要創(chuàng)建一個InvocationHandler的實現(xiàn)類,調(diào)用代理對象的方法都會去調(diào)用InvocationHandler的實現(xiàn)類的invoke方法哆档,所有的攔截和增強在invoke方法中做
  2. 使用cglib的Enhancer蔽挠,需要創(chuàng)建一批MethodInterceptor的實現(xiàn)類,調(diào)用代理對象所有方法都會去調(diào)用MethodInterceptor實現(xiàn)類的intercept方法瓜浸,所有的攔截和增強都在intercept方法中做

spring中定義了一個AopProxy接口澳淑,用于獲取代理類,實現(xiàn)類是JdkProxy和cglibProxy插佛,ProxyFactory提供了創(chuàng)建AopProxy的配置信息杠巡,創(chuàng)建代理類對象的工作交由AopProxy的實現(xiàn)類實現(xiàn)。

X2TMay

通過ProxyFactory配置創(chuàng)建AopProxy實現(xiàn)

spring中定義了ProxyFactory用于創(chuàng)建代理對象雇寇,下面是創(chuàng)建代理對象的一個demo氢拥。

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(targetSource);//設(shè)置被代理對象
factory.setInterfaces(IUserService.class);//設(shè)置要代理的接口
proxyFactory.setProxyTargetClass(true);//設(shè)置使用cglib創(chuàng)建代理
proxyFactory.setExposeProxy(true);//設(shè)置是否暴露代理類
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);//設(shè)置目標類的增強 Advisor列表
return proxyFactory.getProxy(getProxyClassLoader());//創(chuàng)建代理類對象

調(diào)用ProxyFactory的getProxy創(chuàng)建代理類對象蚌铜,首先會創(chuàng)建一個AopProxy對象,然后調(diào)用AopProxy的getProxy方法獲取代理類對象嫩海,在整個創(chuàng)建代理類過程中冬殃,ProxyFactory用于提供配置

public Object getProxy(@Nullable ClassLoader classLoader) {
   return createAopProxy().getProxy(classLoader);
}

調(diào)用父類ProxyCreatorSupport中的createAopProxy創(chuàng)建AopProxy。

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

在AopProxyFactory的createAopProxy創(chuàng)建了AopProxy的具體實現(xiàn)叁怪,會通過ProxyFactory中提供的配置來選擇使用jdk代理還是cglib代理

注意造壮,cglib也可以創(chuàng)建基于接口的代理

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)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

通過AopProxy創(chuàng)建代理類對象

創(chuàng)建完AopProxy之后,調(diào)用AopProxy實現(xiàn)類的getProxy就可以創(chuàng)建代理類骂束,和代理類對象耳璧。

jdk代理

首先來分析spring是如何實現(xiàn)jdk代理的,在JdkDynamicAopProxy中g(shù)etProxy中通過調(diào)用Proxy的api創(chuàng)建代理類對象

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
  //獲取ProxyFactory提供的配置展箱,接口
  Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  //調(diào)用Proxy的api創(chuàng)建代理類對象旨枯。
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

由于JdkDynamicAopProxy自己本身實現(xiàn)了InvocationHandler接口,所以在創(chuàng)建代理對象的時候傳遞的是this

cglib代理

jdk代理比較簡單混驰,接下來看下CglibAopProxy是如何通過cglib來創(chuàng)建代理類對象的

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   try {
      //獲取ProxyFactory配置的目標類型
      Class<?> rootClass = this.advised.getTargetClass();
      Class<?> proxySuperClass = rootClass;
      //如果當(dāng)前的類型已經(jīng)是cglib創(chuàng)建的代理類攀隔,使用代理類的父類
      if (ClassUtils.isCglibProxyClass(rootClass)) {
         proxySuperClass = rootClass.getSuperclass();
         Class<?>[] additionalInterfaces = rootClass.getInterfaces();
         for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
         }
      }
      //檢查下方法是否是final修飾和 private
      validateClassIfNecessary(proxySuperClass, classLoader);
     //使用cglib的Enhancer創(chuàng)建代理
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);//設(shè)置父類
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
      //最重要的一步,組裝cglib需要的攔截器實現(xiàn)
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      //設(shè)置攔截器過濾器栖榨,用于指定什么方法應(yīng)用什么攔截器
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);
      // enhancer創(chuàng)建代理
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
     //此處省略代碼....
   }
   catch (Throwable ex) {
      //此處省略代碼....
   }
}

通過以上邏輯昆汹,spring就完成了代理類的創(chuàng)建,接下來對代理類的方法調(diào)用都會去調(diào)用InvocationHandler的invoke方法或者Callback的intercept方法婴栽。

代理類的方法調(diào)用

jdk代理的方法調(diào)用

zsq7Kv

JdkDynamicAopProxy實現(xiàn)了InvocationHandler的invoke方法满粗,代理類對象所有方法的執(zhí)行都會通過invoke方法來調(diào)用

  1. 根據(jù)Pointcut篩選應(yīng)用目標方法的Advisor,將Advisor中的Advice封裝為增強目標方法的攔截器鏈

  2. 基于攔截器鏈封裝MethodInvocation實現(xiàn)類對象愚争,方法的調(diào)用和攔截增強全由MethodInvocation實現(xiàn)映皆,

注意每次目標方法執(zhí)行都會新建一個MethodInvocation對象

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   //用于記錄ThreadLocal中之前的代理類對象
   Object oldProxy = null;
   boolean setProxyContext = false;
   //被代理對象
   TargetSource targetSource = this.advised.targetSource;
   Object target = null;
   try {
     //此處省略代碼....
      Object retVal;
          //判斷如果要暴露代理類對象,如果是將代理對象放入ThreadLocal轰枝,通過AopContext.currentProxy獲取
     //同時要記錄下 ThreadLocal中之前存儲的代理類對象捅彻,方法執(zhí)行完畢后要重置回去
      if (this.advised.exposeProxy) {
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
      //最重要的一步 封裝獲取攔截器鏈
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      //如果攔截器鏈為空,直接反射調(diào)用被代理對象的方法
      if (chain.isEmpty()) {
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }else {
         //如果存在攔截器鏈鞍陨,構(gòu)造方法調(diào)用的封裝對象MethodInvocation步淹,被代理對象方法的調(diào)用以及攔截器的增強封裝都通過該類實現(xiàn),每次都新建ReflectiveMethodInvocation對象诚撵,這個對象中有currentInterceptorIndex缭裆,避免污染
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         retVal = invocation.proceed();
      }
      //此處省略代碼....
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // 方法執(zhí)行完畢后重置ThreadLocal中的代理類對象
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

來看下spring中是如何篩選目標方法的攔截器鏈的,在getInterceptorsAndDynamicInterceptionAdvice會首先去獲取緩存中的攔截器鏈砾脑,如果沒有幼驶,再進行篩選解析艾杏,感覺這里還是直接new一個list韧衣,然后將cached放進去,避免后續(xù)流程修改列表

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
  //使用方法名作為緩存key,將攔截器鏈緩存起來畅铭,避免重復(fù)解析
   MethodCacheKey cacheKey = new MethodCacheKey(method);
   List<Object> cached = this.methodCache.get(cacheKey);
   if (cached == null) {
      cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
            this, method, targetClass);
      this.methodCache.put(cacheKey, cached);
   }
   return cached;
}

通過DefaultAdvisorChainFactory構(gòu)建攔截器鏈氏淑,主要就是通過Advisor中的Pointcut的ClassFilter和MethodMatcher來篩選能夠增強目標方法的Advisor,然后封裝為攔截器MethodInterceptor對象硕噩,組裝成攔截器鏈返回

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
      Advised config, Method method, @Nullable Class<?> targetClass) {
   AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
   Advisor[] advisors = config.getAdvisors();//獲取配置中的Advisor列表
   List<Object> interceptorList = new ArrayList<>(advisors.length);
   Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
   Boolean hasIntroductions = null;
    //遍歷Advisor假残,篩選能夠增強目標方法的Advisor,并封裝為攔截器鏈返回
   for (Advisor advisor : advisors) {
     //如果是PointcutAdvisor炉擅,判斷Advisor的Pointcut是否攔截本方法
      if (advisor instanceof PointcutAdvisor) {
         PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
        //先通過ClassFilter判斷是否攔截目標類
         if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
            MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
            boolean match;
           //再通過MethodMatcher判斷是否攔截目標方法
            if (mm instanceof IntroductionAwareMethodMatcher) {
               if (hasIntroductions == null) {
                  hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
               }
               match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
            }
            else {
               match = mm.matches(method, actualClass);
            }
            if (match) {
               MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
              //如果是要根據(jù)運行時參數(shù)進行方法校驗辉懒,需要封裝攔截器,將methodMatcher傳下去谍失,用于執(zhí)行時判斷參數(shù)
               if (mm.isRuntime()) {
                  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 {//其他類型Advisor快鱼,不需要過濾颠印,默認攔截所有方法
         Interceptor[] interceptors = registry.getInterceptors(advisor);
         interceptorList.addAll(Arrays.asList(interceptors));
      }
   }
   return interceptorList;
}

獲取到目標方法的攔截器鏈之后,接下來構(gòu)建MethodInvocation對象抹竹,調(diào)用MethodInvocation的proceed方法线罕。在MethodInvocation中會在目標方法執(zhí)行前后執(zhí)行攔截器鏈中的增強方法,然后在合適的時機再回調(diào)目標方法窃判,實現(xiàn)代理的邏輯钞楼。

通過ReflectiveMethodInvocation的構(gòu)造器可以看出,該對象就是對目標方法調(diào)用和攔截器鏈的封裝

//構(gòu)造器
protected ReflectiveMethodInvocation(
            Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
            @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
        this.proxy = proxy;//代理類對象
        this.target = target;//被代理對象
        this.targetClass = targetClass;//被代理對象類型
        this.method = BridgeMethodResolver.findBridgedMethod(method);//目標方法
        this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);//方法參數(shù)
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;//攔截器鏈
}

再來看如何在proceed方法完成對方法的增強袄琳,在該方法中會遍歷執(zhí)行攔截器鏈中的攔截方法窿凤,所有攔截器都執(zhí)行完畢后再調(diào)用被代理類的目標方法。攔截器鏈是在代理類的方法中傳入的跨蟹,而且是緩存過的共享對象雳殊,這里避免修改該鏈,使用currentInterceptorIndex來記錄鏈的執(zhí)行位置窗轩。其實這塊是可以進行優(yōu)化的

private int currentInterceptorIndex = -1;//記錄攔截器鏈執(zhí)行第幾個攔截器

public Object proceed() throws Throwable {
   // 攔截器鏈遍歷完畢后夯秃,調(diào)用目標方法
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
     //遍歷獲取下一個攔截器
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  //如果是動態(tài)執(zhí)行過程中攔截,需要根據(jù)參數(shù)來判斷是否需要對目標方法進行攔截增強
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
     //通過參數(shù)判斷是否需要增強痢艺,如果需要直接調(diào)用攔截器的invoke方法仓洼,并把當(dāng)前MethodInvocation傳遞過來,以便方法攔截邏輯執(zhí)行后再次調(diào)回來
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      } else {
        //如果不進行攔截堤舒,遞歸色建,重新執(zhí)行下一個攔截器.
         return proceed();
      }
   } else {
     //靜態(tài)攔截器,直接調(diào)用攔截器對方法進行增強
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

ReflectiveMethodInvocation中舌缤,在執(zhí)行完攔截器鏈中所有的攔截器邏輯之后箕戳,會調(diào)用被代理對象的目標方法某残,默認是通過反射調(diào)用

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

CglibMethodInvocation中覆寫了ReflectiveMethodInvocation的invokeJoinpoint方法,會通過cglib的MethodProxy進行調(diào)用,避免反射

@Override
protected Object invokeJoinpoint() throws Throwable {
   if (this.methodProxy != null) {
      return this.methodProxy.invoke(this.target, this.arguments);
   } else {
      return super.invokeJoinpoint();
   }
}

分析到現(xiàn)在陵吸,AOP剩下的功能就是要看Spring中定義的幾種通知類型是如何在攔截器中進行調(diào)用的玻墅。

攔截器中通知方法的執(zhí)行

spring中對通知advice和攔截器MethodInterceptor的繼承實現(xiàn)由點亂,不太明白為什么要這樣做壮虫,不如分開

在攔截器中可以方便的在目標方法執(zhí)行前后添加邏輯澳厢,很簡單。在使用@Aspect的切面的時候囚似,會調(diào)用@Aspect注冊的bean中的方法通知剩拢。這里重點看下@Aspect是如何實現(xiàn)的

前置通知

首先來看下前置通知攔截器中如何調(diào)用前置通知。在invoke方法中先調(diào)用通知饶唤,然后再回調(diào)MethodInvocation目標方法

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
   //包裝了一個前置通知
   private final MethodBeforeAdvice advice;
   public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
      this.advice = advice;
   }
   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      //調(diào)用前置通知方法
      this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());裸扶、
      //通知方法調(diào)用完畢之后,回調(diào) 連接點的方法執(zhí)行
      return mi.proceed();
   }

}

AspectJMethodBeforeAdvice實現(xiàn)了MethodBeforeAdvice

public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {

   public AspectJMethodBeforeAdvice(
         Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
      super(aspectJBeforeAdviceMethod, pointcut, aif);
   }

   @Override
   public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
     //調(diào)用 @aspect 切面中聲明的方法
      invokeAdviceMethod(getJoinPointMatch(), null, null);
   }
}

在AbstractAspectJAdvice定義了切面方法調(diào)用過程

protected Object invokeAdviceMethod(JoinPoint jp, @Nullable JoinPointMatch jpMatch,
      @Nullable Object returnValue, @Nullable Throwable t) throws Throwable {
   //獲取方法參數(shù)搬素,反射調(diào)用通知方法
   return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}

通知方法反射調(diào)用呵晨,完成前置通知的調(diào)用

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
   Object[] actualArgs = args;
   if (this.aspectJAdviceMethod.getParameterCount() == 0) {
      actualArgs = null;
   }
   try {
      ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
      //通知方法反射調(diào)用,對象是@aspect注冊的bean
      return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
   }
   catch (IllegalArgumentException ex) {
      //...
   }
}

環(huán)繞通知

看完了前置通知熬尺,后置通知摸屠、異常通知基本都一樣,這里看下環(huán)繞通知粱哼,環(huán)繞通知實現(xiàn)比較復(fù)雜

環(huán)繞通知直接調(diào)用了通知方法季二,在這里沒有再回調(diào)目標方法的執(zhí)行,因為環(huán)繞通知對目標方法的執(zhí)行寫在通知方法內(nèi)部揭措,接下來看環(huán)繞通知是如何完成既完成對目標方法的調(diào)用又完成剩余通知的執(zhí)行

public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
       if (!(mi instanceof ProxyMethodInvocation)) {
        //...
       }
       ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
       ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
       JoinPointMatch jpm = getJoinPointMatch(pmi);
       //調(diào)用通知方法
       return invokeAdviceMethod(pjp, jpm, null, null);
    }
}

lazyGetProceedingJoinPoint返回了一個ProceedingJoinPoint胯舷,封裝了原始的MethodInvocation對象

protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) {
   return new MethodInvocationProceedingJoinPoint(rmi);
}

接下來通知方法的調(diào)用跟前置通知一樣,只不過是將ProceedingJoinPoint作為參數(shù)傳遞給了通知方法

@Around("test()")
public Object aroundTest(ProceedingJoinPoint p) {
    System.out.println("around before");
    Object result = null;
    try {
        //調(diào)用原始方法
        result = p.proceed();
    } catch (Throwable e) {
    }
    System.out.println("around after");
    return result;
}

調(diào)用p.proceed() 绊含,在 MethodInvocationProceedingJoinPoint 會將原始的 MethodInvocation克隆一份桑嘶,會將剩余攔截器鏈和下標復(fù)制下來,然后重新調(diào)用 proceed方法

@Override
public Object proceed() throws Throwable {
   return this.methodInvocation.invocableClone().proceed();
}

ReflectiveMethodInvocation進行深克隆

@Override
public MethodInvocation invocableClone() {
   Object[] cloneArguments = this.arguments;
   if (this.arguments.length > 0) {
      // Build an independent copy of the arguments array.
      cloneArguments = new Object[this.arguments.length];
      System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
   }
   return invocableClone(cloneArguments);
}
@Override
public MethodInvocation invocableClone(Object... arguments) {
   ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
   clone.arguments = arguments;
   return clone;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末躬充,一起剝皮案震驚了整個濱河市逃顶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌充甚,老刑警劉巖以政,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伴找,居然都是意外死亡盈蛮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門技矮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抖誉,“玉大人殊轴,你說我怎么就攤上這事〈缥澹” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵耿币,是天一觀的道長梳杏。 經(jīng)常有香客問我,道長淹接,這世上最難降的妖魔是什么十性? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮塑悼,結(jié)果婚禮上劲适,老公的妹妹穿的比我還像新娘。我一直安慰自己厢蒜,他們只是感情好霞势,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斑鸦,像睡著了一般愕贡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巷屿,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天固以,我揣著相機與錄音,去河邊找鬼嘱巾。 笑死憨琳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旬昭。 我是一名探鬼主播巢掺,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脓匿!你這毒婦竟也來了焕济?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤场梆,失蹤者是張志新(化名)和其女友劉穎墅冷,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體或油,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡寞忿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了顶岸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腔彰。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡叫编,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霹抛,到底是詐尸還是另有隱情搓逾,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布杯拐,位于F島的核電站霞篡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏端逼。R本人自食惡果不足惜朗兵,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顶滩。 院中可真熱鬧余掖,春花似錦、人聲如沸礁鲁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仅醇。三九已至找田,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間着憨,已是汗流浹背墩衙。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甲抖,地道東北人漆改。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像准谚,于是被迫代替她去往敵國和親挫剑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348