关于Java注解

一、什么是注解(Annotation)?

  • 注解是一种 元数据(metadata),用于为代码提供额外信息。
  • 不直接影响程序逻辑,但可被编译器、开发工具或运行时环境读取并处理。
  • 本质上,注解是 接口(interface),继承自 java.lang.annotation.Annotation
public @interface MyAnnotation {
    String value() default "default";
}

✅ 注解不是注释!注释(// 或 /* */)会被编译器忽略;注解会被保留并可被程序读取。


二、Java 内置的三大核心注解(来自 java.lang)

注解 作用
@Override 标记方法是重写父类/接口方法,编译器会校验
@Deprecated 标记已过时,使用时会警告
@SuppressWarnings 抑制编译器警告(如 "unchecked""rawtypes"

示例:

@SuppressWarnings("unchecked")
List list = new ArrayList();

三、元注解(Meta-Annotations)

元注解是 “注解的注解”,用于定义自定义注解的行为。Java 提供了 5 个标准元注解:

1. @Retention —— 保留策略(生命周期)

决定注解在什么阶段可用:

策略 含义 能否通过反射获取?
RetentionPolicy.SOURCE 仅在源码中存在,编译后丢弃(如 @Override
RetentionPolicy.CLASS 编译到 .class 文件,但 JVM 运行时不加载(默认值)
RetentionPolicy.RUNTIME 保留在运行时,可通过反射读取 ✅(最常用)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeAnnotation {}

2. @Target —— 使用位置

限制注解能用在哪些程序元素上:

@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyFieldOrMethodAnnotation {}

常见 ElementType

  • TYPE:类、接口、枚举
  • METHOD:方法
  • FIELD:字段
  • PARAMETER:参数
  • CONSTRUCTOR:构造器
  • LOCAL_VARIABLE:局部变量
  • PACKAGE:包

3. @Documented

表示该注解应包含在 JavaDoc 中。

4. @Inherited

允许子类 自动继承 父类的注解(仅对类注解有效)。

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface ParentAnno {}

@ParentAnno
class Parent {}

class Child extends Parent {} // Child 也“拥有” @ParentAnno

5. @Repeatable(Java 8+)

允许在同一位置重复使用同一注解。

@Repeatable(Authorizations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
    String role();
}

@Retention(RetentionPolicy.RUNTIME)
public @interface Authorizations {
    Authorization[] value();
}

// 使用
@Authorization(role = "admin")
@Authorization(role = "user")
public void doSomething() {}

四、如何定义自定义注解?

语法:使用 @interface 关键字。

基本规则:

  • 方法不能有参数、不能抛异常、不能是泛型。
  • 返回类型只能是:基本类型、String、Class、枚举、注解,或其数组。
  • 可以有默认值(default)。

示例:一个权限控制注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequireRole {
    String[] value();          // 必填
    boolean logAccess() default true; // 可选,默认 true
}

使用:

@RequireRole(value = {"admin", "manager"}, logAccess = false)
public void deleteUser(String id) {
    // ...
}

五、如何读取注解?(反射)

只有 @Retention(RUNTIME) 的注解才能通过反射获取。

示例:读取方法上的注解

Method method = MyClass.class.getMethod("deleteUser", String.class);
if (method.isAnnotationPresent(RequireRole.class)) {
    RequireRole anno = method.getAnnotation(RequireRole.class);
    System.out.println(Arrays.toString(anno.value())); // [admin, manager]
    System.out.println(anno.logAccess()); // false
}

读取类/字段/参数注解类似:

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Parameter.getAnnotation(Class)

六、注解的典型应用场景

场景 框架示例 说明
依赖注入 Spring (@Autowired, @Component) 容器根据注解自动装配 Bean
Web 路由 Spring MVC (@RequestMapping) 将 HTTP 请求映射到方法
数据库映射 JPA/Hibernate (@Entity, @Column) 对象与表字段映射
参数校验 Bean Validation (@NotNull, @Min) 配合 @Valid 自动校验
AOP 切面 Spring (@Aspect, @Around) 声明切面逻辑
测试 JUnit (@Test, @BeforeEach) 标记测试方法

七、进阶:编译时注解处理(APT)

除了运行时反射,还可以在 编译期 处理注解,生成代码(如 Lombok、Dagger、ButterKnife)。

  • 使用 javax.annotation.processing.Processor
  • 需要配合 META-INF/services/javax.annotation.processing.Processor 注册
  • 不影响运行时性能(因为代码在编译时就生成好了)

⚠️ 这部分较复杂,初学可先掌握运行时反射方式。


八、常见误区 & 注意事项

  1. 注解 ≠ 接口实现
    注解本身没有行为,必须由外部(框架/你自己)解析并执行逻辑。
  2. 默认值很重要
    如果不设 default,使用时必须显式赋值。
  3. 数组 vs 单值
    如果只传一个值,可直接写:@MyAnno("hello")(前提是方法名为 value()
  4. 注解不能继承普通类
    所有注解都隐式继承 java.lang.annotation.Annotation,不能再继承其他类。

九、小练习(巩固理解)

尝试自己实现一个简单的日志注解:

@Retention(RUNTIME)
@Target(METHOD)
public @interface LogExecution {
    String value() default "";
}

然后写一个工具方法,通过代理或 AOP(或简单反射)在调用方法前打印日志。


如果你正在学习 Spring,那么注解是它的核心!掌握好注解,你就掌握了现代 Java 开发的“声明式编程”思想。

posted @ 2025-12-26 11:33  火星程序员ty  阅读(1)  评论(0)    收藏  举报