目录
- [一、什么是 @annotation](#一、什么是 @annotation)
- 二、写法一:只拦截方法,不读取注解内容
- [三、写法二:拦截 + 获取注解对象](#三、写法二:拦截 + 获取注解对象)
- 四、两种写法的对比总结
- 五、完整运行示例
- 六、总结
在 Spring AOP 的开发中,我们经常看到这样的切面写法:
java
@Before("@annotation(com.example.annotation.PayLog)")
public void beforeAnnotation() {
System.out.println("通过 @annotation 拦截带 @PayLog 注解的方法");
}
或者这样:
java
@Before("@annotation(payLog)")
public void beforeAnnotation(PayLog payLog) {
System.out.println("检测到支付操作:" + payLog.value());
}
看起来很相似,但又不太一样 🤔
到底有什么区别?为什么都能生效?
这篇文章帮你彻底搞清楚!
一、什么是 @annotation
在 AOP(Aspect-Oriented Programming,面向切面编程)中,
我们通过切点表达式定义要拦截的方法。
其中,@annotation(...)
是一种非常常见的表达式,用来匹配:
"所有带有指定注解的方法"。
举个例子
假设我们定义了一个自定义注解 @PayLog
:
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayLog {
String value() default "默认支付";
}
二、写法一:只拦截方法,不读取注解内容
java
@Aspect
@Component
public class PayAspect {
@Before("@annotation(com.example.annotation.PayLog)")
public void beforeAnnotation() {
System.out.println("通过 @annotation 拦截带 @PayLog 注解的方法");
}
}
这段切面表示:
"在执行任何带有
@PayLog
注解的方法之前,先执行我这个方法。"
示例:
java
@Service
public class OrderService {
@PayLog
public void pay() {
System.out.println("执行支付逻辑...");
}
}
输出:
通过 @annotation 拦截带 @PayLog 注解的方法
执行支付逻辑...
🧩 解释:
@annotation(com.example.annotation.PayLog)
表示拦截所有被该注解标记的方法;beforeAnnotation()
方法执行时,并不会接收到注解对象;- 这种写法适合只想拦截,不需要读取注解里的内容。
三、写法二:拦截 + 获取注解对象
java
@Aspect
@Component
public class PayAspect {
@Before("@annotation(payLog)")
public void beforeAnnotation(PayLog payLog) {
System.out.println("检测到支付操作:" + payLog.value());
}
}
这段代码看起来只多了一个参数 payLog
,
其实功能更强大------它可以直接获取目标方法上的注解实例。
示例
java
@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、自定义注解
java
package com.example.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayLog {
String value() default "默认支付类型";
}
2、AOP 切面类
java
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、被拦截的业务类
java
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 |
想统一记录操作日志 | 推荐带参数写法,灵活性更强 |