📫 作者简介:「六月暴雪飞梨花」,专注于研究Java,就职于科技型公司后端工程师
🏆 近期荣誉:华为云云享专家、阿里云专家博主、腾讯云优秀创作者、腾讯云TDP-KOL、ACDU成员、墨天轮技术专家博主
🔥 三连支持:欢迎 ❤️关注、👍点赞、👉收藏三连,支持一下博主~
概述
在 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 - ……
com.liu.aop.RetentionTest
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框架使用的常用方式。
欢迎关注博主 「六月暴雪飞梨花」 或加入【六月暴雪飞梨花社区】一起学习和分享Linux、C、C++、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。