spring——AOP原理及源碼五【系列完】

前情回顧:

  在上一篇中,經過 wrapIfNecessary 方法,咱們獲取到了合適的加強器(日誌方法)與業務類進行包裝,最終返回了咱們業務類的代理對象。html

  

 

  本篇咱們將從業務方法的執行開始,看看加強器(日誌方法)是怎麼在方法執行的先後和發生異常時被調用的。以及在文章的最後總結整個AOP的執行流程。緩存

 

調試的起點:

給測試方法打上斷點,而後一直跳到下一個斷點直到執行方法,以下  app

 

進入斷點:ide

 1 @Override
 2         public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
 3             Object oldProxy = null;
 4             boolean setProxyContext = false;
 5             Class<?> targetClass = null;
 6             Object target = null;
 7             try {
 8                 if (this.advised.exposeProxy) {
 9                     // Make invocation available if necessary.
10                     oldProxy = AopContext.setCurrentProxy(proxy);
11                     setProxyContext = true;
12                 }
13                 // May be null. Get as late as possible to minimize the time we
14                 // "own" the target, in case it comes from a pool...
15                 target = getTarget();
16                 if (target != null) {
17                     targetClass = target.getClass();
18                 }
19                 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
20                 Object retVal;
21                 // Check whether we only have one InvokerInterceptor: that is,
22                 // no real advice, but just reflective invocation of the target.
23                 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
24                     // We can skip creating a MethodInvocation: just invoke the target directly.
25                     // Note that the final invoker must be an InvokerInterceptor, so we know
26                     // it does nothing but a reflective operation on the target, and no hot
27                     // swapping or fancy proxying.
28                     Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
29                     retVal = methodProxy.invoke(target, argsToUse);
30                 }
31                 else {
32                     // We need to create a method invocation...
33                     retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
34                 }
35                 retVal = processReturnType(proxy, target, method, retVal);
36                 return retVal;
37             }
38             finally {
39                 if (target != null) {
40                     releaseTarget(target);
41                 }
42                 if (setProxyContext) {
43                     // Restore old proxy.
44                     AopContext.setCurrentProxy(oldProxy);
45                 }
46             }
47         }
intercept


intercept 方法從上往下看:

16~18:獲取目標類(注意不是代理對象)
測試

  

 

19:經過目標類和目標方法獲取攔截器鏈this

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);lua


 

重點探究獲取攔截器鏈的過程

進入 getInterceptorsAndDynamicInterceptionAdvicespa

 

繼續進入 this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass)代理

 1 @Override
 2     public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
 3             Advised config, Method method, Class<?> targetClass) {
 4 
 5         // This is somewhat tricky... We have to process introductions first,
 6         // but we need to preserve order in the ultimate list.
 7         List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
 8         Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
 9         boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
10         AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
11 
12         for (Advisor advisor : config.getAdvisors()) {
13             if (advisor instanceof PointcutAdvisor) {
14                 // Add it conditionally.
15                 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
16                 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
17                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
18                     MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
19                     if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
20                         if (mm.isRuntime()) {
21                             // Creating a new object instance in the getInterceptors() method
22                             // isn't a problem as we normally cache created chains.
23                             for (MethodInterceptor interceptor : interceptors) {
24                                 interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
25                             }
26                         }
27                         else {
28                             interceptorList.addAll(Arrays.asList(interceptors));
29                         }
30                     }
31                 }
32             }
33             else if (advisor instanceof IntroductionAdvisor) {
34                 IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
35                 if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
36                     Interceptor[] interceptors = registry.getInterceptors(advisor);
37                     interceptorList.addAll(Arrays.asList(interceptors));
38                 }
39             }
40             else {
41                 Interceptor[] interceptors = registry.getInterceptors(advisor);
42                 interceptorList.addAll(Arrays.asList(interceptors));
43             }
44         }
45 
46         return interceptorList;
47     }
getInterceptorsAndDynamicInterceptionAdvice

 

以上代碼從上往下看:調試

5:建立攔截器鏈

List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length)

12~32:

  • 遍歷全部加強器
  • 通過一系列判斷,將加強器放入interceptorList中 :
  • interceptorList.addAll(Arrays.asList(interceptors))

46:將攔截器鏈返回

 

接下來將攔截器鏈返回,並存入緩存中

 

最後將攔截器鏈返回

 

 這就是攔截器鏈獲取的過程


 

接下來來到 intercept 方法的真正執行部分:

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

先經過new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy) 獲取到

 

一進來先調用父類方法:

 

 設置好代理對象、目標類、目標方法、攔截器鏈等一系列屬性:

 

接着一路返回後調用 proceed 方法進行執行:

 1 @Override
 2     public Object proceed() throws Throwable {
 3         //    We start with an index of -1 and increment early.
 4         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
 5             return invokeJoinpoint();
 6         }
 7 
 8         Object interceptorOrInterceptionAdvice =
 9                 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
10         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
11             // Evaluate dynamic method matcher here: static part will already have
12             // been evaluated and found to match.
13             InterceptorAndDynamicMethodMatcher dm =
14                     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
15             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
16                 return dm.interceptor.invoke(this);
17             }
18             else {
19                 // Dynamic matching failed.
20                 // Skip this interceptor and invoke the next in the chain.
21                 return proceed();
22             }
23         }
24         else {
25             // It's an interceptor, so we just invoke it: The pointcut will have
26             // been evaluated statically before this object was constructed.
27             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
28         }
29     }
proceed

 

3~6:能夠看到有一個從-1開始的索引,這是用來記錄當前執行次數的(這裏的size爲5對應咱們的五個加強器)

 

 

8~9:每次從攔截器鏈中獲取一個加強器,索引加一

10:判斷這個加強器是否是 InterceptorAndDynamicMethodMatcher 類型,咱們這裏判斷不知足,來到了else,返回調用 invoke 方法的結果

 


 

接下來咱們進入這個執行過程

invoke方法調用proceed方法

 

 

來到proceed方法繼續判斷索引大小

 

往下走又來到 invoke 方法,從下圖能夠看到當前是異常加強器的invoke()

 

進入invoke

先 return 調用 proceed 方法

能夠看到下圖中 catch 部分,說明若是有出現異常,會在catch部分調用加強器方法,並拋出異常

 

接下來又是調用AfterReturning的invoke過程

 

下圖能夠看到又調用了proceed 

 

中間的過程也同樣,這裏就不演示了

最終咱們的索引來到末尾

整個過程開始從內到外執行日誌方法

開始調用日誌方法打印:

 

 

拋出異常

 

 

最終攔截器鏈調用完畢,獲得結果:

 

以上能夠看到,整個執行流程是一個遞歸調用的過程,對以前排好序的攔截器鏈,經過索引判斷界限,一層一層往裏調用,最終遞歸回來一層層執行加強器(日誌方法)

 


 

 

AOP總結:

經過@EnableAspectJAutoProxy 註解,給容器中註冊 AnnotationAwareAspectJAutoProxyCreator,這個組件是一個後置處理器

會在每個bean建立以前執行它的後置處理器方法來獲取對應加強器,並獲取到目標代理對象

在執行切面方法時,經過代理對象和加強器等信息,獲取到攔截器鏈

攔截器鏈在包裝處理後進入執行流程,嵌套調用後執行加強器方法

相關文章
相關標籤/搜索