JAVA 注解(Annotation):从原理到实战应用

6 阅读5分钟

Java 注解(Annotation):从原理到实战应用

注解是 Java 5 引入的代码级别的说明标记,它相当于给代码贴“标签”,本身不直接执行逻辑,但可以被编译器、框架、JVM读取并解析,实现自动化配置、代码生成、校验等功能。

从本质上讲,注解是一种特殊的接口,底层通过反射实现解析,是 Spring、MyBatis、Spring Boot 等主流框架的核心基础。


一、注解核心基础

1. 什么是注解?

  • 定义:用于修饰类、方法、变量、参数、包的元数据(描述数据的数据)。
  • 特点:
  1. 不直接影响代码逻辑;
  2. 可在编译、类加载、运行时被读取;
  3. 是框架实现「约定大于配置」的核心。

2. 注解的分类

(1)JDK 内置标准注解(直接用)
注解作用
​@Override​标记方法重写,编译时校验
​@Deprecated​标记方法/类已废弃,提醒不推荐使用
​@SuppressWarnings​压制编译器警告
​@FunctionalInterface​标记函数式接口
(2)元注解(用来定义注解的注解,核心)

Java 提供了 4 个元注解,专门用于自定义注解:

  1. ​ @Target​​:指定注解能修饰的目标(类、方法、字段等)
  2. ​ @Retention​​:指定注解的生命周期(源码、编译、运行时)
  3. ​ @Documented​​:标记注解会被生成到 Javadoc 中
  4. ​ @Inherited​​:标记注解可被子类继承

重点:​​@Retention​​ 是注解生效的关键!

  • ​RetentionPolicy.SOURCE​​:仅源码阶段(如 ​​@Override​​)
  • ​RetentionPolicy.CLASS​​:编译阶段(默认)
  • ​RetentionPolicy.RUNTIME​​:运行时(可通过反射解析,框架常用)

二、注解原理:底层是怎么工作的?

1. 本质原理

  1. 注解本质是 继承自​Annotation​​ 接口的特殊接口
  2. 注解的属性 = 接口的抽象方法;
  3. 注解的解析 = Java 反射机制读取注解信息;
  4. 框架(如 Spring)通过解析注解,自动完成对象创建、依赖注入等操作。

2. 核心流程

定义注解 → 标记代码 → 反射解析注解 → 执行对应逻辑


三、实战一:自定义注解 + 反射解析(运行时注解)

我们手写一个自定义校验注解,实现:给实体类字段加注解,自动校验字段非空、长度限制。

步骤 1:定义自定义注解

import java.lang.annotation.*;

// 元注解:注解作用在字段上
@Target(ElementType.FIELD)
// 元注解:运行时生效(必须写,否则反射读取不到)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckRule {
    // 非空校验:默认true
    boolean notNull() default false;
    // 长度最小值
    int minLength() default 0;
    // 长度最大值
    int maxLength() default Integer.MAX_VALUE;
    // 校验失败提示信息
    String message() default "参数校验失败";
}

步骤 2:编写实体类,使用注解

public class User {
    // 非空校验 + 长度2-10
    @CheckRule(notNull = true, minLength = 2, maxLength = 10, message = "用户名不能为空且长度2-10")
    private String username;

    // 仅非空校验
    @CheckRule(notNull = true, message = "密码不能为空")
    private String password;

    // getter/setter
    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username;}
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}
}

步骤 3:反射解析注解(核心逻辑)

import java.lang.reflect.Field;

