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


前情回顧:

  上一篇中,通過 wrapIfNecessary 方法,我們獲取到了合適的增強器(日志方法)與業務

類進行包裝,最終返回了我們業務類的代理對象。

  

 

  本篇我們將從業務方法的執行開始,看看增強器(日志方法)是怎么在方法執行的前后和發

生異常時被調用的。以及在文章的最后總結整個AOP的執行流程。

 

1、調試的起點:

給測試方法打上斷點,然后一直跳到下一個斷點直到執行方法,如下  

 

接着進入方法,會被intercept方法攔截 

進入斷點:

 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:通過目標類和目標方法獲取攔截器鏈

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


 

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

進入 getInterceptorsAndDynamicInterceptionAdvice

 

繼續進入 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 

 

中間的過程也一樣,這里就不演示了

最終我們的索引來到末尾

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

開始調用日志方法打印:

 

 

拋出異常

 

 

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

 

以上可以看到,整個執行流程是一個遞歸調用的過程,對之前排好序的攔截器鏈,通過索引判斷界限,一層一層往里調用,最終遞歸回來一層層執行增強器(日志方法)

 


 

 

3、AOP總結:

通過@EnableAspectJAutoProxy 注解,給容器中注冊 AnnotationAwareAspectJAutoProxyCreator,這個組件實際上是一個后置處理器,

這樣在容器創建過程,也就是refresh過程中,對后置處理器進行注冊調用

掃描獲取到目標代理對象的類和切面類,將目標代理對象和切面方法包裝成增強器

最后得到一系列增強器,創建與目標代理對象同名的代理類並注冊進容器

在以后,使用的就是同名的代理類,並通過攔截器鏈來攔截並執行切面方法,從而達到切面方法的效果

這里對目標代理類的代理利用了代理模式的設計思想,即將目標類和增強器都包裝起來(目標類和增強器包裝在代理類的advised屬性中)

而代理類中的增強器則使用了適配器的設計思想(使得目標類和切面類能夠一起工作)

 

AOP uml圖

以下UML圖是我對AOP的理解,如果有不對之處,歡迎大家指出

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM