美文网首页
Spring AOP源码01 - 切点的定义

Spring AOP源码01 - 切点的定义

作者: lxian2shell | 来源:发表于2018-01-08 04:56 被阅读0次

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 创建而创建

相关文章

网友评论

      本文标题:Spring AOP源码01 - 切点的定义

      本文链接:https://www.haomeiwen.com/subject/gsgunxtx.html