元注解
元注解是「注解的注解」——用来标注其他注解,控制它们的行为。Java 提供了五个标准元注解。
五个元注解一览
| 元注解 | 作用 |
|---|---|
@Retention | 注解保留到哪个阶段(源码/编译/运行) |
@Target | 注解可以用在哪些元素上 |
@Documented | 注解是否包含在 Javadoc 中 |
@Inherited | 注解是否被子类继承 |
@Repeatable | 注解是否可以重复使用 |
@Retention:保留策略
这是最重要的元注解,决定了注解的生命周期:
| 策略 | 说明 | 典型用途 |
|---|---|---|
| SOURCE | 只存在于源码,编译后丢弃 | @Override、@SuppressWarnings |
| CLASS | 存在于 .class 文件,JVM 加载时不保留 | 编译时注解处理器(如 Lombok) |
| RUNTIME | 存在于运行时,可通过反射获取 | Spring 依赖注入、JUnit 测试 |
java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// 源码保留(默认):编译后丢弃
@Retention(RetentionPolicy.SOURCE)
public @interface SourceOnly { }
// CLASS 保留:编译时保留,运行时丢弃
@Retention(RetentionPolicy.CLASS)
public @interface ClassOnly { }
// RUNTIME 保留:运行时可见,反射可读取
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeOnly { }1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
@Target:使用范围
控制注解可以用在哪些代码元素上:
| ElementType | 说明 |
|---|---|
| TYPE | 类、接口、枚举 |
| METHOD | 方法 |
| FIELD | 字段 |
| PARAMETER | 方法参数 |
| CONSTRUCTOR | 构造方法 |
| LOCAL_VARIABLE | 局部变量 |
| ANNOTATION_TYPE | 注解类型 |
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// 只能用于方法
@Target(ElementType.METHOD)
public @interface MethodAnnotation { }
// 可用于多种元素
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface MultiAnnotation { }1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
@Documented:包含在 Javadoc 中
默认情况下,注解不会出现在 Javadoc 中。加上 @Documented 后会包含:
java
import java.lang.annotation.Documented;
@Documented
public @interface DocumentedAnnotation {
String value();
}
// 使用此注解的代码生成的 Javadoc 会包含这个注解信息1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
@Inherited:子类继承
如果父类标注了 @Inherited 注解,子类会自动继承该注解:
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 子类会继承此注解
public @interface InheritedAnnotation {
String value();
}
@InheritedAnnotation("parent")
class Parent { }
class Child extends Parent { } // 自动继承 @InheritedAnnotation
public class InheritedDemo {
public static void main(String[] args) {
// Child 类继承了父类的注解
if (Child.class.isAnnotationPresent(InheritedAnnotation.class)) {
InheritedAnnotation ann = Child.class.getAnnotation(InheritedAnnotation.class);
System.out.println(ann.value()); // parent
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
注意:@Inherited 只对类级别的注解生效,对方法注解无效。
@Repeatable:可重复注解(JDK 8+)
JDK 8 之前,同一个注解不能重复使用。@Repeatable 解决了这个问题:
java
import java.lang.annotation.*;
// 1. 定义基础注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Authors.class) // 指定容器注解
public @interface Author {
String name();
}
// 2. 定义容器注解(名字必须以复数形式结尾)
@Retention(RetentionPolicy.RUNTIME)
public @interface Authors {
Author[] value();
}
// 3. 使用:同一个注解可以写多次
@Author(name = "张三")
@Author(name = "李四")
public class Book {
public static void main(String[] args) {
// 获取所有 @Author 注解
Author[] authors = Book.class.getAnnotationsByType(Author.class);
for (Author author : authors) {
System.out.println(author.name());
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
综合示例
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface Documentation {
String value() default "";
String author() default "Unknown";
}
@Documentation(value = "用户服务类", author = "张三")
public class UserService {
@Documentation("用户登录方法")
public void login() { }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意事项
@Retention是最重要的元注解:如果设置为SOURCE,运行时反射无法获取@Target可以指定多个值:用数组形式{ElementType.TYPE, ElementType.METHOD}@Inherited只对类生效:方法、字段的注解不会被继承@Repeatable需要容器注解:必须配套定义一个「容器」注解