public class CheckUtils {
    // 通用校验方法:传入对象,自动解析注解完成校验
    public static String validate(Object obj) {
        // 1. 获取对象的Class对象
        Class<?> clazz = obj.getClass();
        // 2. 获取所有字段
        Field[] fields = clazz.getDeclaredFields();

        // 3. 遍历字段,解析注解
        for (Field field : fields) {
            // 判断字段是否有我们的注解
            if (field.isAnnotationPresent(CheckRule.class)) {
                // 获取注解实例
                CheckRule checkRule = field.getAnnotation(CheckRule.class);
                // 暴力反射(打破private限制)
                field.setAccessible(true);

                try {
                    // 获取字段值
                    Object value = field.get(obj);

                    // 非空校验
                    if (checkRule.notNull()) {
                        if (value == null || value.toString().trim().isEmpty()) {
                            return checkRule.message();
                        }
                    }

                    // 长度校验(仅字符串)
                    if (value instanceof String str) {
                        int len = str.length();
                        if (len < checkRule.minLength() || len > checkRule.maxLength()) {
                            return checkRule.message();
                        }
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        // 校验通过
        return null;
    }
}

步骤 4:测试效果

public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setUsername(""); // 空用户名
        user.setPassword(null); // 空密码

        String result = CheckUtils.validate(user);
        if (result != null) {
            System.out.println("校验失败:" + result);
        } else {
            System.out.println("校验通过");
        }
    }
}

输出结果

校验失败:用户名不能为空且长度2-10

✅ 这就是注解的核心实战:自定义注解 + 反射解析 = 自动化逻辑


四、实战二:模拟 Spring @Component 注解

Spring 中 ​​@Component​​ 作用:标记类为 Spring 管理的 Bean,我们手写简化版实现。

步骤 1:定义注解

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
    // Bean名称
    String value() default "";
}

步骤 2:标记业务类

@MyComponent("userService")
public class UserService {
    public void sayHello() {
        System.out.println("Hello, 注解实战!");
    }
}

步骤 3:模拟 Spring 容器(反射扫描注解)

import java.util.HashMap;
import java.util.Map;

public class MySpringContext {
    // 模拟Spring容器,存储Bean
    private final Map<String, Object> beanMap = new HashMap<>();

    // 初始化:扫描类上的注解,创建对象存入容器
    public void init(Class<?>... classes) {
        for (Class<?> clazz : classes) {
            if (clazz.isAnnotationPresent(MyComponent.class)) {
                MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                // 获取Bean名称
                String beanName = annotation.value().isEmpty() 
                        ? clazz.getSimpleName() 
                        : annotation.value();
                try {
                    // 创建对象
                    Object bean = clazz.newInstance();
                    // 存入容器
                    beanMap.put(beanName, bean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 根据名称获取Bean
    public Object getBean(String beanName) {
        return beanMap.get(beanName);
    }
}

步骤 4:测试

public class TestSpring {
    public static void main(String[] args) {
        MySpringContext context = new MySpringContext();
        // 初始化容器
        context.init(UserService.class);
        // 获取Bean
        UserService userService = (UserService) context.getBean("userService");
        userService.sayHello();
    }
}

输出结果

Hello, 注解实战!

五、注解的高级应用场景

  1. 框架核心:Spring ​​@Controller​​/​​@Service​​/​​@Autowired​​、MyBatis ​​@Mapper​​、Spring Boot ​​@SpringBootApplication​​;
  2. 参数校验:Hibernate Validator ​​@NotBlank​​/​​@NotNull​​;
  3. 代码生成:Lombok ​​@Data​​/​​@Getter​​(编译时生成代码);
  4. 日志/监控:自定义注解 + AOP 实现自动日志、接口限流;
  5. ORM 映射:MyBatis-Plus ​​@TableName​​/​​@TableId​​。

六、核心总结

1. 原理一句话

注解是特殊接口,通过反射解析,是框架实现自动化配置的核心。

2. 自定义注解固定模板

  1. 4个元注解修饰(必写 ​​@Target​​ + ​​@Retention​​);
  2. 定义注解属性(方法形式);
  3. 反射解析注解,实现业务逻辑。

3. 关键知识点

  • ​@Retention(RetentionPolicy.RUNTIME)​​:运行时解析必须加
  • 解析注解核心 API:​​isAnnotationPresent()​​、​​getAnnotation()​​;
  • 注解本身无逻辑,解析器才是核心

掌握注解,你就掌握了 Java 框架的底层密码!