Java运行时注解需用@Retention(RetentionPolicy.RUNTIME)才能被反射读取,配合反射与动态代理实现事务、权限等逻辑;自定义时须同时满足定义、标记、处理三要素。
Java注解在运行时的作用,就是作为“可被反射读取的元数据”,触发动态逻辑——它本身不执行任何代码,但配合反射+代理,就能实现事务、权限校验、自动注入等关键能力。
@Retention(RetentionPolicy.RUNTIME) 才能运行时生效不是所有注解都能活到程序跑起来那一刻。只有明确声明为 RUNTIME 的注解,JVM 才会把它保留在字节码里,并加载进运行时类信息中,供 Class.getAnnotation() 或 Method.getAnnotation() 拿到。
@Retention(RetentionPolicy.RUNTIME) 是最常见错误:注解写了,反射却返回 null,查半天发现只是“贴了张便利贴,但没让它活到开大会那天”@Retention(RetentionPolicy.CLASS)(默认)只存进 .class 文件,JVM 不加载;SOURCE 连 class 都不进,编译完就丢@Transactional、MyBatis 的 @Select、JUnit5 的 @Test 全部依赖这个保留策略注解自己不会动,真正干活的是你写的处理逻辑——而主流框架几乎都走“反射扫描 + 代理拦截”这条路:
clazz.getDe
claredMethods() → method.isAnnotationPresent(MyTransactional.class)
@Transactional,实际执行的是代理对象的 invoke(),不是你原方法本身想让自己的注解在运行时起作用,光写个 @interface 不够,缺一不可:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // ← 必须有
public @interface LogExecutionTime {
String value() default "default";
}
@Target(能贴在哪)和 @Retention(RetentionPolicy.RUNTIME)(能活多久)@LogExecutionTime("user-service") public void updateUser() { ... }
@Aspect 切面,要么在启动时用反射扫描并注册回调,否则注解就是死的最容易被忽略的一点:运行时注解的价值不在“标记”,而在“谁来读、怎么用”。很多人卡在反射拿不到注解,其实问题不在注解定义,而在没确认类加载器是否一致、是否用了字节码增强工具(如 Lombok)干扰了原始类结构,或者代理层级太深导致反射拿到的是代理类而非目标类。