Spring系列11:切点表达式

切点是切面的「瞄准镜」

@Pointcut("execution(* com.example.service..*.*(..))"),是整个切面的选点逻辑------表达式写宽了误伤无辜方法,写窄了漏拦。这一篇把瞄准镜校准。

一、@Pointcut 签名约定(30 秒复习)

java 复制代码
@Pointcut("execution(* transfer(..))")   // 切点表达式
private void anyOldTransfer() {}          // 切点签名

官方约定:签名方法必须 void 返回、空方法体 ------它不执行任何逻辑,只是给表达式起名字,供多个通知按名引用(@Before("anyOldTransfer()"))。可见性遵循 Java 规则但不影响匹配:private 切点在同一个切面里用,public 切点可以跨切面共享。

二、execution 六段式解剖(高频 90% 的场景)

text 复制代码
execution(modifiers-pattern? ret-type-pattern
          declaring-type-pattern?name-pattern(param-pattern)
          throws-pattern?)
段 是否必填 说明
modifiers 可省 public / protected 等
ret-type 必填 返回类型,* 通配任意
declaring-type 可省 声明方法的类型(含包名)
name-pattern 必填 方法名,* 通配,set* 匹配前缀
param-pattern 必填 见下方通配符表
throws 可省 异常类型

参数模式是易错点,官方给的就这四种:

  • ()------无参方法
  • (..)------任意个参数(最常用)
  • (*)------恰好一个任意类型参数
  • (*,String)------两个参数,第二个是 String

四个官方示例,覆盖日常 90% 场景:

java 复制代码
execution(public * *(..))                        // 任何 public 方法
execution(* set*(..))                            // 所有 set 开头的方法
execution(* com.xyz.service.AccountService.*(..)) // AccountService 接口上的所有方法
execution(* com.xyz.service..*.*(..))            // service 包及子包的所有方法

包名里的 .. 是「含子包」,参数里的 .. 是「任意参数」------两个点号含义不同,别混。

三、全部 10 个切点指示符(PCD)快览

execution 之外还有 9 个,按用途分三组记:

① 按类型圈范围

  • within(com.xyz.service..*):该类型内声明的所有连接点(Spring AOP 即方法执行)
  • this(AccountService):代理对象是该类型的实例------匹配的是「代理实现/继承了什么」
  • target(AccountService):目标对象是该类型的实例------匹配的是「被代理的真实对象」

② 按注解圈范围(实战主力)

  • @annotation(org.springframework.transaction.annotation.Transactional):方法上标了该注解------自定义切面最常用
  • @within(SomeAnnotation):声明类型上标了该注解(类级)
  • @target(SomeAnnotation):目标对象的运行时类标了该注解
  • @args(Classified):传入实参的运行时类型标了该注解

③ 按参数与 Spring 扩展

  • args(java.io.Serializable):单个运行时实参是 Serializable 实例
  • bean(tradeService) / bean(*Service):按 Spring bean 名字 匹配(支持 * 通配)------这是 Spring AOP 特有扩展,AspectJ 里没有

this vs target 是面试常客 :this 看代理(代理实现了哪个接口/继承了哪个类),target 看目标(真实对象是谁)。JDK 动态代理下,代理只实现接口、并不是目标类的子类------所以 this(SomeServiceImpl) 在 JDK 代理下不命中,target(SomeServiceImpl) 命中。弄清代理机制后再回头看会更透。

四、组合与复用:&&、||、! 和命名切点

java 复制代码
public class Pointcuts {
    @Pointcut("execution(public * *(..))")
    public void publicMethod() {}          // 任何 public 方法

    @Pointcut("within(com.xyz.trading..*)")
    public void inTrading() {}             // trading 模块内

    @Pointcut("publicMethod() && inTrading()")
    public void tradingOperation() {}      // 组合:trading 模块内的 public 方法
}

三个逻辑运算符 &&(与)、||(或)、!(非)自由组合,切点可以按名字互相引用------工程实践:把通用切点集中到一个切点类里,通知按名引用,改切点不用翻遍所有通知。

看图就懂:execution 解剖 + PCD 家族全景

一个官方级避坑:声明类型 vs 运行时类型

execution(* *(java.io.Serializable)) 和 args(java.io.Serializable) 长得像,语义完全不同 ------前者按方法签名里声明的参数类型 匹配,后者按调用时传入实参的运行时类型匹配。传一个 String 给声明为 Object 的参数,前者不命中、后者命中(String 实现了 Serializable)。排查「切点怎么没拦住」时,先分清你写的是哪种。

一句话收束:execution 定主战场,@annotation 定业务开关,within/bean 定模块边界,this/target 分代理与目标------四把尺子量清楚,切点就不失控。


📌 版本与出处说明 :本文基于 Spring Framework 7.x 官方 Reference (查阅时文档版本 7.0.10,2026-10)撰写。全部切点指示符与示例对照 Core → AOP → @AspectJ Support → Declaring a Pointcut 一章,语法与措辞均出自该章。

相关推荐
30955666628 分钟前
股票接口返回 404 与空数组的排查笔记:证书状态与级别参数
java·笔记·python·ios·iphone·股票数据api·股票数据接口
nice_lcj52031 分钟前
MySQL 存储引擎入门:InnoDB 和 MyISAM 到底怎么选
java·数据库·mysql·面试·innodb·存储引擎·myisam
文丑颜不良啊1 小时前
LeetCode2861~2880题目解析
java·leetcode
装不满的克莱因瓶1 小时前
设计模式之策略模式核心概念和应用场景
java·后端·设计模式·架构·系统架构·策略模式
晴天的雨.9921 小时前
【C++】继承篇
java·开发语言·c++
SomeOtherTime1 小时前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
qq_406981031 小时前
c++ lamda(三)Lambda表达式
java·前端·c++
头发还在的女程序员1 小时前
Java在线租房系统源码解析:Spring Boot+Vue 3+UniApp房源管理与预约看房方案
java·vue.js·spring boot·租房小程序·租房系统·租房软件
FYKJ_20101 小时前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计