别再懵注解!从 JDK 到 SpringBoot,这篇 “代码贴标签” 攻略超下饭

咱后端写代码时,总少不了跟@XXX这种 "@符号怪" 打交道 ------ 比如加个@Override就敢改父类方法,写个@RestController接口就跑起来了。但你真的懂这些 "标签" 到底是啥吗?今天咱就把注解扒得明明白白,从基础到 SpringBoot 实战,看完保你再也不慌!

一、先搞懂:注解到底是个啥?------ 代码的 "说明书"

其实注解(Annotation)超简单,本质就是给代码贴的 "标签"------ 就像你给快递贴 "易碎品",给文件标 "加急",注解就是给类、方法、变量贴的 "说明符"。

它不直接影响代码运行,但能告诉编译器 / 框架:"喂,这个代码是干这个的!" 比如@Deprecated贴在方法上,编译器就会喊:"警告!这方法要被淘汰啦!"

二、JDK 自带的 "注解小工具"------ 这些你天天用,可能没细想

JDK 早就给咱准备了一批常用注解,相当于 "现成的标签",咱先盘点几个高频的:

注解 作用(人话版) 场景举例
@Override 告诉编译器:"我这是重写父类的方法!" 子类重写toString()方法
@Deprecated 标红警告:"这玩意儿要淘汰了,别用了!" 老项目里的Date的toLocaleString()
@SuppressWarnings 让编译器 "闭嘴":"别报这个警告了,我知道我在干啥" 用了老 API 不想看黄色警告
@FunctionalInterface 强制检查:"这接口必须是函数式接口(只有一个抽象方法)" 写 Lambda 表达式时用

比如你写个类重写方法,没加@Override也能跑,但加了之后编译器会帮你检查 ------ 要是父类根本没这方法,直接报错!这就是 "标签" 的力量~

三、自己造注解!------ 定义注解的 "语法模板"

光用现成的不够爽,咱也能自己造注解!记住,定义注解的语法像极了 "创建接口",但要加个@前缀,还得配 "元注解"(后面讲)。

先看个最简单的自定义注解模板:

less 复制代码
// 元注解(给注解加的注解,相当于"标签的标签")
@Retention(RetentionPolicy.RUNTIME) // 控制注解能存活到什么时候
@Target(ElementType.METHOD) // 控制注解能贴在什么地方(这里是方法上)
public @interface MyFirstAnnotation { 
    // 注解的属性(相当于标签的"填写项")
    String value() default "默认值"; // 带默认值的属性
    int version() required; // 必须填的属性(没有default)
}

划重点:定义注解用@interface,不是interface!别少写了@~

四、元注解:给 "注解" 贴的标签 ------ 注解的 "管理员"

刚才定义注解时,前面加的@Retention、@Target就是 "元注解"------ 相当于给 "标签" 本身定规矩的 "管理员"。JDK 里就 4 个核心元注解,记牢这 4 个就够了:

  1. @Retention:控制注解 "活多久"(生命周期)
    • SOURCE:只在源码里存在,编译成 class 就没了(比如@Override)
    • CLASS:能进 class 文件,但 JVM 运行时不读(默认值,少用)
    • RUNTIME:能活到 JVM 运行时,框架常用(比如 Spring 的@Autowired)
  1. @Target:控制注解能 "贴在哪"(作用范围)
    • 常用值:TYPE(类 / 接口)、METHOD(方法)、FIELD(变量)、PARAMETER(参数)
    • 比如@Target({ElementType.METHOD, ElementType.FIELD}),就既能贴方法也能贴变量
  1. @Inherited:允许子类 "继承" 父类的注解(比如父类贴了@MyAnno,子类也能拿到)
  1. @Documented:让注解能被javadoc生成到文档里(一般用得少,追求文档完整可以加)

五、注解的 "属性":给标签加 "填写项"------ 让注解更灵活

注解的属性就像标签的 "填空栏",比如你造个@Log注解,想让它支持 "日志级别""描述",就可以加属性:

