AOP-方法攔截器-筆記


  方法攔截器的繼承層次圖:

 

這些攔截器具體長什么樣??

一、MethodBeforeAdviceInterceptor

這個攔截器只有一個屬性就是前置通知。需要注意的是前置通知和返回通知的攔截器才會持有的通知的引用,也就是攔截器會有一個屬性是前置通知或返回通知。其他三個既是通知又是攔截器。如:AspectJAfterAdvice 既是通知又是攔截器,AspectJAfterThrowingAdviceAspectJAroundAdvice也同樣既是通知又是攔截器。

/**
 * 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()]);//返回
    }

 


免責聲明!

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



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