登录
首页 >  文章 >  java教程

自定义Retention为RUNTIME的注解及解析方法

时间:2026-05-10 21:19:38 487浏览 收藏

本文深入解析了如何正确定义并安全使用 `@Retention(RetentionPolicy.RUNTIME)` 注解,强调这是运行时通过反射获取注解的前提条件——否则注解将在编译后被丢弃;同时系统讲解了必须搭配 `@Target` 明确作用位置、注解元素的合法类型与默认值规范、反射读取时的空值防护、继承场景下的声明类定位陷阱(如子类重写方法上添加注解却误查父类)、以及缓存优化和模块化环境下的潜在限制,帮助开发者避开从定义到解析全链路中高频且隐蔽的实战坑点。

怎么自定义Retention为RUNTIME的注解并在运行时解析

为什么 @Retention(RetentionPolicy.RUNTIME) 是前提

没有这个声明,注解在编译后就会被丢弃,运行时连 Class.getAnnotations() 都拿不到。JVM 默认只保留 SOURCECLASS 级注解,RUNTIME 是唯一能通过反射读取的级别。如果你写了注解但 getDeclaredAnnotations() 返回空数组,先检查这里。

必须搭配 @Target 明确作用位置,否则编译不通过。常见组合是:@Target({ElementType.METHOD, ElementType.TYPE}) —— 否则哪怕加了 RUNTIME,也不能用在类或方法上。

怎么定义一个带参数的 RUNTIME 注解

注解本质是接口,所有元素必须是常量类型(StringClassenum、基本类型、其他注解,或它们的一维数组)。不能有方法体,不能继承,不能有 default nullnull 不是合法默认值)。

示例:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
    String value() default "default";
    int thresholdMs() default 100;
    Class extends Throwable> ignoreException() default RuntimeException.class;
}
  • value() 是惯用名,调用时可省略键名:@LogExecutionTime("slow")
  • 数组类型写法是 String[] tags() default {},不能写 default null
  • Class 类型参数必须用 Class> 或具体泛型,不能用原始类型 Class

运行时怎么安全地获取并解析注解

反射读取注解本身很快,但频繁调用 getMethod().getAnnotation() 会有反射开销;更关键的是,注解属性访问可能抛 NullPointerException —— 比如你用 annotation.ignoreException(),而该注解没显式设置,又没设 default,就会崩。

推荐写法:

LogExecutionTime ann = method.getAnnotation(LogExecutionTime.class);
if (ann != null) {
    String label = ann.value(); // 安全:有 default 就不会 NPE
    int threshold = ann.thresholdMs();
    Class> ignoreType = ann.ignoreException(); // 注意:即使 default 是 RuntimeException.class,这里也绝不会为 null
}
  • 永远先判空再取值,getAnnotation() 在找不到时返回 null,不是空数组
  • 不要对注解对象做 toString()equals() 判等,它只是代理对象,行为不可靠
  • 如果需批量处理(如 AOP),建议缓存注解元数据,避免每次执行都反射

常见踩坑:注解生效但解析不到

最典型的情况是——你在子类方法上加了注解,却去父类的 Method 对象上调用 getAnnotation()。Java 反射的 getAnnotation() 不继承,必须从实际声明该注解的类中获取。

例如:

class Parent { void doWork() {} }
class Child extends Parent {
    @LogExecutionTime("child")
    @Override void doWork() {} // 注解在这里
}
  • 错误:Parent.class.getMethod("doWork").getAnnotation(...) → 返回 null
  • 正确:Child.class.getMethod("doWork").getAnnotation(...)
  • 若不确定声明位置,可用 method.getDeclaringClass() 确认
  • 注意:接口默认方法上的注解,要从接口类而非实现类获取

注解解析看似简单,真正难的是边界情况——继承关系、泛型擦除后 Class 参数的类型安全、重复注解(@Repeatable)的遍历顺序、还有模块系统(Java 9+)下 Module.addOpens() 对反射的限制。这些不显式踩一次,很难意识到。

终于介绍完啦!小伙伴们,这篇关于《自定义Retention为RUNTIME的注解及解析方法》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>