Spring AOP 中@annotation的两种写法详解
目录
- 一、什么是 @annotation
- 二、写法一:只拦截方法,不读取注解内容
- 三、写法二:拦截 + 获取注解对象
- 四、两种写法的对比总结
- 五、完整运行示例
- 1、自定义注解
- 2、AOP 切面类
- 3、被拦截的业务类
- 4、输出结果
- 六、总结
在 Spring AOP 的开发中,我们经常看到这样的切面写法:
@Before("@annotation(com.example.annotation.PayLog)")
public void beforeAnnotation() {System.out.println("通过 @annotation 拦截带 @PayLog 注解的方法");
}
或者这样:
@Before("@annotation(payLog)")
public void beforeAnnotation(PayLog payLog) {System.out.println("检测到支付操作:" + payLog.value());
}
看起来很相似,但又不太一样 🤔
到底有什么区别?为什么都能生效?
这篇文章帮你彻底搞清楚!
一、什么是 @annotation
在 AOP(Aspect-Oriented Programming,面向切面编程)中,
我们通过切点表达式定义要拦截的方法。
其中,@annotation(...)
是一种非常常见的表达式,用来匹配:
“所有带有指定注解的方法”。
举个例子
假设我们定义了一个自定义注解 @PayLog
:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayLog {String value() default "默认支付";
}
二、写法一:只拦截方法,不读取注解内容
@Aspect
@Component
public class PayAspect {@Before("@annotation(com.example.annotation.PayLog)")public void beforeAnnotation() {System.out.println("通过 @annotation 拦截带 @PayLog 注解的方法");}
}
这段切面表示:
“在执行任何带有
@PayLog
注解的方法之前,先执行我这个方法。”
示例:
@Service
public class OrderService {@PayLogpublic void pay() {System.out.println("执行支付逻辑...");}
}
输出:
通过 @annotation 拦截带 @PayLog 注解的方法
执行支付逻辑...
🧩 解释:
@annotation(com.example.annotation.PayLog)
表示拦截所有被该注解标记的方法;beforeAnnotation()
方法执行时,并不会接收到注解对象;- 这种写法适合只想拦截,不需要读取注解里的内容。
三、写法二:拦截 + 获取注解对象
@Aspect
@Component
public class PayAspect {@Before("@annotation(payLog)")public void beforeAnnotation(PayLog payLog) {System.out.println("检测到支付操作:" + payLog.value());}
}
这段代码看起来只多了一个参数 payLog
,
其实功能更强大——它可以直接获取目标方法上的注解实例。
示例
@Service
public class OrderService {@PayLog("微信支付")public void payWx() {System.out.println("执行微信支付逻辑...");}@PayLog("支付宝支付")public void payAli() {System.out.println("执行支付宝支付逻辑...");}
}
输出结果:
检测到支付操作:微信支付
执行微信支付逻辑...
检测到支付操作:支付宝支付
执行支付宝支付逻辑...
🧠 解释:
@annotation(payLog)
告诉 AOP:
拦截时把目标方法上的@PayLog
实例赋值给payLog
参数;- 于是我们可以直接通过
payLog.value()
获取注解中的值。
四、两种写法的对比总结
对比项 | 写法一 | 写法二 |
---|---|---|
表达式 | @annotation(com.example.annotation.PayLog) | @annotation(payLog) |
方法参数 | 无 | 有(注解类型参数) |
是否能读取注解内容 | ❌ 否 | ✅ 可以 |
主要用途 | 只做拦截、执行前后逻辑 | 需要读取注解参数(如描述、类型等) |
示例应用 | 简单记录日志 | 根据注解参数执行不同逻辑 |
五、完整运行示例
1、自定义注解
package com.example.annotation;import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayLog {String value() default "默认支付类型";
}
2、AOP 切面类
package com.example.aspect;import com.example.annotation.PayLog;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
@Component
public class PayAspect {// 写法一:只拦截@Before("@annotation(com.example.annotation.PayLog)")public void onlyIntercept() {System.out.println("拦截到带 @PayLog 注解的方法");}// 写法二:拦截 + 获取注解@Before("@annotation(payLog)")public void beforeWithAnnotation(PayLog payLog) {System.out.println("检测到支付操作:" + payLog.value());}
}
3、被拦截的业务类
package com.example.service;import com.example.annotation.PayLog;
import org.springframework.stereotype.Service;@Service
public class OrderService {@PayLog("微信支付")public void payWx() {System.out.println("执行微信支付逻辑...");}@PayLog("支付宝支付")public void payAli() {System.out.println("执行支付宝支付逻辑...");}
}
4、输出结果
拦截到带 @PayLog 注解的方法
检测到支付操作:微信支付
执行微信支付逻辑...拦截到带 @PayLog 注解的方法
检测到支付操作:支付宝支付
执行支付宝支付逻辑...
六、总结
@annotation
有两种写法:
@annotation(注解类路径)
:只拦截,不读参数;@annotation(变量名)
+ 方法参数:拦截并获取注解对象。
两者都对,只是用途不同。
场景 | 推荐写法 |
---|---|
只想拦截注解方法,不关心内容 | @annotation(com.xxx.PayLog) |
需要读取注解参数(如 type、desc) | @annotation(payLog) + PayLog payLog |
想统一记录操作日志 | 推荐带参数写法,灵活性更强 |