less 复制代码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
    // 1. 普通属性:类型 + 名称(),可加default给默认值
    String level() default "INFO"; // 日志级别,默认INFO
    // 2. value属性:特殊!使用时可以省略"value="
    String value() default "无描述"; // 日志描述
    // 3. 数组属性:用{}表示多个值
    String[] tags() default {}; // 日志标签,支持多值
}

用的时候超简单,像填表单一样:

less 复制代码
public class UserService {
    // 用value属性:可以省略"value="
    @Log("查询用户") 
    public User getUserById(Long id) { ... }
    // 多属性:按名填写,数组用{}
    @Log(level = "WARN", value = "删除用户", tags = {"敏感操作", "用户管理"})
    public void deleteUser(Long id) { ... }
}

小技巧:如果属性是数组且只有一个值,连{}都能省,比如tags = "敏感操作"~

六、自定义注解实战:写个 "日志注解" 玩玩 ------ 从定义到使用

光说不练假把式,咱来写个能实际用的@Log注解,配合反射解析它,实现 "调用方法时自动打日志"。

步骤 1:定义日志注解

less 复制代码
@Retention(RetentionPolicy.RUNTIME) // 必须RUNTIME,反射才能拿到
@Target(ElementType.METHOD) // 只贴在方法上
public @interface Log {
    String value() default "执行方法"; // 日志描述
    String level() default "INFO"; // 日志级别
}

步骤 2:写个工具类,用反射解析注解

反射是解析注解的核心!因为只有反射能在运行时拿到类 / 方法上的注解信息:

arduino 复制代码
public class LogUtils {
    // 传入方法,解析它的@Log注解并打日志
    public static void printLog(Method method) {
        // 1. 判断方法上有没有@Log注解
        if (method.isAnnotationPresent(Log.class)) {
            // 2. 拿到注解对象
            Log logAnnotation = method.getAnnotation(Log.class);
            // 3. 获取注解的属性值,打日志
            String level = logAnnotation.level();
            String desc = logAnnotation.value();
            System.out.printf("[%s] %s:%s%n", level, method.getName(), desc);
        }
    }
}

步骤 3:测试一下!

arduino 复制代码
public class TestLog {
    @Log(value = "查询用户列表", level = "DEBUG")
    public void getUsers() { /* 模拟查用户 */ }
    public static void main(String[] args) throws NoSuchMethodException {
        // 拿到getUsers方法对象
        Method method = TestLog.class.getMethod("getUsers");
        // 解析注解并打日志
        LogUtils.printLog(method); 
        // 输出结果:[DEBUG] getUsers:查询用户列表
    }
}

看到没?这就是自定义注解的魅力 ------ 以后想给方法加统一功能(比如日志、权限校验),用注解 + 反射就搞定!

七、扒开注解的 "真面目"------ 原来它是个 "特殊接口"

前面说注解像标签,但你知道它的本质吗?其实注解编译后会变成接口,而且默认继承java.lang.annotation.Annotation!

比如刚才的@Log注解,编译后会变成这样(反编译结果):

csharp 复制代码
public interface Log extends java.lang.annotation.Annotation {
    String value();
    String level();
}

所以你定义的注解属性,其实是接口的抽象方法;拿到的注解对象,是 JVM 动态生成的接口实现类 ------ 是不是瞬间懂了?

八、注解的 "正确打开方式"------ 在哪用,怎么用?

注解的使用场景主要分两类,咱后端 er 最常用的就是第二种:

  1. 编译器级别的使用:告诉编译器做检查 / 处理
    • 比如@Override让编译器校验重写正确性,@SuppressWarnings屏蔽警告
    • 这类注解一般用@Retention(SOURCE),编译后就没了
  1. 框架级别的使用:给框架提供配置信息
    • 比如 Spring 的@Autowired让框架自动注入对象,MyBatis 的@Select指定 SQL
    • 这类注解必须用@Retention(RUNTIME),框架靠反射解析

九、解析注解的 "核心 API"------ 反射包里的这几个方法要记牢

解析注解全靠java.lang.reflect包,关键就这几个方法,记下来够用了:

