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

 

posted @ 2018-01-08 12:27  钓鱼翁  阅读(2965)  评论(0编辑  收藏  举报