Spring AOP 中@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
想统一记录操作日志 推荐带参数写法,灵活性更强

相关推荐
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane199413 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
噢,我明白了14 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品14 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘14 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记14 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端