#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point

学向勤中得,萤窗万卷书。这篇文章主要讲述#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point相关的知识,希望能为你提供帮助。
@[TOC](Pointcut如何匹配到 join point)
前言通过前面的介绍,我们知道,实现 Spring AOP 大体会分如下几步:

  1. 找到 Pointcut 所匹配的所有 join point 对应的类
  2. 为Pointcut 匹配到的类生成动态代理
  3. 通过动态代理类执行 Pointcut 对应的 Advice
  4. 将 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:
切点。通过 ClassFilterMethodMatcher 匹配所有的 join point。
Advice:
它是一个标记型接口,没有任何方法,用于标记 Advice 类。
Advisor:
持有 Advice 的基础接口。Advisor 是一个只包含有一个 Advice 对象的切面。
【#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point】AopInfrastructureBean:
标记型接口,用于表示这个 bean 是 Spring AOP 基础设施的一部分。被标记的 bean 即使被 pointcut 匹配到,也不会被代理。
Pointcut 的类图
#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point

文章图片

可以看出,Spring 支持 aspectj 表达式匹配,也支持正则表达式匹配 和 按方法名匹配。
Pointcut 如何匹配 join point这里我们主要研究一下 aspectj 表达式的匹配问题,所以,重点就是 AspectJExpressionPointcut 这个类。
它是通过 AspectJExpressionPointcut#matches(Method, Class& lt; ?& gt; , 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

    推荐阅读