Java自定义注解-SpringBoot实战

文章目录

做Java开发这么久,我们每天都在跟注解打交道。

写SpringBoot接口要加@RestController,做配置要加@Configuration,注入对象用@Autowired,简单点说,现在的Java开发,离开注解根本写不动代码

很多小伙伴天天用别人写好的注解,却一直搞不懂自定义注解怎么玩,总觉得这东西高深、难理解,好像只有框架源码里才会用到。

其实完全不是这样。

自定义注解就是咱们程序员的代码小工具 ,核心作用就一个:给类、方法、参数打标签,然后通过程序识别这个标签,自动帮我们干活

今天由浅入深从零讲起,最后带大家在SpringBoot里手写一个真实项目能用的自定义操作日志注解,看完就能直接用到自己项目里。


一、先搞懂:注解到底是个啥?

不用记官方定义,就记住一句大白话:

注解就是贴在代码上的特殊标签,本身不干活,只做标记,等着别人来识别、执行对应的逻辑。

举个日常开发的例子:

你在Controller的方法上加个@GetMapping\(\&\#34;/list\&\#34;\),这个注解本身不会处理任何请求,它就只是一个标记。

真正干活的是SpringMVC框架:启动时扫描所有带这个注解的方法,记录好请求路径和对应方法的关系,前端发起请求后,SpringMVC根据这个标记匹配接口,执行对应的业务代码。

所有注解的底层逻辑都是这三步:

  1. 定义注解:造出来一个专属标签

  2. 使用注解:把标签贴在类、方法、字段上

  3. 解析注解:写代码识别这个标签,执行具体业务逻辑

原生自带的注解是框架帮我们定义和解析好了,自定义注解就是咱们自己动手,造标签、自己写解析逻辑而已。


二、自定义注解核心基础:4个元注解必须懂

想要自己定义注解,不用写复杂逻辑,只需要用@interface关键字就行。

但写自定义注解的时候,必须搭配元注解 使用。元注解就是修饰注解的注解,用来规定咱们自定义的标签能用在哪、能活多久。

一共就4个,日常开发只用记最常用的2个就够,简单易懂不绕弯。

1、@Target:注解能贴在哪?

限定咱们自定义的标签,只能标记在哪些代码位置。

常用取值就这几个:

  • ElementType.METHOD:只能贴在方法上(最常用,比如日志、权限注解)

  • ElementType.TYPE:只能贴在类、接口、枚举上(比如全局配置注解)

  • ElementType.FIELD:只能贴在成员变量上(比如字段校验注解)

  • ElementType.PARAMETER:只能贴在方法参数上

2、@Retention:注解能活多久?

限定注解的生命周期,核心就看这一个:

  • RetentionPolicy.RUNTIME :运行时一直有效,程序跑起来也能通过反射获取到注解信息(自定义业务注解必选

  • 另外两个SOURCE、CLASS基本不用,不用额外记忆

3、@Documented、@Inherited(了解即可)

  • @Documented:生成接口文档时,带上这个注解信息

  • @Inherited:子类能继承父类的注解

日常开发自定义注解,默认加上前两个核心元注解就行,简单不出错。


三、手写第一个简单自定义注解(零基础入门)

先不搞复杂业务,咱们先写一个最简单的自定义注解,看懂结构,摸清套路。

语法格式记住:注解里可以定义属性,写法跟接口方法一样,还能给默认值

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

// 自定义注解:标记接口操作类型
@Target(ElementType.METHOD) // 仅作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,允许反射获取
public @interface MyOperationLog {
    // 注解属性:操作名称,默认值为空
    String operationName() default "";
}

就这么几行,一个自定义注解就造好了。

使用起来也超级简单,直接贴在方法上,给属性赋值就行:

java 复制代码
// 给新增用户接口打上标记,标注操作类型是"新增用户"
@MyOperationLog(operationName = "新增系统用户")
public void addUser(User user){
    // 核心新增业务逻辑
    userService.save(user);
}

到这里,完成了定义注解、使用注解两步。

现在这个注解只是个摆设,贴上去没任何效果,因为还差最关键的一步:解析注解,执行具体逻辑


四、SpringBoot实战核心:自定义注解+AOP实现自动日志

实际开发中,自定义注解90%的场景都是搭配AOP切面使用。

为什么用AOP?不用写重复代码,不用改原有业务逻辑,通过切面拦截加了注解的方法,自动执行前置、后置逻辑,完美契合解耦思想。

咱们做一个真实项目刚需功能:接口调用自动记录操作日志,无需每个接口手动写日志代码

实战整体步骤(四步走完)

  1. SpringBoot项目引入AOP依赖

  2. 创建自定义操作日志注解

  3. 创建AOP切面类,拦截注解、解析日志信息

  4. Controller接口使用注解,测试效果

第一步:pom.xml引入AOP依赖

SpringBoot项目必须先引入切面依赖,否则AOP功能不生效:

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

第二步:创建自定义注解@SysOperationLog

专门用于标记需要记录操作日志的接口方法:

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

/**
 * 自定义接口操作日志注解
 * 标记需要自动记录操作日志的业务方法
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysOperationLog {

    /**
     * 业务操作类型(比如新增、修改、删除、查询)
     */
    String businessType() default "";

    /**
     * 接口功能描述
     */
    String description() default "";
}

第三步:核心!创建AOP切面解析注解

这一步是关键:切面拦截所有加了@SysOperationLog注解的方法,方法执行前后,自动打印操作信息、请求参数、耗时、操作人等日志。

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 操作日志切面处理类
 * 拦截自定义注解,自动记录操作日志
 */
@Aspect // 标识为切面类
@Component // 交给Spring容器管理
public class OperationLogAspect {

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

    // 环绕通知:方法执行前后都能拦截处理
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 1、获取方法执行开始时间
        long startTime = System.currentTimeMillis();

        // 2、获取目标方法和注解上的自定义信息
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        SysOperationLog operationLog = method.getAnnotation(SysOperationLog.class);

        // 3、执行目标接口的业务方法
        Object result = point.proceed();

        // 4、计算方法执行耗时
        long costTime = System.currentTimeMillis() - startTime;

        // 5、打印操作日志(实际项目可存入数据库)
        System.out.println("===== 接口操作日志开始 =====");
        System.out.println("操作业务类型:" + operationLog.businessType());
        System.out.println("接口功能描述:" + operationLog.description());
        System.out.println("请求方法名称:" + method.getDeclaringClass().getName() + "." + method.getName());
        System.out.println("方法执行耗时:" + costTime + "ms");
        System.out.println("===== 接口操作日志结束 =====\n");

        return result;
    }
}

