Java定义可反射读取的自定义注解必须加@Retention(RetentionPolicy.RUNTIME),否则getAnnotation()返回null;读取时应使用getMethod()而非getDeclaredMethod(),并判空避免NPE。

Java 里怎么定义一个可被反射读取的自定义注解
必须用 @Retention(RetentionPolicy.RUNTIME),否则反射时拿不到。缺了这句,getAnnotation() 永远返回 null。
常见错误是只加了 @Target 就以为够了,结果运行时死活读不到值。
-
@Target(ElementType.METHOD)或ElementType.TYPE等,标明能用在哪种元素上 -
@Retention(RetentionPolicy.RUNTIME)是硬性要求,没它就白写 - 推荐加上
@Documented(方便 Javadoc 生成)和@Inherited(仅对类注解生效,子类是否继承)
示例:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
String value() default "";
boolean showArgs() default false;
}
怎么用反射读取方法上的注解值
别直接调 method.getAnnotation(LogExecutionTime.class) 就完事——得先确认方法确实被标注了,否则 NPE 风险高。
更关键的是:如果目标方法在父类或接口中,而你只查当前类的声明方法(getDeclaredMethod),会漏掉继承来的注解。
- 用
getMethod()而非getDeclaredMethod(),才能跨类层次查找 - 拿到注解对象后,再调
annotation.value()、annotation.showArgs()读具体字段 - 注意:注解字段不能为
null,未设默认值又没显式赋值会编译报错
示例(安全读取):
Method method = target.getClass().getMethod("doWork");
LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class);
if (annotation != null) {
System.out.println(annotation.value());
System.out.println(annotation.showArgs());
}
为什么 getAnnotations() 返回空数组但 getAnnotation() 能拿到
这是初学者高频困惑点:getAnnotations() 返回所有「显式声明」的注解(不含继承),而 getAnnotation(Class) 内部做了继承查找(对类有效,对方法/字段无效)。
重点:方法上的注解不支持继承(@Inherited 对 METHOD 无效),所以 getAnnotations() 查父类方法注解一定为空。
- 若想批量检查多个注解,仍需逐个调
getAnnotation(X.class) - 类上注解可用
getAnnotations()批量获取,但前提是它们都在当前类声明,不是从父类继承来的 - 没有“通用版 getInheritedAnnotations()”API,要模拟继承需手动遍历
getSuperclass()和getInterfaces()
Spring AOP 里用自定义注解时为啥反射读不到
因为 Spring AOP 默认代理目标对象,实际执行的是代理对象的方法——你反射查的是原始类,但运行时调用的是代理类(比如 $Proxy23),而代理类上没保留原注解。
这不是反射失效,是对象错了。解决方案只有两个:要么换 CGLIB 代理(需关闭接口代理),要么改用 Spring 的 Advised 接口提取目标对象再反射。
- 开启 CGLIB:
@EnableAspectJAutoProxy(proxyTargetClass = true) - 或运行时通过
AopContext.currentProxy()获取代理对象,再用targetSource.getTarget()拿到真实对象 - 更稳妥的做法:在切面里用
JoinPoint.getSignature()获取MethodSignature,再调getMethod()反射读注解——这个Method是原始方法,注解还在
绕开代理问题最轻量的方式就是别在代理对象上反射,而是从 JoinPoint 入手。










