本节回顾一下AOP的概念
AOP是面向切面的编程思想,百科的概述为:
Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点。从关注点中分离出横切关注点是面向切面的程序设计的核心概念。分离关注点是解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序的变动就可以很好的管理起来。
Advice通知定义在连接点做什么,为切面增强提供织入接口。在Spring AOP中,主要描述Spring AOP围绕方法调用而注入的切面行为。Advice是AOP联盟定义的一个接口,具体的接口定义在org.aopalliance.aop.Advice中。具体的通知类型比如BeforeAdvice、AfterAdvice、AfterReturningAdvice、ThrowsAdvice等。
下面从BeforeAdvice看一下类层次关系:
可以看到MethodBeforeAdvice为待增强的目标方法设置的前置增强接口:
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, @Nullable Object target) throws Throwable;
}
作为回调函数,before方法的实现在Advice中被配置到目标方法前,会在调用目标方式时被回调。
具体的调用参数:Method对象,这个参数是目标方法的反射对象;Object[]对象数组,这个对象数组中包含目标方法的输入参数。以CountingBeforeAdvice为例子,CountingBeforeAdvice是MethodBeforeAdvice的具体实现:
@SuppressWarnings("serial")
public class CountingBeforeAdvice extends MethodCounter implements MethodBeforeAdvice {
@Override
public void before(Method m, Object[] args, Object target) throws Throwable {
count(m);
}
}
CountingBeforeAdvice是完成统计被调用的方法次数。作为切面增强实现,会根据调用方法的方法名进行统计,把统计结果根据方法名和调用刺水作为键值对放入一个map中。
count方法在基类MethodCounter中实现,它在对象中维护一个哈希表,用来存储统计数据。在统计过程中,首先通过目标方法的反射对象得到方法名,然后进行累加,把统计结果放到维护的哈希表中。
public class MethodCounter implements Serializable {
/** 这个hashMap存储方法名和调用次数的键值对 */
private HashMap<String, Integer> map = new HashMap<>();
private int allCount;//所有的调用次数
protected void count(Method m) {
count(m.getName());
}
protected void count(String methodName) {//根据目标方法的方法名进行统计调用次数
Integer i = map.get(methodName);
i = (i != null) ? new Integer(i.intValue() + 1) : new Integer(1);
map.put(methodName, i);
++allCount;
}
public int getCalls(String methodName) {//根据方法名获得调用的次数
Integer i = map.get(methodName);
return (i != null ? i.intValue() : 0);
}
public int getCalls() {//取得所有方法的调用次数
return allCount;
}
/**
* A bit simplistic: just wants the same class.
* Doesn't worry about counts.
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object other) {
return (other != null && other.getClass() == this.getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
Pointcut切点决定Advice通知应该作用于哪个连接点,也就是说通过Pointcut定义需要增强的方法的集合,这些集合的选取可以按照一定的规则来完成。
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
在接口的定义可以看到,需要返回一个MethodMatcher。对于Ponitcut的匹配判断功能,具体由这个返回的MethodMatcher完成,也就是由MethodMatcher判断是否需要对当前方法进行增强,或者是否需要对当前方法应用配置好的Advice通知。
切点在Spring AOP中的类继承体系:
下面以正则表达式切点JdkRegexpMethodPointcut为实例,了解一下Pointcut的工作原理。
通过基类StaticMethodMatcherPointcut可以看到,getMethodMatcher返回的是StaticMethodMatcher。
在JdkRegexpMethodPointcut可以看到一个matches方法,这个方法是使用正则表达式对方法名进行匹配的地方。
@Override
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
此外还有通过方法名匹配进行Advice匹配的NameMatchMethodPointcut
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass) {
for (String mappedName : this.mappedNames) {
if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
return true;
}
}
return false;
}
Advisor通知器,完成对目标方法的切面增强设计和关注点的设计以后,需要一个对象结合起来,完成这个作用的就是Advisor通知器。
我们以DefaultPointcutAdvisor为例子,来了解Advisor的工作原理。
在DefaultPointcutAdvisor中,有两个属性,分别是advice和pointcut。通过这两个属性,可以分别配置Advice和Pointcut:
@SuppressWarnings("serial")
public class DefaultPointcutAdvisor extends AbstractGenericPointcutAdvisor implements Serializable {
private Pointcut pointcut = Pointcut.TRUE;
public DefaultPointcutAdvisor() {
}
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
this.pointcut = pointcut;
setAdvice(advice);
}
public void setPointcut(@Nullable Pointcut pointcut) {
this.pointcut = (pointcut != null ? pointcut : Pointcut.TRUE);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String toString() {
return getClass().getName() + ": pointcut [" + getPointcut() + "]; advice [" + getAdvice() + "]";
}
}
在DefaultPointcutAdvisor中,Pointcut默认是Pointcut.True
Pointcut TRUE = TruePointcut.INSTANCE;
TruePointcut的INSTANCE是一个单例。可以看到采用了单例模式
class TruePointcut implements Pointcut, Serializable {
public static final TruePointcut INSTANCE = new TruePointcut();
private TruePointcut() {
}
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
@Override//对任何方法的匹配都返回true
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "Pointcut.TRUE";
}
}
class TrueMethodMatcher implements MethodMatcher, Serializable {
public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher();
private TrueMethodMatcher() {
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass) {
return true;
}
@Override
public boolean matches(Method method, @Nullable Class<?> targetClass, Object... args) {
// Should never be invoked as isRuntime returns false.
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "MethodMatcher.TRUE";
}
/**
* Required to support serialization. Replaces with canonical
* instance on deserialization, protecting Singleton pattern.
* Alternative to overriding {@code equals()}.
*/
private Object readResolve() {
return INSTANCE;
}
}