通俗易懂!2025 年最新 Spring 切入点表达式(Pointcut)全攻略
背完这张表 + 口诀,面试手写切面随便秒,大厂随便进!
一张图记住所有语法(面试30秒画完满分版)
execution( 修饰符 返回值类型 包名.类名.方法名(参数) throws异常 )
execution( public * com.example.service..*.*(..) )
↑ ↑ ↑ ↑ ↑ ↑ ↑
可省 可* 可* 关键.. 可* 可* 任意参数
2025 年真实项目最常用的 10 种写法(直接抄就行)
| 排名 | 写法 | 意思(大白话) | 真实使用场景 |
|---|---|---|---|
| 1 | execution(* com.example.controller..*.*(..)) | 所有 controller 包下的所有类的所有方法 | 统一日志、统一异常处理 |
| 2 | execution(* com.example.service..*Service.*(..)) | 所有 Service 包下以 Service 结尾的所有方法 | 事务、缓存、分布式锁 |
| 3 | execution(* com.example.service.UserService.*(..)) | 只有 UserService 的所有方法 | 精确控制某个核心服务 |
| 4 | execution(* com.example.controller.*Controller.save*(..)) | 所有 Controller 里以 save 开头的方法 | 防重复提交 |
| 5 | execution(* com.example.service..*(..) throws BusinessException) | 所有 Service 方法抛业务异常的 | 统一捕获业务异常 |
| 6 | @annotation(org.springframework.transaction.annotation.Transactional) | 加了 @Transactional 注解的方法 | 二次增强事务(比如打印事务耗时) |
| 7 | @annotation(com.example.annotation.NoRepeatSubmit) | 加了自定义 @NoRepeatSubmit 注解的方法 | 防重点击 |
| 8 | within(com.example.controller..*) | controller 包下的所有类(包括子包) | 和 execution 差不多,稍微粗糙一点 |
| 9 | execution(* com.example.service..*.*(..)) && @annotation(org.springframework.web.bind.annotation.GetMapping) | Service 里加了 @GetMapping 的方法(几乎不用) | 基本没人这么写 |
| 10 | execution(* com..*.*(..)) && args(String,..) | 第一个参数是 String 的所有方法 | 参数匹配(很少用) |
常用通配符 + 符号全解(背3分钟,终身不忘)
| 通配符 | 意思 | 例子 |
|---|---|---|
* | 匹配任意字符 | *Service → UserService、OrderService |
.. | 匹配任意层级包 | com..service → com.service / com.a.b.service |
.. | 匹配任意参数 | *(..) → 0个、1个、多个参数都行 |
+ | 匹配子类 | com.example.service.BaseService+ |
&& | 并且 | execution + annotation |
|| | 或者 | execution A 或 execution B |
! | 取反 | !execution(* com.example.service.Admin*.*(..)) |
2025 大厂最爱用的组合写法(直接抄进项目)
@Aspect
@Component
public class AllInOneAspect {
// 1. 所有 Controller(最常用)
@Pointcut("execution(* com.example.controller..*.*(..))")
private void controller() {}
// 2. 所有 Service(事务、缓存、锁)
@Pointcut("execution(* com.example.service..*Service.*(..))")
private void service() {}
// 3. 加了自定义注解 @NoRepeatSubmit 的方法
@Pointcut("@annotation(com.example.annotation.NoRepeatSubmit)")
private void noRepeat() {}
// 4. 加了 @Cacheable 的方法(用于打印缓存命中率)
@Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")
private void cacheable() {}
// 组合用法:所有 Controller + 加了 @NoRepeatSubmit 的方法
@Before("controller() || noRepeat()")
public void doBefore(JoinPoint jp) {
log.info("请求来了:{}", jp.getSignature());
}
}
面试终极 6 连问 + 标准答案(背完无敌)
..和*区别是什么?
答:..匹配任意层级包或任意参数,*只匹配一层或一个名字。- 怎么切“所有加了 @Transactional 的方法”?
答:@annotation(org.springframework.transaction.annotation.Transactional) - 怎么切“所有以 save 开头的方法”?
答:execution(* *(..).save*(..))或execution(* com..*.save*(..)) - within 和 execution 区别?
答:within 是按“类”匹配,execution 是按“方法”精确匹配,execution 更精准。 - 想切“所有有 String 参数的方法”怎么写?
答:execution(* *(*,String,..))或args(String,..) - 怎么排除某个包不被切?
答:execution(* com.example..*.*(..)) && !execution(* com.example.controller.admin..*.*(..))
终极记忆口诀(10秒背完所有语法)
“execution 包..类.方法(参数)
星号通配一层,点点通配全部
@annotation 切注解最牛
&& 或 || ! 组合随便揉!”
背完这张表 + 10个常用写法 + 口诀,
面试官让你“手写一个切所有 Controller 的 Pointcut”,
你10秒写完 + 顺便甩出 @annotation 高级玩法,
他当场就说:“这小伙子切点玩得飞起!”
现在你已经可以轻松手撕任何横切需求了(日志、事务、权限、防重、限流、缓存)一把梭!冲!