#yyds干货盘点# 老王读Spring AOP-1Pointcut如何匹配到 join point
Posted 老王学源码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#yyds干货盘点# 老王读Spring AOP-1Pointcut如何匹配到 join point相关的知识,希望对你有一定的参考价值。
@[TOC](Pointcut如何匹配到 join point)
前言
通过前面的介绍,我们知道,实现 Spring AOP 大体会分如下几步:
- 找到 Pointcut 所匹配的所有 join point 对应的类
- 为Pointcut 匹配到的类生成动态代理
- 通过动态代理类执行 Pointcut 对应的 Advice
- 将 Spring AOP 与 Spring IoC 进行结合
现在我们就针对第一步来进行分析,看 Pointcut 是如何匹配到 join point 对应的类的。
版本约定
Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)
正文
Pointcut 由 ClassFilter 和 MethodMatcher 组成。多个 Pointcut 可以组合起来构成 ComposablePointcut
。
public interface Pointcut
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
可以看出,Pointcut 匹配类有两个条件:类过滤器 + 方法匹配器。通过这两个条件来筛选匹配的类。
Spring 对 AOP 的抽象
Spring 对 AOP 抽象出了几个关键的类,也叫 AOP 的基础设施类:
Pointcut、Advice、Advisor、AopInfrastructureBean
Pointcut:
切点。通过 ClassFilter
和 MethodMatcher
匹配所有的 join point。
Advice:
它是一个标记型接口,没有任何方法,用于标记 Advice 类。
Advisor:
持有 Advice 的基础接口。Advisor 是一个只包含有一个 Advice 对象的切面。
AopInfrastructureBean:
标记型接口,用于表示这个 bean 是 Spring AOP 基础设施的一部分。被标记的 bean 即使被 pointcut 匹配到,也不会被代理。
Pointcut 的类图
可以看出,Spring 支持 aspectj 表达式匹配,也支持正则表达式匹配 和 按方法名匹配。
Pointcut 如何匹配 join point
这里我们主要研究一下 aspectj 表达式的匹配问题,所以,重点就是 AspectJExpressionPointcut
这个类。
它是通过 AspectJExpressionPointcut#matches(Method, Class<?>, boolean hasIntroductions)
方法来完成匹配的:
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions)
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
if (shadowMatch.alwaysMatches())
return true;
else if (shadowMatch.neverMatches())
return false;
else
// the maybe case
if (hasIntroductions)
return true;
// A match test returned maybe - if there are any subtype sensitive variables
// involved in the test (this, target, at_this, at_target, at_annotation) then
// we say this is not a match as in Spring there will never be a different
// runtime subtype.
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
Tips:
aspectj 表达式是可以支持 and、or、not 的,最终在 parse 的时候会将其转化为 &&、||、!:
// AspectJExpressionPointcut#replaceBooleanOperators()
private String replaceBooleanOperators(String pcExpr)
String result = StringUtils.replace(pcExpr, " and ", " && ");
result = StringUtils.replace(result, " or ", " || ");
result = StringUtils.replace(result, " not ", " ! ");
return result;
AspectJ expression 匹配测试
public class FooService
public void doBiz()
public String m1()
return null;
public String m2(int flag)
return null;
public class ExpressionMatchTest
public static void main(String[] args)
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
// pc.setExpression("execution(* com.kvn.aop.expression.FooService.*(..))");
// pc.setExpression("execution(void com.kvn.aop.expression.*.*(..))");
pc.setExpression("execution(* com.kvn.aop.expression.*.*(int))");
// 类级别的匹配
boolean rlt = AopUtils.canApply(pc, FooService.class);
System.out.println(pc.getExpression() + ", 匹配:" + FooService.class.getName() + ", 结果:" + rlt);
System.out.println("--------------------");
// 方法级别的匹配
Method[] methods = FooService.class.getDeclaredMethods();
for (Method method : methods)
boolean matches = pc.matches(method, method.getDeclaringClass());
System.out.println(pc.getExpression() + ", 匹配:" + method + ", 结果:" + matches);
例子输出:
execution(* com.kvn.aop.expression.*.*(int)), 匹配:com.kvn.aop.expression.FooService, 结果:true
--------------------
execution(* com.kvn.aop.expression.*.*(int)), 匹配:public void com.kvn.aop.expression.FooService.doBiz(), 结果:false
execution(* com.kvn.aop.expression.*.*(int)), 匹配:public java.lang.String com.kvn.aop.expression.FooService.m1(), 结果:false
execution(* com.kvn.aop.expression.*.*(int)), 匹配:public java.lang.String com.kvn.aop.expression.FooService.m2(int), 结果:true
Spring AOP 支持的 AspectJ 原语类型
Spring AOP 并不是支持所有的 AspectJ 语法,只是对部分语法进行了支持。
Spring AOP 支持的 AspectJ 语法有: execution、args、reference pointcut、this、target、within、@annotation、@within、@args、@target
具体可以看 AspectJExpressionPointcut
的源码:
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
static
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
......
aspectj 所有的原语:
public final class PointcutPrimitive extends TypeSafeEnum
public static final PointcutPrimitive CALL = new PointcutPrimitive("call",1);
public static final PointcutPrimitive EXECUTION = new PointcutPrimitive("execution",2);
public static final PointcutPrimitive GET = new PointcutPrimitive("get",3);
public static final PointcutPrimitive SET = new PointcutPrimitive("set",4);
public static final PointcutPrimitive INITIALIZATION = new PointcutPrimitive("initialization",5);
public static final PointcutPrimitive PRE_INITIALIZATION = new PointcutPrimitive("preinitialization",6);
public static final PointcutPrimitive STATIC_INITIALIZATION = new PointcutPrimitive("staticinitialization",7);
public static final PointcutPrimitive HANDLER = new PointcutPrimitive("handler",8);
public static final PointcutPrimitive ADVICE_EXECUTION = new PointcutPrimitive("adviceexecution",9);
public static final PointcutPrimitive WITHIN = new PointcutPrimitive("within",10);
public static final PointcutPrimitive WITHIN_CODE = new PointcutPrimitive("withincode",11);
public static final PointcutPrimitive CFLOW = new PointcutPrimitive("cflow",12);
public static final PointcutPrimitive CFLOW_BELOW = new PointcutPrimitive("cflowbelow",13);
public static final PointcutPrimitive IF = new PointcutPrimitive("if",14);
public static final PointcutPrimitive THIS = new PointcutPrimitive("this",15);
public static final PointcutPrimitive TARGET = new PointcutPrimitive("target",16);
public static final PointcutPrimitive ARGS = new PointcutPrimitive("args",17);
public static final PointcutPrimitive REFERENCE = new PointcutPrimitive("reference pointcut",18);
public static final PointcutPrimitive AT_ANNOTATION = new PointcutPrimitive("@annotation",19);
public static final PointcutPrimitive AT_THIS = new PointcutPrimitive("@this",20);
public static final PointcutPrimitive AT_TARGET = new PointcutPrimitive("@target",21);
public static final PointcutPrimitive AT_ARGS = new PointcutPrimitive("@args",22);
public static final PointcutPrimitive AT_WITHIN = new PointcutPrimitive("@within",23);
public static final PointcutPrimitive AT_WITHINCODE = new PointcutPrimitive("@withincode",24);
private PointcutPrimitive(String name, int key)
super(name, key);
小结
Spring AOP 抽象出了 Pointcut、Advice、Advisor、AopInfrastructureBean 几个基础设施类。
aspectj 表达式匹配是通过 AspectJExpressionPointcut
来进行支持的。
Spring AOP 只对 aspectj 原语中的部分语法进行了支持!
如果本文对你有所帮助,欢迎点赞收藏!
有关 Spring 源码方面的问题欢迎一起交流,备注:51cto (vx: Kevin-Wang001)
博主好课推荐:
课程 | 地址 |
---|---|
Dubbo源码解读——通向高手之路 | https://edu.51cto.com/course/23382.html |
正则表达式基础与提升 | https://edu.51cto.com/course/16391.html |
以上是关于#yyds干货盘点# 老王读Spring AOP-1Pointcut如何匹配到 join point的主要内容,如果未能解决你的问题,请参考以下文章
#yyds干货盘点# 老王读Spring AOP-4Spring AOP 与Spring IoC 结合的过程 && ProxyFactory 解析
#yyds干货盘点# 老王读Spring AOP-1Pointcut如何匹配到 join point