第四步:Controller接口使用注解测试

咱们写两个简单的测试接口,直接在方法上加上自定义注解,不用手动写任何日志打印代码:

java 复制代码
import com.example.demo.annotation.SysOperationLog;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    // 查询用户列表接口,加自定义日志注解
    @GetMapping("/user/list")
    @SysOperationLog(businessType = "查询操作", description = "查询系统所有用户列表")
    public String getUserList(){
        // 模拟业务执行
        return "用户列表查询成功,数据返回完毕";
    }

    // 新增用户接口,加自定义日志注解
    @GetMapping("/user/add")
    @SysOperationLog(businessType = "新增操作", description = "新增后台系统管理用户")
    public String addUser(){
        // 模拟业务执行
        return "新增用户成功";
    }
}

五、实际开发中,自定义注解常用场景

学会这个套路,工作中很多重复繁杂的通用功能,都可以用自定义注解+AOP搞定,除了操作日志,这些场景高频常用:

  1. 接口权限校验注解:@RequiresPermission,加在接口上,自动校验用户是否有访问权限,无权限直接拦截报错

  2. 接口限流防刷注解:@RequestLimit,限制同一个用户几秒内只能访问一次接口,防止接口被恶意刷爆

  3. 字段数据脱敏注解:@DataMask,标记手机号、身份证字段,返回前端自动脱敏隐藏中间数字

  4. 缓存自动刷新注解:@CacheRefresh,操作数据后自动清空对应缓存,不用手动写缓存代码

所有通用、重复、跟核心业务无关的代码,全都可以抽成自定义注解+AOP,让业务代码只专注做业务。


六、最后总结(记住这几句就够了)

  1. 注解本质就是代码标签,本身不干活,只做标记,靠反射或AOP解析执行逻辑

  2. 自定义注解核心就两步:元注解定义标签 + AOP切面写解析逻辑

  3. SpringBoot开发自定义注解,必配@Target@Retention\(RetentionPolicy\.RUNTIME\)

  4. 核心价值:解耦通用逻辑、消灭重复代码、让代码更简洁优雅

相关推荐
XS0301064 小时前
Java ArrayList
java·开发语言
凯尔萨厮4 小时前
Springboot2.x+JSP项目创建
java·数据库
暴力求解4 小时前
Linux---保存信号
linux·运维·服务器·开发语言·操作系统
钝挫力PROGRAMER4 小时前
贫血模型的改进
java·开发语言·设计模式·架构
lsx2024064 小时前
AngularJS 事件处理机制详解
开发语言
小书房4 小时前
Kotlin的内联函数
java·开发语言·kotlin·inline·内联函数
码农阿豪4 小时前
Python 操作金仓数据库的完全指南(上篇):连接管理与高可用
开发语言·数据库·python
计算机学姐4 小时前
基于微信小程序的校园失物招领管理系统【uniapp+springboot+vue】
java·vue.js·spring boot·mysql·信息可视化·微信小程序·uni-app
xyq20244 小时前
CSS Backgrounds(背景)
开发语言