38--SpringAop代理调用过程(二)

接前面一章继续分析SpringAOP获取拦截器链和拦截器链的调用过程。

1.获取拦截器链
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}
/**
 * 获取拦截器链
 */
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {

    // This is somewhat tricky... We have to process introductions first,
    // but we need to preserve order in the ultimate list.
    // 获取AdvisorAdapterRegistry对象,Spring默认初始化了MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter和ThrowsAdviceAdapter
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 获取所有增强
    Advisor[] advisors = config.getAdvisors();
    // 创建interceptorList保存返回结果,这里可以看到new ArrayList指定了集合长度,也是编码中节约内存开销的一个小技巧
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    // 获取代理类的Class对象
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;

    // 循环所有的增强
    for (Advisor advisor : advisors) {
        // 如果增强是PointcutAdvisor的实例
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // config.isPreFiltered() -> 返回是否对该代理配置进行了预筛选,以便仅对其进行筛选包含适用的增强(匹配此代理的目标类)。
            // pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass) -> 当前切点匹配的类是否匹配actualClass
            // 以上两个条件是在类一级别上做出判断,如果符合,则接下来对方法级别的再做匹配判断
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                // 获取当前切点匹配的方法
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                // 区分普通的MethodMatcher和IntroductionAwareMethodMatcher,分别调用不同的匹配方法做出判断
                // IntroductionAwareMethodMatcher可以作用域引入类型的增强,且当匹配方法不包含引用增强时,可以提升匹配效率
                if (mm instanceof IntroductionAwareMethodMatcher) {
                    if (hasIntroductions == null) {
                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                    }
                    match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                }
                else {
                    match = mm.matches(method, actualClass);
                }
                // 如果匹配
                if (match) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    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));
                    }
                }
            }
        }
        // 如果增强是IntroductionAdvisor实例
        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;
}

该段代码比较关键的点:

// 如果匹配,获取方法拦截器
if (match) {
    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
    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));
    }
}

通过上面的代码可以发现Spring最终还是要把增强(切面)转换为方法拦截器,来看其具体的实现:

/**
 * 获取方法连接器
 */
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<>(3);
    Advice advice = advisor.getAdvice();
    // 1.如果增强是MethodInterceptor类型直接添加
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    // 2.循环增强适配器,并判断是否支持
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    // 3.返回结果
    return interceptors.toArray(new MethodInterceptor[0]);
}

通过这段代码,如果增强是MethodInterceptor的实例,直接加入结果中;那么哪些增强是MethodInterceptor类型呢?先不用着急,继续看下面的处理,下面的代码里出现了一个增强适配器,我们来看一下其中都包含了哪些适配器类型:

private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

DefaultAdvisorAdapterRegistry通过构造函数对增强适配器进行了初始化,包含了MethodBeforeAdviceAdapter前置增强适配器、AfterReturningAdviceAdapter后置返回增强适配器、ThrowsAdviceAdapter后置异常增强适配器。那么从这里我们也可以推断出,除了这几种增强适配器对应的增强类型之外,其他的都是MethodInterceptor类型。

接下来看下这三种适配器都做了哪些工作:

  • MethodBeforeAdviceAdapter
/**
 * 增强适配器
 * 这个方法在获取拦截器链的时候调用,从这里也可以看出,Spring中的advisor(增强/切面)
 * 最终还是被转换为MethodInterceptor对象
 *
 * AdvisorAdapter的实现类有AfterReturningAdviceAdapter,MethodBeforeAdviceAdapter,ThrowsAdviceAdapter三个
 *
 * AfterReturningAdviceAdapter -> new AfterReturningAdviceInterceptor(advice) -> 后置返回增强
 * MethodBeforeAdviceAdapter -> new MethodBeforeAdviceInterceptor(advice) -> 前置增强
 * ThrowsAdviceAdapter -> new ThrowsAdviceInterceptor(advisor.getAdvice()) -> 该适配器有些特殊...看源码吧
 */
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
    MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
    return new MethodBeforeAdviceInterceptor(advice);
}
  • AfterReturningAdviceAdapter
