java反射读取注解需声明@retention(retentionpolicy.runtime),通过class/method/field的getannotation()获取;属性限于基本类型、string、class、枚举、注解及对应数组,值在编译期固化,运行期仅提取不可修改。

Java中在运行期通过反射读取并解析注解,核心是利用 Class、Method、Field 等对象的 getAnnotation() 或 getAnnotations() 方法,配合注解的 @Retention(RetentionPolicy.RUNTIME) 声明。
确保注解在运行期可用
只有声明为 RetentionPolicy.RUNTIME 的注解才能被反射读取。例如:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) // 关键:必须设为 RUNTIME
public @interface MyConfig {
String value() default "";
int timeout() default 3000;
String[] servers() default {"localhost"};
}
如果漏掉 @Retention(RetentionPolicy.RUNTIME),反射调用会始终返回 null。
在类或方法上读取注解
获取类上的注解:
MyConfig classAnn = MyClass.class.getAnnotation(MyConfig.class);
if (classAnn != null) {
System.out.println("value = " + classAnn.value());
System.out.println("timeout = " + classAnn.timeout());
System.out.println("servers = " + Arrays.toString(classAnn.servers()));
}
获取某个方法上的注解(需先拿到 Method 对象):
try {
Method method = MyClass.class.getDeclaredMethod("doSomething");
MyConfig methodAnn = method.getAnnotation(MyConfig.class);
if (methodAnn != null) {
System.out.println("Method config: " + methodAnn.value());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
批量扫描和解析多个注解
常用于框架自动装配(如 Spring 的 @Component 扫描)。可递归遍历包路径下的类,用 ClassLoader 加载类后检查注解:
- 使用
Thread.currentThread().getContextClassLoader()获取类加载器 - 将包名转为路径(如
com.example.config→/com/example/config) - 从
classpath中定位并读取.class文件字节,用defineClass或第三方库(如Reflections)加载 - 对每个加载的
Class调用isAnnotationPresent()或getAnnotation()
简化方案:引入 Reflections 库(Maven):
Reflections reflections = new Reflections("com.example",
new TypeAnnotationsScanner(),
new SubTypesScanner());
Set<class extends object>> annotatedClasses =
reflections.getTypesAnnotatedWith(MyConfig.class);
for (Class> clazz : annotatedClasses) {
MyConfig ann = clazz.getAnnotation(MyConfig.class);
System.out.println(clazz.getName() + " -> " + ann.value());
}</class>
注意注解属性的限制与动态性
注解属性只能是以下类型:基本类型、String、Class、枚举、其他注解,以及上述类型的数组。不能是任意对象或运行时计算值。
注解本身是编译期静态元数据,其值在字节码中固化。所谓“动态解析”,是指在运行期按需提取这些静态值,并结合业务逻辑做后续处理(比如根据 timeout() 启动超时监控线程),但注解内容本身不可修改。
若需真正动态配置,应将注解作为触发标记,实际参数从外部(如 application.properties、环境变量或配置中心)加载,注解仅起语义标识作用。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











