Spring AOP的基本概念和抽象
JoinPoint
静态连接点
连接点是指程序执行的特定位置。比如:方法执行前,方法执行后,方法抛出异常后等等。这些特定的位置就是连接点。连接点在程序中是普遍存在的,或者说无处不在的。程序执行的任何一个位置都可以称之为连接点。

图中,JoinPoint、ProceedingJoinPoint和JoinPointImpl都位于aspectjweaver包中,这是AspectJ对于连接点这个概念的抽象和实现。而MethodInvocationProceedingJoinPoint是Spring为了支持AspectJ而对连接点的实现。注意:这里的连接点,指的是静态的连接点,它特指代码中的特定位置。
动态连接点
动态连接点是AOP联盟规范的概念,它指的是一个运行时的连接点。比如:在程序运行的过程当中,对某个方法(静态连接点)的一次调用,就称之为一个动态连接点。它特指运行时的调用动作或者事件。我们可以理解为:在程序运行的过程中,对静态连接点的访问动作就是动态连接点。下面是Spring框架对动态连接点的实现:

其中JoinPoint、Invocation、MethodInvocation和ConstructorInvocation都是AOP联盟的规范定义。ProxyMethodInvocation、ReflectiveMethodInvocation和CglibMethodInvocation是Spring AOP的实现。
Pointcut
一种谓词(通俗的说就是筛选条件或判断条件),用于筛选JoinPoint.Pointcut包含两个部分,ClassFilter和MethodMatcher。
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
ClassFilter
ClassFilter负责对目标类进行筛选。
public interface ClassFilter {
boolean matches(Class clazz);
}
如果ClassFilter的matches方法总是返回true,那么所有类都会被匹配上。
MethodMatcher
public interface MethodMatcher {
boolean matches(Method m, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method m, Class<?> targetClass, Object... args);
}
matches(Method, Class)方法用于检测具体的某个类中的某个方法是否满足条件。该方法的匹配结果可以被缓存,从而避免每次方法调用都进行匹配。如果matches(Method, Class)方法返回true,isRuntime()也返回true,那么就需要调用matches(Method , Class, Object... )方法进行匹配了。matches(Method, Class, Object... )可以对调用时传入目标方法的参数进行判断。
Pointcut类型
如果一个MethodMatcher的isRuntime()总是返回false,那么称这个MethodMatcher是静态的,否则的话就称这个MethodMatcher是动态的。判断一个MethodMatcher是不是动态的,就看这个MethodMatcher是不是需要对根据调用时的参数进行匹配。
静态Pointcut有:StaticMethodMatcherPointcut及其子类。主要有:JdkRegexpMethodPointcut和NameMatchMethodPointcut。AnnotationMatchingPointcut对类或者方法上的注解进行匹配,也属于静态Pointcut。
动态Pointcut有:DynamicMethodMatcherPointcut及其子类。主要有:PerTargetInstantiationModelPointcut。ControlFlowPointcut需要对目标函数的运行时堆栈信息进行匹配,因此也属于动态Pointcut。除此以外,还有AspectJExpressionPointcut也有可能是动态Pointcut(需要对参数匹配时)。
再看一下Pointcut的继承体系:

ExpressionPointcut主要用于使用表达式对类和方法进行匹配,比如AspectJ表达式。
AnnotationMatchingPointcut主要用于使用注解对类和方法进行匹配。
StaticMethodMatcherPointcut主要用于对方法的静态部分进行匹配,比如方法的名称、返回值类型、参数类型等。
DynamicMethodMatcherPointcut主要用于对方法的动态部分进行匹配,比如方法的参数值等。
Advice
增强,也叫通知。是植入到连接点上的一段代码。这段代码的目的往往是为了增加连接点的功能,因此叫增强。Spring中的Advice与AOP联盟的Advice是兼容的。Spring中定义的Advice的父类都是org.aopalliance.aop包中的Advice的子类。
看一下Spring增强的定义及继承体系:

其中Advice是org.aopalliance.aop包中定义的,Interceptor和MethodInterceptor是org.aopalliance.intercept包中定义的。他们都是AOP联盟规范接口。
Advice类型
Spring中的Advice类型有:BeforeAdvice,Interceptor和AfterAdvice。
Interceptor
由于Spring中只能对方法进行增强,因此Interceptor在Spring中特指MethodInterceptor。Interceptor和MethodInterceptor这两个接口也都是由AOP联盟定义的,Spring直接使用。
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
invoke()方法的参数MethodInvocation非常重要。它不仅暴露了目标方法的相关信息及本次调用的参数信息,还暴露了当前的代理对象的相关信息。
invoke()方法的返回值是本地调用的返回值:大多数情况下(不对返回值进行改写),与目标方法的返回值 一致。
来看一个官方文档中的demo:
public class DebugInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Before: invocation=[" + invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
MethodInvocation通常有一个MethodInterceptor Chain,invocation.proceed()会使Interceptor链向下传递下去。
Interceptor链的示意图如下:

值得一提的是,Spring将BeforeAdvice ThrowsAdvice AfterReturningAdvice最后都转化为MethodInterceptor放到过滤器链中。
Before Advice
前置增强在方法(连接点)执行前调用。使用前置增强时,用户不用显式的调用MethodInvocation的proceed()。
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
注意before方法的返回值是void,这意味着它可以在方法执行前对方法的参数进行干预,但是无法改变目标方法的返回值。如果before方法抛出了异常,那么会导致目标方法不能执行。
Throws Advice
异常增强在目标方法执行出现异常时会得到执行。下面的示例中,当目标方法抛出RemoteException时,第一个afterThrowing方法会被执行,当目标方法抛出ServletException时,第二个afterThrowing会被执行。
public static class CombinedThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(RemoteException ex) throws Throwable {
// Do something with remote exception
}
public void afterThrowing(Method m, Object[] args, Object target, ServletException ex) {
// Do something with all arguments
}
}
注意afterThrowing方法的名称是固定的,必须为afterThrowing。该方法的参数可以是一个,也可以是四个。如果是一个,则必须是抛出的异常类型。如果是四个,则是:Method,args,target,Exception。
After Returning Advice
后置增强是在目标方法正常返回时会得到执行。
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target)
throws Throwable;
}
对AspectJ的支持
Spring底层会将AspectJ注解解析成对应的Pointcut和Advice,其中注解中的表达式部分解析成Pointcut,增强逻辑代码解析成Advice。下面看看会将增强代码解析成哪些Advice。

Advisor

浙公网安备 33010602011771号