定义注解
约 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
。