Class.getAnnotations()可获取类上运行时注解,前提是注解必须声明@Retention(RetentionPolicy.RUNTIME)且@Target包含ElementType.TYPE,否则返回空;它只返回直接声明的注解,不包含继承的。

如何用 Class.getAnnotations() 拿到类上的注解
反射读注解第一步不是找方法,而是确认目标类本身有没有被正确标注。很多问题其实卡在注解没生效——比如忘了加 @Retention(RetentionPolicy.RUNTIME),或者用了 @Target(ElementType.METHOD) 却去类上贴。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 检查注解定义里必须有
@Retention(RetentionPolicy.RUNTIME),否则运行时根本看不到 -
getAnnotations()返回所有直接声明的注解(不含继承的),要包括父类注解得用getDeclaredAnnotations()+ 手动遍历父类 - 如果注解带参数(比如
@Config(key = "timeout")),用annotation.value()或annotation.key()直接调用方法取值,别试图用反射再读字段
为什么 Method.getAnnotation() 返回 null
最常见原因是:目标方法在字节码层面被代理覆盖了,比如 Spring AOP 的 CGLIB 代理会生成子类,但子类方法不自动继承父类方法上的注解。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 优先用
Advised接口(Spring)或Proxy.getInvocationHandler()反查原始目标对象,再从原始类的方法上取注解 - 避免在代理对象上调用
getClass().getMethod(...).getAnnotation(...)—— 这拿到的是代理类的方法,不是你写的那个 - 如果必须支持代理场景,注解最好声明在类级别(
@Target(ElementType.TYPE)),比方法级更稳定
解析 @Value 和自定义配置注解的区别
@Value 是 Spring 的运行时表达式解析器驱动的,它不依赖反射读注解本身,而是靠 BeanFactory 在属性注入阶段做 EL 计算;而你自己写的 @Config 注解必须手动触发反射读取+解析逻辑。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 别想复刻
@Value的行为——它背后是PropertySourcesPlaceholderConfigurer和大量上下文钩子,不是几个getAnnotation()能搞定的 - 自定义注解解析推荐配合
BeanPostProcessor.postProcessBeforeInitialization(),在这个时机能拿到真实 bean 实例和它的 class - 如果注解值需要占位符替换(如
${db.url}),自己调用Environment.resolvePlaceholders(),别硬写字符串替换
AOP 切面里怎么安全地读目标方法的注解
在 @Around 切面里,joinPoint.getSignature() 返回的是 MethodSignature,但它封装的 Method 对象可能来自代理类,也可能来自原始类,取决于代理模式。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
((MethodSignature) joinPoint.getSignature()).getMethod()拿到的是“签名方法”,不一定可反射调用;真正要读注解,得用joinPoint.getTarget().getClass().getMethod(...) - 更稳妥的方式是:先通过
joinPoint.getStaticPart()获取Method对象(Spring 5.2+ 支持),它保证是原始类的方法引用 - 注意 JDK 动态代理返回的
Method是接口方法,没有实现类注解;CGLIB 代理返回的是子类方法,需用method.getDeclaringClass().getSuperclass()回溯
注解反射本身不难,难的是在 Spring 生命周期、代理机制、泛型擦除这些上下文里保持引用准确。漏掉一个 RetentionPolicy.RUNTIME 或错判代理类型,后面所有逻辑都跑偏。










