方法攔截器的繼承層次圖:
這些攔截器具體長什么樣??
一、MethodBeforeAdviceInterceptor
這個攔截器只有一個屬性就是前置通知。需要注意的是前置通知和返回通知的攔截器才會持有的通知的引用,也就是攔截器會有一個屬性是前置通知或返回通知。其他三個既是通知又是攔截器。如:AspectJAfterAdvice 既是通知又是攔截器,AspectJAfterThrowingAdvice、AspectJAroundAdvice也同樣既是通知又是攔截器。
/** * Interceptor to wrap am {@link org.springframework.aop.MethodBeforeAdvice}. * Used internally by the AOP framework; application developers should not need * to use this class directly. * * @author Rod Johnson */ @SuppressWarnings("serial") public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { private MethodBeforeAdvice advice;//前置通知 /** * Create a new MethodBeforeAdviceInterceptor for the given advice. * @param advice the MethodBeforeAdvice to wrap *///構造器 public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } //注意:前置通知攔截器是在調用proceed()方法之前調用前置通知方法的。而返回通知攔截器是在調用proceed()方法之后才調用返回通知方法的。后置通知也是在proceed()方法之后,但還是它在finally塊中,因為后置通知不管是否拋異常都要執行。 @Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );//首先調用前置通知方法。 return mi.proceed();//然后調用proceed()方法。 } }
二、AspectJAfterAdvice
既是通知又是攔截器,所以它不需要持有通知的引用。
/** * Spring AOP advice wrapping an AspectJ after advice method. * * @author Rod Johnson * @since 2.0 */ @SuppressWarnings("serial") public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterAdvice( Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif); } @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed();//先調用的proceed()方法。 } finally { invokeAdviceMethod(getJoinPointMatch(), null, null);//因為是后置通知,所以不管程序有沒有拋異常都會執行后置通知方法。所以后置通知方法方法finally中調用。 } } @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return true; } }
三、AfterReturningAdviceInterceptor
返回通知攔截器。持有一個返回通知的引用。
/** * Interceptor to wrap am {@link org.springframework.aop.AfterReturningAdvice}. * Used internally by the AOP framework; application developers should not need * to use this class directly. * * @author Rod Johnson */ @SuppressWarnings("serial") public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { private final AfterReturningAdvice advice;//持有一個返回通知的引用 /** * Create a new AfterReturningAdviceInterceptor for the given advice. * @param advice the AfterReturningAdvice to wrap */ public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed();//先調用proceed()方法,如果proceed()方法拋出了異常那么程序就中斷了,無法執行返回通知方法了。 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());//返回通知方法,上面的proceed()方法拋異常則無法執行。 return retVal; } }
四、AspectJAfterThrowingAdvice
既是通知又是攔截器。沒有持有異常通知的引用。異常通知方法在catch塊中執行。
/** * Spring AOP advice wrapping an AspectJ after-throwing advice method. * * @author Rod Johnson * @since 2.0 */ @SuppressWarnings("serial") public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable { public AspectJAfterThrowingAdvice( Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJBeforeAdviceMethod, pointcut, aif); } //省略掉一些代碼 @Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { invokeAdviceMethod(getJoinPointMatch(), null, ex);//如果拋出異常,在catch塊中調用異常通知方法。 } throw ex; } } //省略掉一些代碼。 }
五、AspectJAroundAdvice
既是通知又是攔截器。稍后。。。。。。。。。
/** * Spring AOP around advice (MethodInterceptor) that wraps * an AspectJ advice method. Exposes ProceedingJoinPoint. * * @author Rod Johnson * @author Juergen Hoeller * @since 2.0 */ @SuppressWarnings("serial") public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable { public AspectJAroundAdvice( Method aspectJAroundAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) { super(aspectJAroundAdviceMethod, pointcut, aif); } @Override public boolean isBeforeAdvice() { return false; } @Override public boolean isAfterAdvice() { return false; } @Override protected boolean supportsProceedingJoinPoint() { return true; } @Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); } ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); return invokeAdviceMethod(pjp, jpm, null, null); } /** * Return the ProceedingJoinPoint for the current invocation, * instantiating it lazily if it hasn't been bound to the thread already. * @param rmi the current Spring AOP ReflectiveMethodInvocation, * which we'll use for attribute binding * @return the ProceedingJoinPoint to make available to advice methods */ protected ProceedingJoinPoint lazyGetProceedingJoinPoint(ProxyMethodInvocation rmi) { return new MethodInvocationProceedingJoinPoint(rmi); } }
六、目標方法對應的通知方法攔截器鏈是如何創建的
當我們調用目標方法時,程序會將調用目標方法的任務轉交給JdkDynamicAopProxy的invoke方法來執行。在invoke方法中根據目標方法為其創建攔截器鏈。也就是這個目標方法一共對應多少個通知方法,為每個通知方法建一個通知,然后將這些通知再包裝成攔截器(其實有寫通知本身就是攔截器),最后將這些攔截器組成一條攔截器鏈。攔截器鏈構建好以后就是執行通知方法和目標方法了。
下面是JdkDynamicAopProxy的invoke方法的代碼(這個方法的代碼都很重,只是這里討論攔截器是如何創建的,所以刪掉了部分代碼):
/** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override 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 { //省略部分代碼// 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 method.這行代碼執行創建攔截器鏈的操作,將目標方法和所屬對象的類對象作為參數 List<Object> 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()) { //省略 } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } //省略掉部分代碼return retVal; } finally { //省略 } }
上面的代碼中調用了AdvisedSupport的方法,如下:
/** * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects * for the given method, based on this configuration. * @param method the proxied method * @param targetClass the target class * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers) */ public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) { MethodCacheKey cacheKey = new MethodCacheKey(method); List<Object> cached = this.methodCache.get(cacheKey);//先從緩存里查看目標方法對應的攔截器鏈是否已經存在,如果不存在則創建攔截器鏈。 if (cached == null) { cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(//緩存中沒有,所以要創建攔截器鏈,這個任務交給了AdvisorChainFactory來完成。 this, method, targetClass); this.methodCache.put(cacheKey, cached);//攔截器鏈創建好以后放到緩存中以便下次使用 } return cached;//返回攔截器鏈。 }
下面看看AdvisorChainFactory是如何創建的攔截器鏈的。(AdvisorChainFactory是接口,它有一個實現類DefaultAdvisorChainFactory)
/** * A simple but definitive way of working out an advice chain for a Method, * given an {@link Advised} object. Always rebuilds each advice chain; * caching can be provided by subclasses. * * @author Juergen Hoeller * @author Rod Johnson * @author Adrian Colyer * @since 2.0.3 */ @SuppressWarnings("serial") public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable { @Override public List<Object> 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<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);//創建一個攔截器鏈,也就是一會兒創建的攔截器會放到這個鏈條中。 Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass()); boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance(); //Advisor對象是單例非懶加載的,所以他們在bean工廠初始化的時候就已經實例化好了。每一個Advisor中包含的一個通知和一個切點,
//因此現在要把目標方法對應的所有通知組成攔截器鏈,可以從Advisor中取出通知,再將通知封裝到攔截器中(有些通知本身就是攔截器,以直接轉化為攔截器類型就可以) for (Advisor advisor : config.getAdvisors()) {//循環目標方法對應的所有Advisor對象,將每個Advisor中的通知封裝到攔截器中。 if (advisor instanceof PointcutAdvisor) { // Add it conditionally. PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//判斷是否預過濾,或是Advisor中的這個通知是否適用於(或者說是否想匹配)目標對象。 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) { //我一開始會奇怪為什么以一個Advisor作為參數卻獲得一個攔截器數組,不是應該一個Advisor對應一個攔截器嗎??
//en。。。。確實是一個Advisor對應一個攔截器。所以它每次返回來的數組都只有一個元素。
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;//返回攔截器鏈。 }
看看上述代碼中標記一處的代碼是如何實現的,這個代碼在DefaultAdvisorAdapterRegistry中
@Override public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3); Advice advice = advisor.getAdvice();//從Advisor中取出通知 if (advice instanceof MethodInterceptor) { interceptors.add((MethodInterceptor) advice);//如果通知本身是就是攔截器,那么進行類型轉換變成攔截器。 } for (AdvisorAdapter adapter : this.adapters) { if (adapter.supportsAdvice(advice)) {//通知本身不是攔截器,將通知封裝到攔截器當中。 interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } return interceptors.toArray(new MethodInterceptor[interceptors.size()]);//返回 }