反射对象 核心方法 作用
Class/Method/Field isAnnotationPresent(Class<? extends Annotation> annoClass) 判断是否有指定注解
Class/Method/Field getAnnotation(Class annoClass) 拿到指定注解的对象
Class/Method/Field getAnnotations() 拿到所有注解(包括继承的)
Annotation annotationType() 拿到注解的类型(比如 Log.class)

十、SpringBoot 里的 "注解全家桶"------ 这些你天天用,今天懂原理了

最后咱聊点实战的:SpringBoot 里的注解为啥这么好用?其实都是基于前面讲的 "注解 + 反射" 原理!盘点几个高频注解,看完你就懂了:

1. 启动相关

  • @SpringBootApplication:SpringBoot 的 "万能钥匙",其实是三个注解的组合:
    • @SpringBootConfiguration:标记这是配置类
    • @ComponentScan:扫描当前包及子包的@Component类(比如@Service、@Controller)
    • @EnableAutoConfiguration:开启自动配置(比如你加了 mysql 依赖,自动配数据源)

2. 接口开发相关

  • @RestController:= @Controller + @ResponseBody,告诉 Spring:"这是接口类,返回 JSON"
  • @RequestMapping("/user"):给接口贴 "地址标签",浏览器访问/user就找到这个方法
  • @RequestParam("id"):告诉 Spring:"把请求里的 id 参数,传给方法的这个参数"

3. 依赖注入相关

  • @Autowired:让 Spring "自动找个对象塞进来",不用自己new
  • @Service/@Component:给类贴 "组件标签",告诉 Spring:"把我装进容器里,后面好用"

4. 事务相关

  • @Transactional:给方法贴 "事务标签",Spring 会自动帮你管理事务(提交 / 回滚)

比如你写个接口:

less 复制代码
@RestController // 标记这是接口类
@RequestMapping("/user") // 接口前缀
public class UserController {
    @Autowired // 自动注入UserService
    private UserService userService;
    @GetMapping("/{id}") // 接口地址:/user/123
    public User getUser(@PathVariable("id") Long id) { // 拿路径里的id参数
        return userService.getUserById(id);
    }
}

现在再看这段代码,是不是瞬间明白:每个注解都是给 SpringBoot 的 "指令",框架靠反射解析这些注解,才帮你完成了注入、接口映射这些工作!

总结:注解这东西,学会了真的香!

其实注解的核心就三点:

  1. 是给代码贴的 "标签",靠元注解定规矩
  1. 自定义注解要配@interface+ 属性,解析靠反射
  1. SpringBoot 等框架靠注解简化配置,本质是 "注解 + 反射"

看完这篇,下次再写@XXX的时候,别再只当它是 "符号" 了 ------ 想想它的生命周期、作用范围,甚至自己造个注解解决问题,这才是后端 er 的进阶姿势!

最后问一句:你平时用注解踩过什么坑?比如没加@Retention(RUNTIME)导致解析不到?评论区聊聊,咱一起避坑~

相关推荐
Joey_Chen7 分钟前
【Golang开发】快速入门Go——Go语言中的面向对象编程
后端·go
lookFlying10 分钟前
Python 项目 Docker 仓库发布指南
后端
易元10 分钟前
模式组合应用-组合模式
后端·设计模式
秋难降15 分钟前
从浅克隆到深克隆:原型模式如何解决对象创建的 “老大难”?😘
后端·设计模式·程序员
bobz96526 分钟前
安装 nvidia 驱动之前要求关闭 secureBoot 么
后端
叫我阿柒啊35 分钟前
Java全栈工程师的面试实战:从技术细节到业务场景
java·数据库·spring boot·微服务·vue·全栈开发·面试技巧
程序员的世界你不懂44 分钟前
【Flask】测试平台开发实战-第一篇
后端·python·flask
CC__xy1 小时前
《ArkUI 记账本开发:状态管理与数据持久化实现》
java·前端·javascript
布朗克1681 小时前
OpenTelemetry 通过自动埋点(Java Agent) 应用于springboot项目
java·spring boot·spring·opentelemetry
bobz9651 小时前
dracut 是什么?
后端