自定义注解实战:用 AOP 让代码“会说话”

简介: 本文详解如何在Spring中通过自定义注解+AOP实现方法日志记录,剖析注解原理与AOP拦截机制,并拓展至权限、缓存、校验等实用场景,提升代码可维护性与优雅度。

在 Spring 项目中,我们经常看到 @Transactional、@Cacheable、@PreAuthorize 等注解——它们简洁优雅,却能自动完成事务、缓存、权限校验等复杂逻辑。

这些“魔法”的背后,正是 自定义注解 + AOP(面向切面编程) 的组合。

本文将手把手带你实现一个用于方法日志记录的自定义注解,并深入理解其原理与扩展可能。


一、什么是自定义注解?

Java 注解(Annotation)本质上是一种元数据,它不直接参与程序逻辑,但可以被编译器、运行时或框架读取并执行相应行为。

要让注解“活”起来,关键在于:

  • 正确使用元注解(如 @Target、@Retention);
  • 结合 AOP 或反射机制 在运行时拦截并处理。

二、实战:实现一个日志注解

1. 定义基础实体与服务(略)

假设已有:

  • User 实体类
  • UserDAO 数据访问层
  • UserService 业务层
  • UserController 控制器
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/user/{id}")
    public User findUser(@PathVariable Integer id) {
        return userService.findUserById(id);
    }
}

现在,我们希望在调用 findUser 时自动打印日志,但不想写重复的 log.info(...)。


2. 创建自定义注解

import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,可通过反射获取
@Target(ElementType.METHOD)          // 仅用于方法
public @interface CustomAnnotation {
    String name() default "";        // 注解属性1
    String value() default "";       // 注解属性2(习惯命名为 value,可简写)
}

关键元注解说明:

元注解 作用
@Documented 生成 Javadoc 时包含该注解
@Retention(RUNTIME) 必须为 RUNTIME,否则 AOP 无法通过反射读取
@Target(METHOD) 限定只能用在方法上

💡 注解中的方法 = 属性。使用时:@CustomAnnotation(name = "xxx", value = "yyy")

若只有 value 属性,可简写为:@CustomAnnotation("yyy")


3. 使用注解标记方法

@CustomAnnotation(name = "findUser", value = "根据ID查找用户")
@GetMapping("/user/{id}")
public User findUser(@PathVariable Integer id) {
    return userService.findUserById(id);
}

此时注解只是“贴标签”,还没任何行为。


4. 用 AOP 拦截注解并执行逻辑

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LogAspect {
    // 定义切入点:所有被 @CustomAnnotation 标记的方法
    @Pointcut("@annotation(cn.example.demo.CustomAnnotation)")
    public void annotatedMethod() {}
    // 在方法执行前拦截
    @Before("annotatedMethod() && @annotation(annotation)")
    public void logBefore(JoinPoint joinPoint, CustomAnnotation annotation) {
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("=== 日志拦截 ===");
        System.out.println("类名: " + className);
        System.out.println("方法: " + methodName);
        System.out.println("功能: " + annotation.value());
        System.out.println("标识: " + annotation.name());
    }
}

✅ 注意:  

  • 切点表达式 @annotation(...) 必须写全注解的完整类名;  
  • 方法参数中 CustomAnnotation annotation 会自动注入注解实例。

5. 启动项目,测试效果

访问:http://localhost:8080/user/1

控制台输出:

=== 日志拦截 ===
类名: cn.example.demo.UserController
方法: findUser
功能: 根据ID查找用户
标识: findUser

✅ 成功!无需修改业务代码,日志自动增强。


三、自定义注解还能做什么?

上述模式可轻松扩展至多种场景:

场景 实现思路
参数校验 如 @Phone、@IdCard,在 AOP 中验证参数合法性
权限控制 如 @RequireRole("ADMIN"),拦截无权限请求
缓存操作 如 @CachePut(key = "#id"),自动写入 Redis
操作审计 记录谁在何时做了什么操作
限流熔断 结合 Sentinel 或自定义计数器

所有这些,底层逻辑都一样:定义注解 → AOP 拦截 → 执行增强逻辑。


