bigywater.tech
Spring · 20

切点表达式:execution、within、@annotation 的匹配范围

切点表达式 execution、within、@annotation、target、args、bean 到底各自匹配什么?protected 和包可见的方法能不能被拦?不同写法每次调用的开销一样吗?这篇在 Spring Framework 7.0.9 上逐项验证。

10 个
同时生效的 @Before 通知,各带一个不同写法的切点
约 80 对 200 ns
只有 execution 与再加 args / @annotation / target 的切点,每次调用的量级耗时
0 个代理
args(String) 配 m(long) 这种静态就不可能匹配的切点,没有创建代理

哪些写法命中什么

实测 · 本机执行输出
  orderService.place("1")            → 命中 [execution(* OrderService.place(..)), execution(* OrderService.*(..))→place, target(Api)→place]
  orderService.cancel("1")(@Audited) → 命中 [execution(* OrderService.*(..))→cancel, @annotation(Audited)→cancel, target(Api)→cancel]
  orderService.ping()                → 命中 [execution(* OrderService.*(..))→ping, target(Api)→ping]
  orderService.callInternal()(内部调 place) → 命中 [execution(* OrderService.*(..))→callInternal, target(Api)→callInternal]
  payService.pay(5)                  → 命中 [within(PayService)→pay, @within(Marked)→pay, execution(public * *(..)) && within(PayService)→pay]
  payService.refund(5)               → 命中 [execution(* *Service.re*(..))→PayService.refund, within(PayService)→refund, @within(Marked)→refund, execution(public * *(..)) && within(PayService)→refund]
  userService.find(7L)               → 命中 [execution(find(long)) && args(id) 拿到 id=7, bean(userService)→find]
  orderService.pkgPrivate()(包可见)     → 命中 [execution(* OrderService.*(..))→pkgPrivate, target(Api)→pkgPrivate]
  orderService.prot()(protected)     → 命中 [execution(* OrderService.*(..))→prot, target(Api)→prot]

execution 按签名,within 与 @within 按类,@annotation 按方法注解,target、args、bean 按运行时对象、参数和 Bean 名。

运行时判断的代价

实测 · 本机执行输出
  没有切面:每次调用 2、1、1 ns;通知执行次数 0;不是代理
  execution(...):每次调用 80、197、80 ns;通知执行次数 5000000;是代理
  execution(...) && args(id):每次调用 192、210、189 ns;通知执行次数 5000000;是代理
  @annotation(Audited):每次调用 215、250、149 ns;通知执行次数 5000000;是代理
  target(Api):每次调用 221、228、182 ns;通知执行次数 5000000;是代理
  execution(...) && args(String)(m 的参数是 long,静态就不可能匹配):每次调用 0、1、1 ns;通知执行次数 0;不是代理

带 args、@annotation、target 的切点每次调用要多判断一次,比单纯 execution 贵一倍左右。

分步演示

切点表达式
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + AspectJ 注解,proxyTargetClass = true;耗时是简单循环计时(没有用 JMH),三次运行的差别很大(例如 execution 一栏 80、197、80 ns),只能当量级参考;“包可见和 protected 方法命中”是在 CGLIB 代理、调用方与目标在同一个包的条件下测的
    • 没有验证:JDK 代理下 protected / 包可见方法;this 与 target 的区别;@args、@target 切点;reference pointcut(@Pointcut 复用);AspectJ 加载时织入;切点解析本身(parse)的耗时;多个切面同时匹配时的开销叠加
    京ICP备2026067502号-1京公网安备11010502063487号