什么是注解?
在深入自定义之前,我们先理解注解是什么,注解(Annotation)是 Java 5 引入的一种元数据(metadata)机制,它不属于程序逻辑本身,但可以被用来提供关于代码的额外信息。

可以把注解想象成给代码打的“标签”或“注释”,但这些标签可以被编译器、工具或运行时环境读取,并执行相应的操作。
常见的内置注解有:
@Override: 告诉编译器这个方法是一个重写方法,如果方法名写错了,编译器会报错。@Deprecated: 标记一个类、方法或字段已过时,不推荐使用。@SuppressWarnings: 告诉编译器忽略某些警告。@FunctionalInterface: 标记一个接口是函数式接口。
自定义注解的语法
自定义注解需要使用 @interface 关键字,一个最简单的自定义注解如下:
// 定义一个名为 MyAnnotation 的注解
public @interface MyAnnotation {
}
元注解
元注解是用于注解其他注解的注解,它们提供了如何使用自定义注解的规则,最常用的元注解有:

-
@Target: 定义注解可以应用在哪些程序元素上。ElementType.TYPE: 类、接口、枚举ElementType.METHOD: 方法ElementType.FIELD: 字段、属性ElementType.PARAMETER: 参数ElementType.CONSTRUCTOR: 构造函数ElementType.ANNOTATION_TYPE: 注解类型ElementType.PACKAGE: 包
-
@Retention: 定义注解的生命周期,即注解信息保留到什么时候。RetentionPolicy.SOURCE: 只在源码中保留,编译时被丢弃(如@Override)。RetentionPolicy.CLASS: 在.class文件中保留,但在运行时被 JVM 丢弃,这是默认策略。RetentionPolicy.RUNTIME: 在运行时仍然保留,可以通过反射机制读取。这是我们最常关心的策略,因为只有在运行时可用,才能在程序逻辑中处理注解。
-
@Documented: 表示该注解会被包含在 JavaDoc 文档中。 -
@Inherited: 表示如果一个注解被用在某个类上,那么这个类的子类也会自动继承这个注解。
(图片来源网络,侵删)
带成员的自定义注解
注解可以像接口一样定义成员(抽象方法),成员的返回类型只能是以下几种:
- 基本数据类型 (
byte,char,short,int,long,float,double,boolean) StringClass及其数组enum及其数组- 其他注解及其数组
- 类型的数组
成员不能有参数,也不能有 throws 子句,默认值通过 default 关键字指定。
示例:定义一个带有成员的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 1. @Target: 这个注解可以用于方法和字段
@Target({ElementType.METHOD, ElementType.FIELD})
// 2. @Retention: 这个注解在运行时可用
@Retention(RetentionPolicy.RUNTIME)
// 3. 定义注解的成员
public @interface MyAnnotation {
// 成员,默认值为 "default value"
String value() default "default value";
// 成员,没有默认值,使用时必须提供
int priority();
// 成员,有默认值
String[] tags() default {"tag1", "tag2"};
}
如何使用自定义注解?
使用自定义注解非常简单,只需要在需要的地方加上 符号和注解名称即可。
public class MyClass {
// 使用注解在字段上
@MyAnnotation(priority = 1, value = "This is a field annotation")
private String myField;
// 使用注解在方法上
@MyAnnotation(priority = 10, value = "This is a method annotation", tags = {"important", "service"})
public void myMethod() {
System.out.println("Annotated method is called.");
}
}
如何处理(解析)自定义注解?
注解本身不会做任何事情,它只是一个“标签”,真正的威力在于如何解析这些标签,解析注解主要通过反射机制。
下面是一个完整的示例,展示如何定义、使用和解析我们上面创建的 @MyAnnotation。
步骤 1: 定义注解
// MyAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "default value";
int priority();
String[] tags() default {"tag1", "tag2"};
}
步骤 2: 在代码中使用注解
// MyClass.java
public class MyClass {
@MyAnnotation(priority = 1, value = "Annotated Field")
private String myField;
@MyAnnotation(priority = 10, value = "Annotated Method", tags = {"core", "api"})
public void doSomething() {
System.out.println("Executing doSomething...");
}
// 一个没有使用注解的方法,用于对比
public void doSomethingElse() {
System.out.println("Executing doSomethingElse...");
}
}
步骤 3: 编写解析器(通过反射读取注解)
// AnnotationProcessor.java
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class AnnotationProcessor {
public static void process(String className) {
try {
// 1. 获取 Class 对象
Class<?> clazz = Class.forName(className);
System.out.println("=== Processing Class: " + clazz.getName() + " ===");
// 2. 处理类上的注解 (如果有的话)
// System.out.println("Class annotations: " + Arrays.toString(clazz.getAnnotations()));
// 3. 处理字段上的注解
System.out.println("\n--- Processing Fields ---");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
System.out.println("Field: " + field.getName());
System.out.println(" - Value: " + annotation.value());
System.out.println(" - Priority: " + annotation.priority());
System.out.println(" - Tags: " + String.join(", ", annotation.tags()));
}
}
// 4. 处理方法上的注解
System.out.println("\n--- Processing Methods ---");
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println("Method: " + method.getName());
System.out.println(" - Value: " + annotation.value());
System.out.println(" - Priority: " + annotation.priority());
System.out.println(" - Tags: " + String.join(", ", annotation.tags()));
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 解析 MyClass 中的注解
process("MyClass");
}
}
运行结果
执行 AnnotationProcessor 的 main 方法,你将看到如下输出:
=== Processing Class: MyClass ===
--- Processing Fields ---
Field: myField
- Value: Annotated Field
- Priority: 1
- Tags: tag1, tag2
--- Processing Methods ---
Method: doSomething
- Value: Annotated Method
- Priority: 10
- Tags: core, api
实际应用场景
自定义注解是许多现代 Java 框架的基石。
-
依赖注入:
- Spring 框架大量使用注解,如
@Autowired、@Component、@Service、@RestController等。 - 当 Spring 启动时,它会扫描指定包下的所有类,通过反射读取这些注解,然后根据注解信息创建对象(Bean)并管理它们的生命周期,实现依赖注入。
- Spring 框架大量使用注解,如
-
框架配置:
- JUnit 5 的
@Test、@BeforeEach、@AfterEach等注解,测试运行器会读取这些注解,在适当的时候执行测试方法和生命周期方法。
- JUnit 5 的
-
Web 开发:
