学向勤中得,萤窗万卷书。这篇文章主要讲述#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point相关的知识,希望能为你提供帮助。
@[TOC](Pointcut如何匹配到 join point)
前言通过前面的介绍,我们知道,实现 Spring AOP 大体会分如下几步:
- 找到 Pointcut 所匹配的所有 join point 对应的类
- 为Pointcut 匹配到的类生成动态代理
- 通过动态代理类执行 Pointcut 对应的 Advice
- 将 Spring AOP 与 Spring IoC 进行结合
版本约定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 对象的切面。
【#yyds干货盘点#老王读Spring AOP-1Pointcut如何匹配到 join point】AopInfrastructureBean:
标记型接口,用于表示这个 bean 是 Spring AOP 基础设施的一部分。被标记的 bean 即使被 pointcut 匹配到,也不会被代理。
Pointcut 的类图
文章图片
可以看出,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 |
推荐阅读
- OPPO大数据离线任务调度系统OFLOW
- #星光计划2.0#HarmonyOS开发,从listContainer谈容器类控件的使用
- Java编程后端开发小书架!毛遂自荐
- #yyds干货盘点# linux iscsi 简单实现windows文件互通和实现多路径访问,并实现负载均衡高可用超详细配置方法和原理的个人理解
- Bitlocker磁盘加密策略Without TPM---Intune终结点管理
- JVM调优指南-工具篇(jps)
- Win10 LTSC封装教程
- 学习Java必备的基础知识打卡12.23,要想学好必须扎实基本功(?建议收藏)#yyds干货盘点#
- # yyds干货盘点 # 手把手教你如何新建scrapy爬虫框架的第一个项目(上)