适用人群:
- 知道 Spring AOP 是啥
- 写过
@Around / @Before- 但对「注解是怎么传进 Advice 的」有点懵
这篇文章不默认你已经懂,我们从「注解怎么写」开始。
0️⃣ 先准备一个“被切的注解”(否则一切都是空谈)
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
String value() default "";
boolean required() default true;
}
关键点(只记 2 个):
-
@Retention(RUNTIME):没这个,AOP 在运行时根本拿不到注解
-
METHOD / TYPE:决定你后面能不能用
@annotation/@within
1️⃣ 再看一个“被切的方法”(完整上下文)
@Auth("admin")
public void createOrder() {
// business logic
}
现在场景明确了:
- 方法上有
@Auth - AOP 想在运行时 拿到这个注解里的配置
2️⃣ 最容易让人懵的地方:两种“几乎一样”的写法
❌ 写法一:只能“切到”,拿不到注解
@Around("@annotation(Auth)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed();
}
它的真实含义是:
只要方法上“存在”
@Auth,我就切
✔ 能切
❌ 但你拿不到 @Auth 实例
原因很简单:
你只是问了“有没有”, 没说“拿给我”。
✅ 写法二:切 + 绑定 + 传参(正确姿势)
@Around("@annotation(auth)")
public Object around(ProceedingJoinPoint pjp, Auth auth) throws Throwable {
// auth 就是方法上的 @Auth 注解实例
String role = auth.value();
return pjp.proceed();
}
这里发生了三件事:
-
@annotation(auth)- auth 是变量名,不是类型
-
Spring 从目标方法上
- 拿到
@Auth注解实例
- 拿到
-
注入到参数
Auth auth
👉 这一步,叫 参数绑定(binding)
3️⃣ 一个万能记忆口诀(真的有用)
切点里写“类型” → 只判断有没有 切点里写“变量名” → 才能把值传进来
| 写法 | 含义 |
|---|---|
@annotation(Auth) | 存在性判断 |
@annotation(auth) | 绑定注解实例 |
args(id) | 绑定方法参数 |
target(service) | 绑定目标对象 |
4️⃣ 方法注解 + 类注解一起用怎么办?
@Around("@annotation(auth) || @within(auth)")
public Object around(ProceedingJoinPoint pjp, Auth auth) throws Throwable {
return pjp.proceed();
}
含义是:
- 方法上有
@Auth→ 用方法级 - 否则用类上的
@Auth
⚠️ 说明:
- Spring 优先方法级
- 但规则不直观
📌 权限 / 框架场景建议:
手动解析更稳
Method m = ((MethodSignature) pjp.getSignature()).getMethod();
Auth auth = m.getAnnotation(Auth.class);
if(auth ==null){
auth =pjp.
getTarget().
getClass().
getAnnotation(Auth .class);
}
5️⃣ 常见误区(90% 的坑都在这)
❌ 误区 1:Pointcut 参数 = 普通方法参数
实际:它是上下文占位符
❌ 误区 2:Advice 里多写一个参数,Spring 会自动给
实际:没绑定来源,直接启动报错
❌ 误区 3:Auth 和 auth 没区别
实际:
Auth:类型判断auth:变量绑定
6️⃣ 最后一段人话总结
AOP 参数不是“注入”的, 是你在切点里“声明来源”, Spring 再把上下文对象塞给你。
写对了是自动挡, 写错了是离合踩到底还挂不上档。
TL;DR(真·一句话)
切点里不绑定变量名,Advice 里就永远拿不到参数。
——完