四、注意事项

  1. @Retention 必须是 RUNTIME,否则反射拿不到;
  2. Spring AOP 默认只对 Spring Bean 生效,确保被注解的类由 Spring 管理;
  3. 注解不能继承(@Inherited 仅对类注解有效,对方法无效);
  4. 性能敏感场景慎用:AOP 本质是代理,有轻微开销。

五、总结

自定义注解不是“语法糖”,而是解耦业务与横切关注点的强大工具。

通过 “注解 + AOP” 模式,你可以:

  • 让代码更声明式(Declarative);
  • 避免重复样板代码;
  • 提升系统可维护性与扩展性。

下次当你想“在某些方法前后统一做点事”时,不妨试试自定义注解——

让代码自己描述意图,而不是堆满 if-else 和 log。


相关文章
|
监控 Cloud Native 安全
《服务治理》全景指南:构建可靠的分布式系统
服务治理是分布式系统的“交通管制中心”,通过注册发现、配置管理、流量控制等机制,确保服务稳定高效协作。涵盖核心组件、关键能力及Service Mesh等前沿趋势,助力构建可靠云原生架构。(239字)
|
10月前
|
数据采集 Java 数据库连接
Spring Batch实战全解析:从入门到精通,搞定企业级批处理难题
本文全面介绍了SpringBatch框架在企业级批处理应用中的核心技术与实战方案。文章首先阐述了批处理的典型特征(无交互性、海量数据、可靠性等)和SpringBatch的核心优势(轻量化、可扩展、事务安全等),并通过对比其他批处理方案突出其适用性。随后详细解析了SpringBatch的核心架构,包括JobLauncher、Job、Step等组件的职责分工,以及批处理执行流程。
884 1
|
人工智能 自然语言处理 算法
基于DeepSeek的具身智能高校实训解决方案——从DeepSeek+机器人到通用具身智能
本实训方案围绕「多模态输入 -> 感知与理解 -> 行动执行 -> 反馈学习」的闭环过程展开。通过多模态数据的融合(包括听觉、视觉、触觉等),并结合DeepSeek模型和深度学习算法,方案实现了对自然语言指令的理解、物体识别和抓取、路径规划以及任务执行的完整流程。
2155 12
|
存储 NoSQL 关系型数据库
十年大厂员工终明白:MySQL性能优化的尽头,是对B+树的极致理解
存储引擎是数据库的核心组件,负责数据的存储与管理。常见存储引擎如MySQL的InnoDB采用B+树结构,以优化读取性能,支持高效查询、范围检索和有序遍历。相比哈希表和B树,B+树通过减少I/O次数,提升大规模数据下的查询效率。本文深入解析B+树的原理、优势及其在MySQL中的应用。
566 0
|
10月前
|
安全 Java 编译器
我们来说一下 synchronized 与 ReentrantLock 的区别
我是小假 期待与你的下一次相遇 ~
432 4
|
9月前
|
弹性计算 缓存 小程序
阿里云服务器包年包月一口价:价格透明、告别隐形消费及成本更低的计费模式
阿里云ECS包年包月为预付费模式,价格透明、一口价无隐形消费,长期使用享低至2.3折优惠,成本比按量付费节省超60%,适合稳定业务场景,推荐个人开发者及企业选用。
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
7519 117
|
SQL 关系型数据库 MySQL
mybatisPlus之自动填充功能及防全表更新与删除插件
mybatisPlus之自动填充功能及防全表更新与删除插件
|
10月前
|
Java 大数据 Apache
Excel工具-HUTOOL-输出Excel
基于Hutool与Apache POI,封装Excel写入功能,提供ExcelWriter和BigExcelWriter,支持List、Map、Bean等数据类型导出,可自定义样式、多Sheet操作,并避免内存溢出,适用于高效生成Excel文件及Web下载场景。
516 0
|
人工智能 搜索推荐 自动驾驶
如何抓住本世纪伟大成就AI的风口脱颖而出?AI到底会带来什么影响?AI对程序员的影响?AI对软件行业的影响?——2025年如何抓住AI的机会-成为AI工程师-程序员可成为高级AI工程师
如何抓住本世纪伟大成就AI的风口脱颖而出?AI到底会带来什么影响?AI对程序员的影响?AI对软件行业的影响?——2025年如何抓住AI的机会-成为AI工程师-程序员可成为高级AI工程师
1121 55

热门文章

最新文章