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 Returning
和 Introduction
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
继承了MethodInterceptor
和DynamicIntroductionAdvice
,从而可以在 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
(这跟之后要介绍的拦截链有关)。MethodInterceptor
的invoke
方法是advice 对外统一的调用接口。
非 MethodInterceptor
实现的 Advice 都需要由由 AdvisorAdapter
装换为MethodInterceptor
才能使用。
public interface AdvisorAdapter {
boolean supportsAdvice(Advice advice);
MethodInterceptor getInterceptor(Advisor advisor);
}
After Returning 的转换例子
AfterReturningAdviceAdapter
将 AfterReturningAdvice
转化为 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;
}
常见的形式有 PointcutAdvisor
和 IntroductionAdvisor
PointcutAdvisor
是简单的 advice 和 pointcut 的组合。
IntroductionAdvisor
单独定义出来是由与和 Pointcut 的定义切点的方式不太一样(Pointcut 由class 和method 来确定切点,而introduction 的切点由只由class 确定)。
总结
- Pointcut - 切点。是组合class (
ClassFilter
) 和 method (MethodMatcher
) 的确定程序执行的中的一个切入点 - Advice - 想要执行的动作。有五种形式,但最终都会由
AdviceAdapter
转为MethodInterceptor
的实现 - Advisor - 一个只有一个 Pointcut 和 Advice 的 Asepct。可以是一个 singleton,也可能随 object 创建而创建
网友评论