1. 程式人生 > >Spring原始碼解析(七):Spring AOP中對攔截器呼叫的實現

Spring原始碼解析(七):Spring AOP中對攔截器呼叫的實現

前面我們分析了Spring AOP實現中得到Proxy物件的過程,下面我們看看在Spring AOP中攔截器鏈是怎樣被呼叫的,也就是Proxy模式是怎樣起作用的,或者說Spring是怎樣為我們提供AOP功能的; 
在JdkDynamicAopProxy中生成Proxy物件的時候: 
Java程式碼  收藏程式碼
  1. return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  

這裡的this引數對應的是InvocationHandler物件,這裡我們的JdkDynamicAopProxy實現了這個介面,也就是說當Proxy物件的函式被呼叫的時候,這個InvocationHandler的invoke方法會被作為回撥函式呼叫,下面我們看看這個方法的實現: 

Java程式碼  收藏程式碼
  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  2.     MethodInvocation invocation = null;  
  3.     Object oldProxy = null;  
  4.     boolean setProxyContext = false;  
  5.     TargetSource targetSource = this.advised.targetSource;  
  6.     Class targetClass = null
    ;  
  7.     Object target = null;  
  8.     try {  
  9.         // Try special rules for equals() method and implementation of the  
  10.         // Advised AOP configuration interface.  
  11.         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {  
  12.             // What if equals throws exception!?  
  13.             // This class implements the equals(Object) method itself.
      
  14.             return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;  
  15.         }  
  16.         if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {  
  17.             // This class implements the hashCode() method itself.  
  18.             return new Integer(hashCode());  
  19.         }  
  20.         if (Advised.class == method.getDeclaringClass()) {  
  21.             // service invocations on ProxyConfig with the proxy config  
  22.             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
  23.         }  
  24.         Object retVal = null;  
  25.         if (this.advised.exposeProxy) {  
  26.             // make invocation available if necessary  
  27.             oldProxy = AopContext.setCurrentProxy(proxy);  
  28.             setProxyContext = true;  
  29.         }  
  30.         // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,  
  31.         // in case it comes from a pool.  
  32.         // 這裡是得到目標物件的地方,當然這個目標物件可能來自於一個例項池或者是一個簡單的JAVA物件  
  33.         target = targetSource.getTarget();  
  34.         if (target != null) {  
  35.             targetClass = target.getClass();  
  36.         }  
  37.         // get the interception chain for this method  
  38.         // 這裡獲得定義好的攔截器鏈  
  39.         List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(  
  40.                 this.advised, proxy, method, targetClass);  
  41.         // Check whether we have any advice. If we don't, we can fallback on direct  
  42.         // reflective invocation of the target, and avoid creating a MethodInvocation.  
  43.         // 如果沒有設定攔截器,那麼我們就直接呼叫目標的對應方法  
  44.         if (chain.isEmpty()) {  
  45.             // We can skip creating a MethodInvocation: just invoke the target directly  
  46.             // Note that the final invoker must be an InvokerInterceptor so we know it does  
  47.             // nothing but a reflective operation on the target, and no hot swapping or fancy proxying  
  48.             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
  49.         }  
  50.         else {  
  51.             // We need to create a method invocation...  
  52.             // invocation = advised.getMethodInvocationFactory().getMethodInvocation(  
  53.             //         proxy, method, targetClass, target, args, chain, advised);  
  54.             // 如果有攔截器的設定,那麼需要呼叫攔截器之後才呼叫目標物件的相應方法  
  55.             // 這裡通過構造一個ReflectiveMethodInvocation來實現,下面我們會看這個ReflectiveMethodInvocation類  
  56.             invocation = new ReflectiveMethodInvocation(  
  57.                     proxy, target, method, args, targetClass, chain);  
  58.             // proceed to the joinpoint through the interceptor chain  
  59.             // 這裡通過ReflectiveMethodInvocation來呼叫攔截器鏈和相應的目標方法  
  60.             retVal = invocation.proceed();  
  61.         }  
  62.         // massage return value if necessary  
  63.         if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {  
  64.             // Special case: it returned "this" and the return type of the method is type-compatible  
  65.             // Note that we can't help if the target sets  
  66.             // a reference to itself in another returned object.  
  67.             retVal = proxy;  
  68.         }  
  69.         return retVal;  
  70.     }  
  71.     finally {  
  72.         if (target != null && !targetSource.isStatic()) {  
  73.             // must have come from TargetSource  
  74.             targetSource.releaseTarget(target);  
  75.         }  
  76.         if (setProxyContext) {  
  77.             // restore old proxy  
  78.             AopContext.setCurrentProxy(oldProxy);  
  79.         }  
  80.     }  
  81. }  

