【源码】Spring AOP 4 Pointcut
前言
Spring AOP 同样也定义好几大族接口,以辅助 AOP 的实现,这章节我们来解析 Pointcut 接口
Pointcut ,切点。它是对 Joinpoint 的匹配点的抽象,可以理解为 Advice 通过 Pointcut 找到 Joinpoint
Pointcut
public interface Pointcut {
// 类匹配
ClassFilter getClassFilter();
// 方法匹配
MethodMatcher getMethodMatcher();
// 匹配所有 Pointcut
Pointcut TRUE = TruePointcut.INSTANCE;
}
Pointcut
由 ClassFilter
和 MethodMatcher
组成
ClassFilter
@FunctionalInterface
public interface ClassFilter {
// 给定类是否匹配
boolean matches(Class<?> clazz);
// 匹配所有类
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
ClassFilter
相对比较简单,主要提供 match
方法校验目标类是否匹配
AnnotationClassFilter
// 检查给定类是否包含指定注解
public class AnnotationClassFilter implements ClassFilter {
// 目标注解
private final Class<? extends Annotation> annotationType;
// 是否检查父类
private final boolean checkInherited;
// ...
/**
* 层级检查依赖方法 AnnotatedElementUtils#hasAnnotation
* 非层级检查依赖方法 Class#isAnnotationPresent
*/
@Override
public boolean matches(Class<?> clazz) {
return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(clazz, this.annotationType) :
clazz.isAnnotationPresent(this.annotationType));
}
// ...
}
- 常用的实现类
AnnotationClassFilter
,主要是基于注解匹配类 - 属性
checkInherited
决定是否检查上层类,分别基于方法AnnotatedElementUtils#hasAnnotation
和Class#isAnnotationPresent
AnnotationCandidateClassFilter
private static class AnnotationCandidateClassFilter implements ClassFilter {
// 指定注解
private final Class<? extends Annotation> annotationType;
AnnotationCandidateClassFilter(Class<? extends Annotation> annotationType) {
this.annotationType = annotationType;
}
// 依赖 AnnotationUtils#isCandidateClass:类、方法、属性上是否有指定注解
@Override
public boolean matches(Class<?> clazz) {
return AnnotationUtils.isCandidateClass(clazz, this.annotationType);
}
// ...
}
一个常用的内部类实现,也是基于注解匹配,但是主要类、方法、属性任一含有目标注解就匹配,比如 @Async
注解
MethodMatcher
public interface MethodMatcher {
// 静态匹配
boolean matches(Method method, Class<?> targetClass);
// true 动态 false 静态
boolean isRuntime();
// 动态匹配
boolean matches(Method method, Class<?> targetClass, Object... args);
// 匹配所有方法
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
MethodMatcher
主要区分两类:
静态匹配
,不会对方法的参数进行匹配动态匹配
,会同时匹配方法的参数,但不常用
主要看下静态相关实现
StaticMethodMatcher
public abstract class StaticMethodMatcher implements MethodMatcher {
// 静态
@Override
public final boolean isRuntime() {
return false;
}
// 因为是静态,所以动态匹配方法 UnsupportedOperationException
@Override
public final boolean matches(Method method, Class<?> targetClass, Object... args) {
throw new UnsupportedOperationException("Illegal MethodMatcher usage");
}
}
静态抽象基类,isRuntime
返回 false
,动态匹配方法调用抛出 UnsupportedOperationException
异常,主要聚焦于 matches(Method method, Class<?> targetClass)
方法的实现
AnnotationMethodMatcher
public class AnnotationMethodMatcher extends StaticMethodMatcher {
// 目标注解
private final Class<? extends Annotation> annotationType;
// 是否检查层级
private final boolean checkInherited;
// ...
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 先进行一次快速匹配
if (matchesMethod(method)) {
return true;
}
// 忽略代理类,因为代理类没有注解信息
if (Proxy.isProxyClass(targetClass)) {
return false;
}
// 假设是接口方法,尝试获取实现类方法
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 如果获取到了再匹配一次
return (specificMethod != method && matchesMethod(specificMethod));
}
// 根据 checkInherited 区分是否需要检查父类方法
private boolean matchesMethod(Method method) {
return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(method, this.annotationType) :
method.isAnnotationPresent(this.annotationType));
}
// ...
}
- 跟
AnnotationClassFilter
对应的,AnnotationMethodMatcher
基于注解进行方法匹配 - 同样的,属性
checkInherited
决定是否层级匹配
StaticMethodMatcherPointcut
/**
* 首先是一个 MethodMatcher,同时是一个 Pointcut,这样就可以拓展出
* ClassFilter 的能力了,值得学习
*/
public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {
private ClassFilter classFilter = ClassFilter.TRUE;
// 允许指定一个 ClassFilter,拓展出了 ClassFilter 的能力
public void setClassFilter(ClassFilter classFilter) {
this.classFilter = classFilter;
}
@Override
public ClassFilter getClassFilter() {
return this.classFilter;
}
// 自己就是个 MethodMatcher
@Override
public final MethodMatcher getMethodMatcher() {
return this;
}
}
- 挺值得学习的设计模式,类似一种组合,跟
MethodBeforeAdviceInterceptor
的设计类似,StaticMethodMatcherPointcut
本身是个MethodMatcher
,实现Pointcut
又拓展出了ClassFilter
的能力,这样子类只要专注实现MethodMatcher
的方法就好 setClassFilter
方法支持设置指定的ClassFilter
NameMatchMethodPointcut
public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {
// 匹配模板
private List<String> mappedNames = new ArrayList<>();
public void setMappedName(String mappedName) {
setMappedNames(mappedName);
}
public void setMappedNames(String... mappedNames) {
this.mappedNames = new ArrayList<>(Arrays.asList(mappedNames));
}
// 添加模板方法
public NameMatchMethodPointcut addMethodName(String name) {
this.mappedNames.add(name);
return this;
}
// 静态匹配,equals 或者正则匹配
@Override
public boolean matches(Method method, Class<?> targetClass) {
for (String mappedName : this.mappedNames) {
if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
return true;
}
}
return false;
}
// 支持形如 "xxx*", "*xxx" 的正则匹配
protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
// 略
}
在 StaticMethodMatcherPointcut
的基础上,支持基于方法名匹配,同时支持形如 xxx*
, *xxx
的模糊匹配,比如*Service
匹配所有 Serivce
结尾的方法
demo
@My
public class A {
public void aaa() {
}
public void bbb() {
}
}
public class B {
public void aaa() {
}
}
public class NameMatchMethodTest {
public static void main(String[] args) {
NameMatchMethodPointcut matchMethodPointcut
= new NameMatchMethodPointcut();
matchMethodPointcut.setClassFilter(
new AnnotationClassFilter(My.class)
);
matchMethodPointcut.setMappedName("*a");
if (matchMethodPointcut.getClassFilter().matches(A.class)) {
for (Method m : A.class.getDeclaredMethods()) {
boolean match = matchMethodPointcut.matches(m, A.class);
System.out.println(
"A类的方法" + m.getName() + (match ? " 匹配" : " 不匹配")
);
}
} else {
System.out.println("A类不匹配");
}
if (matchMethodPointcut.getClassFilter().matches(B.class)) {
for (Method m : B.class.getDeclaredMethods()) {
boolean match = matchMethodPointcut.matches(m, B.class);
System.out.println(
"B类的方法" + m.getName() + (match ? " 匹配" : " 不匹配")
);
}
} else {
System.out.println("B类不匹配");
}
}
}
// 结果
A类的方法aaa 匹配
A类的方法bbb 不匹配
B类不匹配
StaticMethodMatcherPointcutDemo
public class StaticMethodMatcherPointcutDemo extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return "a".equals(method.getName());
}
public void a() {
}
public void b() {
}
@Test
public void test() throws NoSuchMethodException {
StaticMethodMatcherPointcutDemo demo = new StaticMethodMatcherPointcutDemo();
demo.setClassFilter(clazz -> clazz == StaticMethodMatcherPointcutDemo.class);
boolean a = demo.matches(StaticMethodMatcherPointcutDemo.class.getMethod("a")
, StaticMethodMatcherPointcutDemo.class);
System.out.println(a); // true
boolean b = demo.matches(StaticMethodMatcherPointcutDemo.class.getMethod("b")
, StaticMethodMatcherPointcutDemo.class);
System.out.println(b); // false
}
}
自己实现一个 StaticMethodMatcherPointcut
加深印象
ComposablePointcut
Composable
(可组合的)的 Pointcut
,提供了 union(并集)
intersection(交集)
等方法,而且还支持链式调用,设计思想值得学习
demo
@Deprecated
public static class Sample {
@Autowired
public void a() {
}
// @After("")
public void b() {
}
}
@Test
public void test() throws NoSuchMethodException {
AnnotationMatchingPointcut annotationMatchingPointcut
= new AnnotationMatchingPointcut(Deprecated.class, After.class, false);
ComposablePointcut union = new ComposablePointcut(annotationMatchingPointcut)
.union(new AnnotationMethodMatcher(Autowired.class));
Method a = Sample.class.getMethod("a");
Method b = Sample.class.getMethod("b");
// true
System.out.println(union.getMethodMatcher().matches(a, String.class));
// false
System.out.println(union.getMethodMatcher().matches(b, String.class));
}
AspectJExpressionPointcut
这个类比较复杂也比较重要,它的主要功能可以根据类名理解:基于 AspectJ
表达式的 Pointcut
同时它也是 ClassFilter
MethodMatcher
,对于类和方法的匹配都是基于 org.aspectj.weaver.tools
下的原生类来实现的
写个 demo
演示一下
public class AService implements A {
public void a() {
}
}
public class BService implements B {
public void b() {
}
}
public class Test2 {
@Test
public void test1() {
AspectJExpressionPointcut aspectJExpressionPointcut
= new AspectJExpressionPointcut();
aspectJExpressionPointcut.setExpression("execution(* a())");
System.out.println("A类:" + aspectJExpressionPointcut.matches(AService.class));
System.out.println("B类:" + aspectJExpressionPointcut.matches(BService.class));
System.out.println("================================");
for (Method m : AService.class.getDeclaredMethods()) {
System.out.println(m.getName() + ": " +
aspectJExpressionPointcut.matches(m, m.getDeclaringClass()));
}
System.out.println("================================");
for (Method m : BService.class.getDeclaredMethods()) {
System.out.println(m.getName() + ": " +
aspectJExpressionPointcut.matches(m, m.getDeclaringClass()));
}
}
}
// 结果
A类:true
B类:true
================================
a: true
================================
b: false
类图
Pointcut
ClassFilter
MethodMatcher
总结
这一章节涉及的类比较多一点,主要对 Pointcut 体系抽象做了整体的了解,它可以与 ClassFilter 和 MethodMatcher 组合,来实现对类、方法的匹配
其中 AspectJExpressionPointcut 就是对基于注解的 AspectJ 的 切点表达式 的匹配实现
下一章节,了解下 Spring 下的 Advisor 抽象
上一篇:【源码】Spring AOP 3 Joinpoint
下一篇:【源码】Spring AOP 5 Advisor
参考
【小家Spring】Spring AOP核心类Pointcut解析,对PointcutExpression切点表达式解析原理分析(以AspectJExpressionPointcut为例)