Spring AOP源码01 - 切点的定义

Pointcut

切点,定义切面的位置。Spring 中的切点可以在class 或者method 上。
所有的Pointcut 都遵循下面的interface

public interface Pointcut {

    ClassFilter getClassFilter();
    
    MethodMatcher getMethodMatcher();
}

public interface ClassFilter {

    boolean matches(Class clazz);
}

public interface MethodMatcher {

    boolean matches(Method m, Class targetClass);

    boolean isRuntime();

    boolean matches(Method m, Class targetClass, Object[] args);
}

matches(Method m, Class targetClass) 用于静态检查
matches(Method m, Class targetClass, Object[] args) 用于runtime检查,比如可以在这里检查传入的参数。
MethodMatcher 先会检查matches(Method m, Class targetClass) 的结果,如果通过,则检查isRuntime() 是否为true。true 的话则继续检查 matches(Method m, Class targetClass, Object[] args)

一些内置的Pointcut

StaticMethodMatcher - 只有静态方法检查的切点
AspectJExpressionPointcut - 利用AspectJ 去parse AspectJ 的语法,然后检查切点。
JdkRegexpMethodPointcut - 用JDK 内置的Regexp 来做方法名字匹配的切点
ControlFlowPointcut - 提供类似AspectJ 的cflow 的切点

ControlFlowPointcut

ControlFlowPointcut 是 一个比较有趣的实现。类似 AspectJ 的 c-flow, 它会match调用栈上面所有符合条件的切点

        for (StackTraceElement element : new Throwable().getStackTrace()) {
            if (element.getClassName().equals(this.clazz.getName()) &&
                    (this.methodName == null || element.getMethodName().equals(this.methodName))) {
                return true;
            }
        }
        return false;

使用new Throwable().getStackTrace() 来获取当前的调用栈,然后遍历所有的调用

Advice

Action to take at pointcut.

基本形式

有五种类型 Around, Before, After Throwing, After ReturningIntroduction

Around

public interface MethodInterceptor extends Interceptor {

    Object invoke(MethodInvocation invocation) throws Throwable;
}

invocation 是pointcut 上的调用,可以在其前后加入自己的代码,然后使用 invocation.proceed() 调用原本的方法。也可以选择放弃继续调用。

Before

public interface MethodBeforeAdvice extends BeforeAdvice {

    void before(Method m, Object[] args, Object target) throws Throwable;
}

After Throwing

比较特殊一点,要求implement 空的interface ThrowsAdvice,然后实现

// [ .. ] 里面是 optional arguments
afterThrowing([Method, args, target], subclassOfThrowable)

可以重载多个。
afterThrowing 方法最终会在ThrowsAdviceInterceptor 中被通过反射来调用

Method[] methods = throwsAdvice.getClass().getMethods();
for (Method method : methods) {
    if (method.getName().equals(AFTER_THROWING) &&
            (method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
            Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
        ) {
        // Have an exception handler
        this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
        if (logger.isDebugEnabled()) {
            logger.debug("Found exception handler method: " + method);
        }
    }
}

After Returning

public interface AfterReturningAdvice extends Advice {

    void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable;
}

Introduction

实际是一种 Mixin, 把其他的interface 及实现加进来。
IntroductionInterceptor 继承了MethodInterceptorDynamicIntroductionAdvice,从而可以在 invoke 中用implementsInterface 检查是否需要对方法进行拦截

public interface DynamicIntroductionAdvice extends Advice {
    boolean implementsInterface(Class<?> intf);
}

public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
}

IntroductionInterceptor 的一个简单的实现是 DelegatingIntroductionInterceptor

public Object invoke(MethodInvocation mi) throws Throwable {
     // 用implementsInterface 检查是否是introduction interface 上的方法
    //  是则拦截,否则调用原方法
    if (isMethodOnIntroducedInterface(mi)) {
        Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());
        ....
    }

    return doProceed(mi);
}

protected final boolean isMethodOnIntroducedInterface(MethodInvocation mi) {
    Boolean rememberedResult = this.rememberedMethods.get(mi.getMethod());
    if (rememberedResult != null) {
        return rememberedResult;
    }
    else {
        // Work it out and cache it.
        boolean result = implementsInterface(mi.getMethod().getDeclaringClass());
        this.rememberedMethods.put(mi.getMethod(), result);
        return result;
    }
}

Advice Adapter

虽然有五种形式,但是能被直接使用的只有MethodInterceptor (这跟之后要介绍的拦截链有关)。MethodInterceptorinvoke 方法是advice 对外统一的调用接口。

MethodInterceptor 实现的 Advice 都需要由由 AdvisorAdapter 装换为MethodInterceptor 才能使用。

public interface AdvisorAdapter {

    boolean supportsAdvice(Advice advice);

    MethodInterceptor getInterceptor(Advisor advisor);
}

After Returning 的转换例子

AfterReturningAdviceAdapterAfterReturningAdvice 转化为 AfterReturningAdviceInterceptor
AfterReturningAdvice.afterReturning()MethodInvocation.proceed() 之后被调用,完成了 After Returning 的语义。

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {

    private final AfterReturningAdvice advice;

    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();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }

}

Advisor

Advisor 包含一个advice 和其对应的 pointcut。是一个完整的切面 (Aspect)。

Advisor 的base interface 包含一个获取advice 的方法getAdvice()和一个advice 是否应为singleton 的flag isPerInstance

public interface Advisor {

    Advice getAdvice();

    boolean isPerInstance();

}

public interface PointcutAdvisor extends Advisor {

    Pointcut getPointcut();
}

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {

    ClassFilter getClassFilter();

    void validateInterfaces() throws IllegalArgumentException;
}

常见的形式有 PointcutAdvisorIntroductionAdvisor
PointcutAdvisor 是简单的 advice 和 pointcut 的组合。
IntroductionAdvisor 单独定义出来是由与和 Pointcut 的定义切点的方式不太一样(Pointcut 由class 和method 来确定切点,而introduction 的切点由只由class 确定)。

总结

  1. Pointcut - 切点。是组合class (ClassFilter) 和 method (MethodMatcher) 的确定程序执行的中的一个切入点
  2. Advice - 想要执行的动作。有五种形式,但最终都会由 AdviceAdapter
    转为 MethodInterceptor 的实现
  3. Advisor - 一个只有一个 Pointcut 和 Advice 的 Asepct。可以是一个 singleton,也可能随 object 创建而创建
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 本博中关于spring的文章:Spring IOC和AOP原理,Spring事务原理探究,Spring配置文件属性...
    Maggie编程去阅读 4,067评论 0 34
  • **** AOP 面向切面编程 底层原理 代理!!! 今天AOP课程1、 Spring 传统 AOP2、 Spri...
    luweicheng24阅读 1,316评论 0 1
  • 一、增强类 1 前置增强(MethodBeforeAdvice)重写before(Method method,Ob...
    Q南南南Q阅读 1,150评论 0 0
  • 双峰山,顾名思义,有两个主峰。山不算高,也就几百米的样子,但是植被覆盖很广,山下有一个村庄,收入主要靠景区旅游。我...
    愿河阅读 333评论 0 1
  • 昨天看到专栏提到对"服务业"的看法,确实有点沮丧。感觉只会长期待在模式一当中,模式二好像根本没有。 今天的问答,从...
    水中望我阅读 155评论 0 1