我們先看看目標物件方法的呼叫,這裡是通過AopUtils的方法呼叫 - 使用反射機制來對目標物件的方法進行呼叫: 
Java程式碼  收藏程式碼
  1. public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)  
  2.     throws Throwable {  
  3.     // Use reflection to invoke the method.  
  4.     // 利用放射機制得到相應的方法,並且呼叫invoke  
  5.     try {  
  6.         if (!Modifier.isPublic(method.getModifiers()) ||  
  7.                 !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {  
  8.             method.setAccessible(true);  
  9.         }  
  10.         return method.invoke(target, args);  
  11.     }  
  12.     catch (InvocationTargetException ex) {  
  13.         // Invoked method threw a checked exception.  
  14.         // We must rethrow it. The client won't see the interceptor.  
  15.         throw ex.getTargetException();  
  16.     }  
  17.     catch (IllegalArgumentException ex) {  
  18.         throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +  
  19.                 method + "] on target [" + target + "]", ex);  
  20.     }  
  21.     catch (IllegalAccessException ex) {  
  22.         throw new AopInvocationException("Couldn't access method: " + method, ex);  
  23.     }  
  24. }  

對攔截器鏈的呼叫處理是在ReflectiveMethodInvocation裡實現的: 
Java程式碼  收藏程式碼
  1. public Object proceed() throws Throwable {  
  2.     //    We start with an index of -1 and increment early.  
  3.     // 這裡直接呼叫目標物件的方法,沒有攔截器的呼叫或者攔截器已經呼叫完了,這個currentInterceptorIndex的初始值是0  
  4.     if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {  
  5.         return invokeJoinpoint();  
  6.     }  
  7.     Object interceptorOrInterceptionAdvice =  
  8.         this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);  
  9.     if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {  
  10.         // Evaluate dynamic method matcher here: static part will already have  
  11.         // been evaluated and found to match.  
  12.         // 這裡獲得相應的攔截器,如果攔截器可以匹配的上的話,那就呼叫攔截器的invoke方法  
  13.         InterceptorAndDynamicMethodMatcher dm =  
  14.             (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;  
  15.         if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {  
  16.             return dm.interceptor.invoke(nextInvocation());  
  17.         }  
  18.         else {  
  19.             // Dynamic matching failed.  
  20.             // Skip this interceptor and invoke the next in the chain.  
  21.             // 如果攔截器匹配不上,那就呼叫下一個攔截器,這個時候攔截器鏈的位置指示後移並迭代呼叫當前的proceed方法  
  22.             this.currentInterceptorIndex++;  
  23.             return proceed();  
  24.         }  
  25.     }  
  26.     else {  
  27.         // It's an interceptor, so we just invoke it: The pointcut will have  
  28.         // been evaluated statically before this object was constructed.  
  29.         return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());  
  30.     }  
  31. }  

這裡把當前的攔截器鏈以及在攔截器鏈的位置標誌都clone到一個MethodInvocation物件了,作用是當前的攔截器執行完之後,會繼續沿著得到這個攔截器鏈執行下面的攔截行為,也就是會迭代的呼叫上面這個proceed: 
Java程式碼  收藏程式碼
  1. private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {  
  2.     ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();  
  3.     invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;  
  4.     invocation.parent = this;  
  5.     return invocation;  
  6. }  

這裡的nextInvocation就已經包含了當前的攔截鏈的基本資訊,我們看到在Interceptor中的實現比如TransactionInterceptor的實現中: 
Java程式碼  收藏程式碼
  1. public Object invoke(final MethodInvocation invocation) throws Throwable {  
  2.    ......//這裡是TransactionInterceptor插入的事務處理程式碼,我們會在後面分析事務處理實現的時候進行分析  
  3.         try {  
  4.             //這裡是對配置的攔截器鏈進行迭代處理的呼叫  
  5.             retVal = invocation.proceed();  
  6.         }  
  7.    ......//省略了和事務處理的異常處理程式碼 ,也是TransactionInterceptor插入的處理  
  8.       else {  
  9.         try {  
  10.             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,  
  11.                     new TransactionCallback() {  
  12.                         public Object doInTransaction(TransactionStatus status) {  
  13.                              //這裡是TransactionInterceptor插入對事務處理的程式碼  
  14.                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);  
  15.                             //這裡是對配置的攔截器鏈進行迭代處理的呼叫,接著順著攔截器進行處理  
  16.                             try {                          
  17.                                 return invocation.proceed();  
  18.                             }  
  19.    ......//省略了和事務處理的異常處理程式碼 ,也是TransactionInterceptor插入的處理  
  20.    }  

從上面的分析我們看到了Spring AOP的基本實現,比如Spring怎樣得到Proxy,怎樣利用JAVA Proxy以及反射機制對使用者定義的攔截器鏈進行處理。