2.SpringAop之AdvisedSupport

1.类继承结构

2.解析

AdvisedSupport继承ProxyConfig,自身提供了以下功能

  1. 配置当前代理的Adivsiors
  2. 配置当前代理的目标对象
  3. 配置当前代理的接口
  4. 提供getInterceptorsAndDynamicInterceptionAdvice方法用来获取对应代理方法对应有效的拦截器链

AdvisedSupport本身不会提供创建代理的任何方法,专注于生成拦截器链

我们来探究下第4点中的拦截器是什么东西,以及getInterceptorsAndDynamicInterceptionAdvice的源码实现

先看下拦截器(MethodInterceptor)的接口定义

public interface MethodInterceptor extends Interceptor {
    
    Object invoke(MethodInvocation invocation) throws Throwable;
}

拦截器的作用是封装在目标方法前后执行相应的逻辑,在Aop中的前置,后置,环绕等通知都有对应拦截器实现

再来看下MethodInvocation的定义

image

MethodInvocation中proceed方法比较重要

/**
* Proceeds to the next interceptor in the chain.
*
* <p>The implementation and the semantics of this method depends
* on the actual joinpoint type (see the children interfaces).
*
* @return see the children interfaces' proceed definition.
*
* @throws Throwable if the joinpoint throws an exception. */
Object proceed() throws Throwable;

proceed()方法封装了调用下一个拦截器的逻辑,如果拦截器执行完毕,那么就执行被代理方法
目前在spring框架里面MethodInvocation接口的实现类只有一个,就是ReflectiveMethodInvocation
看下它的proceed方法实现

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;
        if (dm.methodMatcher.matches(this.method, this.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.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

这里会依次执行拦截器,最后执行被代理的方法
其中InterceptorAndDynamicMethodMatcher为动态拦截器,根据运行时参数来决定拦截器是否生效
有动态拦截器当然也有静态拦截器,静态拦截器一般通过包名,类名,方法名 ,参数来确定静态拦截器是否对代理方法生效

proceed方法中的interceptorsAndDynamicMethodMatchers拦截器链就是由AdvisedSupport的getInterceptorsAndDynamicInterceptionAdvice来获取,接下来看下获取拦截器链的具体实现

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(
                this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

这个方法对需要获取的拦截器链做了缓存,实际获取逻辑委托给DefaultAdvisorChainFactory来实现

public interface AdvisorChainFactory {

    /**
     * Determine a list of {@link org.aopalliance.intercept.MethodInterceptor} objects
     * for the given advisor chain configuration.
     * @param config the AOP configuration in the form of an Advised object
     * @param method the proxied method
     * @param targetClass the target class (may be {@code null} to indicate a proxy without
     * target object, in which case the method's declaring class is the next best option)
     * @return List of MethodInterceptors (may also include InterceptorAndDynamicMethodMatchers)
     */
    List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass);

}

在DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass)方法中,遍历AdviseSupport里面配置的所有advisors,并且执行PointCut中MethodMatcher的matches方法来决定对应Advice是否生效,最后得到代理方法的拦截器链
具体代码逻辑如下

AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
    if (advisor instanceof PointcutAdvisor) {
        // Add it conditionally.
        PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
        if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
            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));
    }
}

注意到将Advisor转换为Interceptor拦截器使用了registry.getInterceptors(advisor),转换的逻辑委托给了AdvisorAdapterRegistry实现,注意下转换后得到的是数组,因为Advisor可配置多个Advice

AdvisorAdapterRegistry接口定义如下

public interface AdvisorAdapterRegistry {

    Advisor wrap(Object advice) throws UnknownAdviceTypeException;

    MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException;

    void registerAdvisorAdapter(AdvisorAdapter adapter);
}

其中getInterceptors方法实现如下

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
    Advice advice = advisor.getAdvice();
    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()]);
}

在这个方法里面主要通过AdvisorAdapter 来把Advisor里面的Adivce(通知)转换为对应的拦截器MethodInterceptor

AdvisorAdapter接口定义如下

public interface AdvisorAdapter {

    boolean supportsAdvice(Advice advice);

    MethodInterceptor getInterceptor(Advisor advisor);

}

从接口推断出,每种类型Advisor都会有一个AdvisorAdapter接口实现对应
在DefaultAdvisorAdapterRegistry中默认注册了3个AdvisorAdapter

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

注意到并没有环绕通知,因为环绕通知直接采用MethodInteceptor来实现
在getInterceptors方法里面可以看到

if (advice instanceof MethodInterceptor) {
    interceptors.add((MethodInterceptor) advice);
}

这句代码就是为了给环绕通知做处理

看下AdvisorAdapter和对应拦截器的具体实现能让我们理解更加深刻,我们来看下MethodBeforeAdviceAdapter和MethodBeforeAdviceInterceptor

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}
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;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }
}

转换拦截器的作用主要是形成链式调用,在拦截器的invoke方法都会触发调用下一个拦截器

总结

这个类的主要作用是将Advisor转换为有效拦截器链,接下来我们只要关注怎么生成代理对象,以及在代理对象中怎么调用拦截器链

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

推荐阅读更多精彩内容