public MethodInterceptor getInterceptor(Advisor advisor) {
    AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
    return new AfterReturningAdviceInterceptor(advice);
}
  • ThrowsAdviceAdapter
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
    return new ThrowsAdviceInterceptor(advisor.getAdvice());
}

看到这里大家一定有所了解了,Spring中的增强,最终还是会转换为方法拦截器调用。

3. 拦截器链调动过程
/**
 * 调用拦截器链
 *
 * currentInterceptorIndex维护了一个计数器,该计数器从-1开始,当计数器值等于拦截方法长度减一时,
 * 表名所有的增强方法已经被调用(但是不一定被真正执行),那么此时调用连接点的方法,针对本例:即sayHello方法
 */
@Override
@Nullable
public Object proceed() throws Throwable {
    //  We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    // 动态匹配增强
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 匹配成功则执行
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        // 匹配失败则跳过并执行下一个拦截器
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    // 静态增强
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // System.out.println(interceptorOrInterceptionAdvice.getClass());
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

这段代码看似简单,但是如果真的debug进去,方法栈还是比较深刻的,前面介绍过在获取到合适的增强集合之后,首先在其首位加入了ExposeInvocationInterceptor拦截器,然后对增强集合进行了排序(当然ExposeInvocationInterceptor依然会在首位),那么接下来第一个拦截器调用就是ExposeInvocationInterceptor了。

  1. ExposeInvocationInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    MethodInvocation oldInvocation = invocation.get();
    invocation.set(mi);
    try {
        // 继续拦截器链调用
        return mi.proceed();
    }
    finally {
        invocation.set(oldInvocation);
    }
}
  1. AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 继续拦截器链调用
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}
  1. AspectJAfterThrowingAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 继续拦截器链调用
        return mi.proceed();
    }
    catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}
  1. AfterReturningAdviceInterceptor
public Object invoke(MethodInvocation mi) throws Throwable {
    // 继续拦截器链调用
    Object retVal = mi.proceed();
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}
  1. AspectJAfterAdvice
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 继续拦截器链调用
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}
  1. AspectJAroundAdvice
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);
}

在这里终于看到了方法的调用:

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterCount() == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // TODO AopUtils.invokeJoinpointUsingReflection
        // 调用代理方法
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
  }
public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
{
    if (!override) {
        if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
            Class<?> caller = Reflection.getCallerClass();
            checkAccess(caller, clazz, obj, modifiers);
        }
    }
    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);
}

代码终于执行到了Method类的invoke方法,接下来就可以调用我们的增强方法了:

  1. MethodBeforeAdviceInterceptor
@Around("test()")
public Object aroundTest(ProceedingJoinPoint p) throws Throwable {
    System.out.println("==环绕增强开始");
    // 继续拦截器链调用
    Object o = p.proceed();
    System.out.println("==环绕增强结束");
    return o;
}

执行完之后,我们发现前置增强还没有被调用进来,继续:

public Object invoke(MethodInvocation mi) throws Throwable {
    // 调用前置增强
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}
  1. 目标方法
    执行完前置增强之后,再次进入到proceed方法,这时候,计数器已经满足条件了,执行目标方法调用:
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    return invokeJoinpoint();
}

待执行完目标方法调用后,再将之前压入方法栈的那些增强方法依次出栈并调用。这一段的代码调用用文字表述挺困难的,大家自己跟踪代码吧。。。

到这里拦截器链的调用过程分析就结束了,这里分析的不是那么明确,还是多跟踪代码吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,454评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,553评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,921评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,648评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,770评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,950评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,090评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,817评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,275评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,592评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,724评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,409评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,052评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,815评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,043评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,503评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,627评论 2 350

推荐阅读更多精彩内容