java注解本质是继承annotation接口的元数据载体,通过@target、@retention等元注解控制作用位置与生命周期,需在编译期写入字节码(runtimevisibleannotations)、运行时由框架通过反射读取并触发对应逻辑。

Java 注解不是“装饰符”,而是编译期和运行时可被读取的元数据载体。真正硬核的地方不在记住 @Override 或 @Test,而在于理解它如何参与编译、字节码生成、反射调用,以及如何与 Spring、MyBatis-Plus 等框架深度协同。
注解的本质是接口 + 元注解 + 保留策略
Java 中所有注解都隐式继承 java.lang.annotation.Annotation 接口。定义一个注解,本质是声明一个带特定元注解的接口,比如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogExecutionTime {
String value() default "";
}
-
@Target决定它能加在哪儿(类、方法、参数…) -
@Retention决定它活多久(SOURCE编译即丢,CLASS存进.class但不加载,RUNTIME才能被反射读取) -
@Documented表示生成 Javadoc 时包含该注解
没配 @Retention(RUNTIME),Spring 就扫描不到你的自定义注解;没配 @Target({METHOD, TYPE}),IDE 就会报错不允许加在字段上。
硬核通关三步:写得准、看得见、用得活
- ✅ 写得准:别照抄模板。比如想让注解支持重复(如
@RolesAllowed("ADMIN", "USER")),必须加上@Repeatable(RolesAllowedContainer.class),并配套声明容器注解。漏掉这一步,JVM 会直接拒绝重复使用。 - ✅ 看得见:用
javap -v YourClass.class查看字节码,能看到RuntimeVisibleAnnotations属性——这才是注解真正落地的位置。Spring 启动时就是靠解析这个区域加载@Component、@RequestMapping的。 - ✅ 用得活:不要只停留在
@Autowired这种“开箱即用”。试着手写一个运行时注解处理器:- 定义注解(
@NeedAudit) - 在方法上标注
- 写个
@Aspect切面或MethodInterceptor,通过method.getAnnotation(NeedAudit.class)拦截执行逻辑(如记录操作日志、校验权限) - 配合
@Order控制多个切面执行顺序
- 定义注解(
常见硬核卡点 & 解法
- 注解属性不能是泛型、不能是数组以外的集合类型(只能是
Class>[]、String[]、enum[]等) -
@Configuration类里@Bean方法上的@Scope("prototype")是运行时生效,但@Scope本身是RUNTIME注解,由 Spring 容器解析,不是 JVM 原生行为 - Lombok 的
@Data是编译期注解(RetentionPolicy.SOURCE),靠注解处理器在编译时生成 getter/setter/toString 字节码——它不依赖反射,所以性能高,但你无法在运行时getAnnotation(Data.class)
真正掌握注解,不是背 API,而是清楚:
它在哪一刻被写入?在哪一刻被读取?谁来读?读完之后做了什么动作?
把这条链路打通,@Transactional 为什么能自动回滚、@MapperScan 怎么批量注册 Mapper 接口、@SpringBootTest 如何启动测试上下文——全都不再是黑盒。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











