Java自定义注解必须声明@Retention(RUNTIME)才能被反射读取,否则JVM不加载;需配合@Target限定作用位置,@Inherited仅对类生效;反射应优先用getDeclaredAnnotations()并借助AnnotationUtils.findAnnotation()处理继承与桥接;切面逻辑须置于代理入口(如HandlerInterceptor),避免this调用绕过代理。

Java自定义注解必须声明 @Retention(RUNTIME)
反射读不到注解,八成是忘了设保留策略。@Retention 不写或写成 CLASS / SOURCE,运行时就查不到——因为 JVM 根本不加载它。
实操建议:
立即学习“Java免费学习笔记(深入)”;
-
@Retention(RetentionPolicy.RUNTIME)是硬性前提,缺一不可 - 顺手加上
@Target({ElementType.METHOD, ElementType.TYPE}),明确能用在哪,避免误用 - 如果注解要被子类继承,加
@Inherited;但注意它只对CLASS级生效,且不传递到方法上
反射获取注解时别漏掉 getDeclaredAnnotations()
getAnnotations() 只返回当前元素「显式声明」的注解,父类/接口里的不会出现;而 AOP 场景常需要拦截继承来的方法,比如 Controller 层统一加日志。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 优先用
method.getDeclaringClass().getDeclaredAnnotations()查类级注解 - 查方法注解用
method.getDeclaredAnnotations(),不是getAnnotations() - 若需兼容继承链,得手动遍历
method.getDeclaringClass().getSuperclass()和getInterfaces(),再调getDeclaredMethod()重取
用 AnnotationUtils.findAnnotation() 替代手写查找逻辑
Spring 的 AnnotationUtils 封装了继承、桥接方法、泛型擦除等边界情况。自己写 isAnnotationPresent() + 循环向上找,容易漏掉 @AliasFor 或重复注解(@Repeatable)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 引入
spring-core后直接用AnnotationUtils.findAnnotation(method, YourAnno.class) - 它自动处理桥接方法(如 Lambda 生成的 synthetic 方法)、接口默认方法、以及嵌套注解中的别名字段
- 不用自己判断
isSynthetic()或跳过java.lang.*注解,省掉一堆 if
切面执行时注意代理对象类型和 this 调用陷阱
用反射+注解模拟 AOP,最容易踩的是:在目标方法内部调用 this.xxx(),结果注解逻辑没触发——因为没走代理,反射检查根本没被执行。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 反射检查必须放在「真正入口」处,比如 Spring MVC 的
HandlerInterceptor.preHandle,或自定义的代理包装器 - 不要在业务方法里靠
ThreadLocal或静态缓存“事后补救”,时机不可控 - 如果非要手动触发,用
AopContext.currentProxy()(需开启exposeProxy=true),但这是权宜之计,结构已偏离初衷
注解本身不执行逻辑,反射只是“发现”它;真正的切面行为必须插在调用链的可控节点上。这点比语法细节更关键,也最容易被忽略。










