SpringBoot 实现自定义注解

在 SpringBoot 中,自定义注解 是非常实用的技术,常用于:日志记录、权限校验、接口限流、参数校验、事务增强、统一返回处理等。

核心原理:注解 + AOP(切面) 实现(Spring AOP 基于动态代理)。

下面带你从零实现一个 自定义日志注解(最通用、最易理解的案例)。


一、开发步骤

1. 引入依赖

需要 spring-boot-starter-aop 支持注解和切面:

xml 复制代码
<!-- Spring AOP 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!-- web依赖(用于测试接口) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. 自定义注解类

创建注解 @LogRecord,用于标记需要记录日志的方法:

java 复制代码
import java.lang.annotation.*;

/**
 * 自定义日志注解
 * Target: 注解作用目标(METHOD=方法)
 * Retention: 注解生命周期(RUNTIME=运行时有效)
 */
@Target(ElementType.METHOD) // 只作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时生效
@Documented // 生成文档
public @interface LogRecord {
    // 注解属性:操作描述(默认空)
    String operateDesc() default "";
}

3. 编写 AOP 切面(注解核心逻辑)

这是真正实现注解功能 的地方,拦截加了 @LogRecord 的方法:

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Aspect // 标记为切面类
@Component // 交给 Spring 管理
@Slf4j
public class LogRecordAspect {

    /**
     * 切点:匹配所有添加了 @LogRecord 注解的方法
     */
    @Pointcut("@annotation(com.example.demo.annotation.LogRecord)")
    public void logPointCut() {}

    /**
     * 环绕通知:方法执行前后都拦截
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 执行目标方法
        Object result = point.proceed();
        // 执行耗时
        long time = System.currentTimeMillis() - beginTime;

        // 保存日志
        recordLog(point, time);

        return result;
    }

    /**
     * 记录日志逻辑
     */
    private void recordLog(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取自定义注解
        LogRecord logAnnotation = method.getAnnotation(LogRecord.class);
        log.info("=====================日志开始====================");
        // 获取注解上的描述
        log.info("操作描述:{}", logAnnotation.operateDesc());
        // 获取方法名
        log.info("执行方法:{}", signature.getDeclaringTypeName() + "." + signature.getName());
        // 执行耗时
        log.info("执行耗时:{}ms", time);
        log.info("=====================日志结束====================");
    }
}

4. 使用自定义注解

直接在 Controller/Service 方法 上添加 @LogRecord

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    /**
     * 使用自定义日志注解
     */
    @LogRecord(operateDesc = "测试自定义注解接口")
    @GetMapping("/annotation")
    public String testAnnotation() {
        return "自定义注解生效啦!";
    }
}

二、测试效果

启动项目,访问接口:

http://localhost:8080/test/annotation

控制台输出:

复制代码
=====================日志开始====================
操作描述:测试自定义注解接口
执行方法:com.example.demo.controller.TestController.testAnnotation
执行耗时:3ms
=====================日志结束====================

自定义注解实现完成!


三、进阶:自定义注解常用场景扩展

1. 权限校验注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value(); // 权限码
}

切面中:获取当前登录用户权限 → 对比注解权限 → 无权限抛出异常。

2. 接口限流注解

切面中:使用 Redis + Lua 实现限流逻辑。

3. 参数非空校验注解

作用在参数/字段上,切面自动校验参数是否为空。


四、核心知识点总结

  1. 注解三要素

    • @Target:定义注解用在哪里(方法/类/参数)
    • @Retention:定义注解生命周期(必须用 RUNTIME
    • @interface:声明自定义注解
  2. AOP 五大通知

    • @Around:环绕通知(最常用,前后都拦截)
    • @Before:方法执行前
    • @After:方法执行后
    • @AfterReturning:方法返回后
    • @AfterThrowing:方法异常后
  3. 核心原理

    Spring AOP 通过动态代理,拦截带有自定义注解的方法,执行增强逻辑。


总结

  1. 实现自定义注解 = 定义注解 + AOP切面 + 使用注解
  2. 必须引入 spring-boot-starter-aop 依赖
  3. 注解本身无逻辑,逻辑全在 AOP 切面中
  4. 可用于日志、权限、限流、校验等场景
相关推荐
施棠海1 小时前
自定义并可深度定制的数字滚动选择器完整源代码与相关注意事项
java·开发语言
2601_961194021 小时前
2026六级词汇资料电子版|大学英语六级核心词汇PDF
java·spring·eclipse·pdf·tomcat·hibernate
布朗克1681 小时前
18 面向对象综合实战——设计一个图书管理系统
java·面试·职场和发展·面向对象实战
码不停蹄的玄黓2 小时前
旁路缓存(Cache-Aside,CA)
java·开发语言
NGINX开源社区2 小时前
NGINX Ingress Controller 中的 Cache Policy:VirtualServer 实战指南
java·前端·nginx
lld9510272 小时前
(三)本地策略框架
java·服务器·数据库
SoftLipaRZC2 小时前
C语言文件:文件操作完全指南
android·java·c语言
零陵上将军_xdr2 小时前
API 签名防重放机制:基于 HMAC-SHA256 的设计与实现
java·学习·安全架构
ch.ju2 小时前
Java程序设计(第3版)第四章——set-get方法
java·开发语言