Java反射读取注解需注解声明@Retention(RetentionPolicy.RUNTIME),否则运行时无法获取;须用对应API(如getAnnotation)按目标位置读取,并判空后再取值,注意继承性与类路径可见性。

Java 中通过反射读取注解内容来解析配置信息,关键不在于“能不能读”,而在于“能不能在运行时真正拿到”。核心前提是注解必须声明为 @Retention(RetentionPolicy.RUNTIME),否则反射调用一律返回 null 或空数组。
确保注解支持运行时保留
只有带 @Retention(RetentionPolicy.RUNTIME) 的注解,JVM 才会在类加载后把元数据保留在内存中,供反射访问。常见错误是漏写该元注解,或误用 CLASS(仅保留在字节码)或 SOURCE(仅编译期存在)。
- ✅ 正确写法:@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Config { String host() default "localhost"; int port() default 8080; }
- ❌ 无效写法:@Retention(RetentionPolicy.CLASS) public @interface Config { ... } —— 反射无法获取
按目标位置选择对应反射方法
注解可加在类、方法、字段等不同位置,需用对应的反射 API 获取:
- 读取类上的注解:
clazz.getAnnotation(Config.class)(单个)或clazz.getDeclaredAnnotations()(全部本类声明的) - 读取方法上的注解:
method.getAnnotation(Config.class),配合clazz.getDeclaredMethods()遍历 - 读取字段上的注解:
field.getAnnotation(Config.class),配合clazz.getDeclaredFields()遍历 - 处理可重复注解(如多个
@Role("admin")):clazz.getAnnotationsByType(Role.class),不能用getAnnotation()
安全提取属性值并做空值判断
注解对象本身是接口实例(由 JVM 动态代理生成),调用其方法即可取值,但必须先判空:
立即学习“Java免费学习笔记(深入)”;
Config config = clazz.getAnnotation(Config.class);if (config != null) { String host = config.host(); int port = config.port(); /* 使用配置 */ }- 避免直接链式调用,如
clazz.getAnnotation(Config.class).host()—— 空指针风险高
注意继承与可见性边界
默认情况下,getAnnotations() 会包含父类/接口上继承来的注解(若注解本身标注了 @Inherited 且作用于类);而 getDeclaredAnnotations() 严格只返回当前类显式声明的注解,不含继承。
- 需要严格隔离配置来源时,优先用
getDeclaredAnnotations() - 若注解类型不在 classpath 中(如模块未导出、类加载器隔离),反射会抛
TypeNotPresentException,需捕获处理 - 所有注解成员必须是 public 方法,返回类型限于基本类型、String、Class、枚举、其他注解或上述类型的数组 —— 不支持泛型集合等复杂结构


















