定义注解
约 1040 字大约 3 分钟
2025-07-17
在 Java 语言中,我们使用 @interface 语法来定义注解 (Annotation)。一个注解的定义通常包含注解的名称、参数以及参数的默认值。
例如,以下代码定义了一个名为 Report 的注解:
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}注解的参数类似于无参数方法,并且可以使用 default 关键字来设定一个默认值。强烈建议为注解的参数设置默认值,以提高注解的使用灵活性。通常,最常用的参数应当命名为 value。
2.1 元注解
元注解是用于修饰其他注解的注解。Java 标准库提供了一些预定义的元注解,开发者可以直接使用,而无需自行编写。
2.1.1 @Target
@Target 是最常用的元注解之一。它用于定义注解可以应用于源码的哪些位置。
以下是 @Target 可以定义的几种类型:
- 类或接口:
ElementType.TYPE - 字段:
ElementType.FIELD - 方法:
ElementType.METHOD - 构造方法:
ElementType.CONSTRUCTOR - 方法参数:
ElementType.PARAMETER
例如,要定义注解 @Report 只能用于方法上,需要添加 @Target(ElementType.METHOD):
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}如果希望注解 @Report 可以用于方法或字段上,可以将 @Target 注解的参数设置为数组 { ElementType.METHOD, ElementType.FIELD }:
@Target({
ElementType.METHOD,
ElementType.FIELD
})
public @interface Report {
...
}实际上,@Target 定义的 value 是 ElementType[] 数组。当数组只有一个元素时,可以省略数组的写法。
2.1.2 @Retention
@Retention 是另一个重要的元注解,它定义了注解的生命周期。
以下是 @Retention 可以定义的几种策略:
- 仅编译期:
RetentionPolicy.SOURCE - 仅 class 文件:
RetentionPolicy.CLASS - 运行期:
RetentionPolicy.RUNTIME
如果 @Retention 不存在,则该注解默认为 CLASS。通常,自定义的注解需要在运行期读取,因此务必加上 @Retention(RetentionPolicy.RUNTIME) 这个元注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}@Retention 注解定义了注解的保留策略,即注解在哪个阶段有效。RetentionPolicy.RUNTIME 表示该注解在运行时也保留,可以通过反射机制读取注解信息。这在很多框架和库中非常常见,例如 Spring 框架就广泛使用运行时注解来实现依赖注入和配置。
2.1.3 @Repeatable
@Repeatable 元注解用于定义注解是否可重复。这个注解的应用相对较少。
@Repeatable(Reports.class)
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
@Target(ElementType.TYPE)
public @interface Reports {
Report[] value();
}经过 @Repeatable 修饰后,可以在某个类型声明处添加多个 @Report 注解:
@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Hello {
}@Repeatable 注解允许在同一声明上多次使用相同的注解。要使用 @Repeatable,需要定义一个容器注解(如上面的 Reports),该容器注解包含一个类型为被重复注解数组的 value 成员。
2.1.4 @Inherited
@Inherited 用于定义子类是否可以继承父类定义的注解。@Inherited 仅对 @Target(ElementType.TYPE) 类型的注解有效,并且仅针对 class 的继承,对 interface 的继承无效:
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}当一个类使用了 @Report 注解时:
@Report(type=1)
public class Person {
}则它的子类默认也定义了该注解:
public class Student extends Person {
}@Inherited 注解使得子类可以自动继承父类的注解。需要注意的是,此注解只对类有效,对接口无效。
2.2 如何定义 Annotation
下面总结一下定义 Annotation 的步骤:
第一步,使用 @interface 定义注解:
public @interface Report {
}第二步,添加参数和默认值:
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}建议将最常用的参数定义为 value(),并为所有参数设置默认值。
第三步,使用元注解配置注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}其中,必须设置 @Target 和 @Retention。@Retention 通常设置为 RUNTIME,因为自定义的注解通常需要在运行期读取。一般情况下,不必使用 @Inherited 和 @Repeatable。