概述

在 Java 开发中,自定义注解(Annotation)是一种元编程机制,广泛用于框架设计、代码增强(如 AOP)、参数校验、权限控制、日志埋点等场景。大型的框架工具,如Spring、MyBatis、Hibernate 都大量使用注解机制,一些我们熟知的第三方工具(Hutool、Lombok)也都是用了大量的注解机制来增强和扩展应用。

1 关于自定义注解

一般来说,常见的自定义注解基本的语法包括

  • @Target 注解使用的目标(类、方法、字段等)
  • @Retention 保留策略(源码、类文件、运行时)
  • @Documented 是否包含在 Javadoc 中
  • @Inherited 子类是否可以继承父类上的注解

自定义注解一般的获取方式是结合反射读取注解,而在使用方向上结合 Spring AOP 使用注解是最多的,Spring已经应用在各类Java语言中的架构中。

2 Retention注解

【定义】

@Retention 是一个元注解(Meta‐annotation),来源于java.lang.annotation.Retention,用来标记一个注解自身应当被保留到哪个阶段(源码/字节码/运行时),他只能用在其他注解的定义上。
一张图来看下Retention注解RetentionPolicy中的参数。
图片

【参数详解】

RetentionPolicy 定义了注解(@interface)在 Java 程序中的保留级别或者说是保留策略,也就是注解信息在什么阶段可见、可用。它包含三个枚举值SOURCE、CLASS、RUNTIME:

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

SOURCE:注解仅在源代码中可见,编译时即被丢弃,生成的 .class 文件和运行时都不可见。CLASS:注解在编译后保存在 .class 字节码文件中,但 JVM 加载类时并不保留(即默认行为)。RUNTIME:注解在编译后保存在 .class 文件中,并且在运行时通过反射依然可访问。案例测试为了更好的理解Retention注解,可以参看下代码示例。

package com.liu.aop;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

/**
 * <p> RetentionTest </p>
 * class
 * TODO
 *
 * @author Aion.Liu
 * @version v1.0.0
 * @since 2025/5/21 09:07
 */
public class RetentionTest {

    @SourceOnly
    @ClassOnly
    @RuntimeVisible
    public void test() {
    }

    public static void main(String[] args) throws Exception {
        // 仅 RuntimeVisible 会被反射读取到
        Method m = RetentionTest.class.getMethod("test");
        System.out.println("SOURCE present: " + m.isAnnotationPresent(SourceOnly.class));
        System.out.println("CLASS present:  " + m.isAnnotationPresent(ClassOnly.class));
        System.out.println("RUNTIME present:" + m.isAnnotationPresent(RuntimeVisible.class));
    }
}


// 仅源码阶段
@Retention(RetentionPolicy.SOURCE)
@interface SourceOnly { }

// 编译后保留,但运行时不可见
@Retention(RetentionPolicy.CLASS)
@interface ClassOnly { }

// 运行时可见
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeVisible { }

打印结果如下/xxx/jdk1.8.0_321.jdk/Contents/Home/bin/java -javaagent:/……次数省略不必要的信息。
SOURCE present: false
CLASS present: false

RUNTIME present:true

注意⚠️:
只有标注了 @Retention(RetentionPolicy.RUNTIME) 的注解,才可以通过反射 API(Annotation[], getAnnotation() 等)读取。

  • 默认策略:如果没有声明 @Retention,注解的默认保留策略是 CLASS。
  • 性能考虑:过多 RUNTIME 注解可能增加运行时反射成本,慎用。
  • 兼容性:使用 SOURCE 策略时,IDE 和编译器插件需同时生效,否则注解处理器可能无法触发。

3 Target注解

定义@Target 是用来指定自定义注解可以应用于哪些Java 元素的元注解(Meta-annotation),它定义了注解的作用范围,帮助编译器在错误的地方使用注解时给出提示。一张图来看下Target注解ElementType中的参数(注意:图中包括JDK 1.5 - JDK16之间注解参数值)。
图片

参数详解

@Target 注解参数是ElementType,他是一个枚举值,ElementType 定义了所有可用的目标类型,一共12种(截止到JDK21,)。

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,     // JDK 8
    TYPE_USE,                 // JDK 8
    MODULE,                     // JDK 9
    RECORD_COMPONENT; // JDK 16
}

注意⚠️
在使用时可以存在并存的情况,例如 METHOD 和 PARAMETER可以同时应用。案例测试Target注解只能应用在类和方法上面,在字段或参数上使用会编译报错。

// 自定义注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {
    String action();
}

// 应用在类名称上
@Audit(action = "UserService")
public class UserService { ... }

public class OrderService {

    // 应用在方法上
    @Audit(action = "createOrder")
    public void create() { ... }
}

4 Documented注解

定义

在 Java 中,@Documented 是一个元注解(meta-annotation),用于指示某个注解类型是否应该被包含在 Javadoc 等生成的文档中。它是 Java 注解机制的一部分,定义在 java.lang.annotation 包中。

可从源代码中查看定义,当定义一个自定义注解,并在其上添加了 @Documented,任何使用了该注解的地方,在生成 Javadoc 时都会包含该注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

案例测试

// 1. 定义时使用Documented注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ApiDescription {
    String value();
}


// 2. 在类中使用自定义注解
@ApiDescription("注解描述。")
public class UserController {
}


// 3. 在shell中执行命令,生成的 HTML 文档中将包含注解 @ApiDescription 的信息。
javadoc -d doc UserController.java

5 Inherited注解

元注解(meta-annotation),它控制一个注解是否会被子类继承。

  • @Inherited 只能用在自定义注解的定义上;
  • 它表示:如果某个类使用了某个带有@Inherited 的注解,那么它的子类也会自动继承该注解;
  • 仅对类(class)有效,对方法、字段、构造器等无效。

总结

使用范围自定义注解一般结合 Spring Boot + AOP 实现:

  • 登录校验:@LoginRequired
  • 权限控制:@Permission("admin")
  • 接口限流:@RateLimit(limit = 5, seconds = 60)
  • 日志埋点:@TrackEvent(name = "点击提交")

上述这些也都是在Java语言中,使用Spring框架使用的常用方式。


六月暴雪飞梨花
5 声望0 粉丝

就职于一家数据服务企业,在能源行业做数据治理、数据运维和数据运营