SpringBoot 实现自定义注解
在 SpringBoot 中,自定义注解是非常实用的技术,常用于:日志记录、权限校验、接口限流、参数校验、事务增强、统一返回处理等。
核心原理:注解 + AOP(切面)实现(Spring AOP 基于动态代理)。
下面带你从零实现一个自定义日志注解(最通用、最易理解的案例)。
一、开发步骤
1. 引入依赖
需要spring-boot-starter-aop支持注解和切面:
<!-- 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,用于标记需要记录日志的方法:
importjava.lang.annotation.*;/** * 自定义日志注解 * Target: 注解作用目标(METHOD=方法) * Retention: 注解生命周期(RUNTIME=运行时有效) */@Target(ElementType.METHOD)// 只作用在方法上@Retention(RetentionPolicy.RUNTIME)// 运行时生效@Documented// 生成文档public@interfaceLogRecord{// 注解属性:操作描述(默认空)StringoperateDesc()default"";}3. 编写 AOP 切面(注解核心逻辑)
这是真正实现注解功能的地方,拦截加了@LogRecord的方法:
importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.JoinPoint;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.*;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.stereotype.Component;importjava.lang.reflect.Method;@Aspect// 标记为切面类@Component// 交给 Spring 管理@Slf4jpublicclassLogRecordAspect{/** * 切点:匹配所有添加了 @LogRecord 注解的方法 */@Pointcut("@annotation(com.example.demo.annotation.LogRecord)")publicvoidlogPointCut(){}/** * 环绕通知:方法执行前后都拦截 */@Around("logPointCut()")publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{longbeginTime=System.currentTimeMillis();// 执行目标方法Objectresult=point.proceed();// 执行耗时longtime=System.currentTimeMillis()-beginTime;// 保存日志recordLog(point,time);returnresult;}/** * 记录日志逻辑 */privatevoidrecordLog(ProceedingJoinPointjoinPoint,longtime){MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();Methodmethod=signature.getMethod();// 获取自定义注解LogRecordlogAnnotation=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:
importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;@RestController@RequestMapping("/test")publicclassTestController{/** * 使用自定义日志注解 */@LogRecord(operateDesc="测试自定义注解接口")@GetMapping("/annotation")publicStringtestAnnotation(){return"自定义注解生效啦!";}}二、测试效果
启动项目,访问接口:http://localhost:8080/test/annotation
控制台输出:
=====================日志开始==================== 操作描述:测试自定义注解接口 执行方法:com.example.demo.controller.TestController.testAnnotation 执行耗时:3ms =====================日志结束====================✅自定义注解实现完成!
三、进阶:自定义注解常用场景扩展
1. 权限校验注解
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceRequirePermission{Stringvalue();// 权限码}切面中:获取当前登录用户权限 → 对比注解权限 → 无权限抛出异常。
2. 接口限流注解
切面中:使用 Redis + Lua 实现限流逻辑。
3. 参数非空校验注解
作用在参数/字段上,切面自动校验参数是否为空。
四、核心知识点总结
注解三要素
@Target:定义注解用在哪里(方法/类/参数)@Retention:定义注解生命周期(必须用RUNTIME)@interface:声明自定义注解
AOP 五大通知
@Around:环绕通知(最常用,前后都拦截)@Before:方法执行前@After:方法执行后@AfterReturning:方法返回后@AfterThrowing:方法异常后
核心原理
Spring AOP 通过动态代理,拦截带有自定义注解的方法,执行增强逻辑。
总结
- 实现自定义注解 =定义注解 + AOP切面 + 使用注解
- 必须引入
spring-boot-starter-aop依赖 - 注解本身无逻辑,逻辑全在 AOP 切面中
- 可用于日志、权限、限流、校验等场景
