java注解需配合动态代理与反射才能运行时生效,必须声明@retention(retentionpolicy.runtime),jdk代理要求接口、cglib代理要求非final类和方法,spring aop据此统一实现注解增强。

Java 中注解本身不会自动执行任何逻辑,要实现在运行期解析并增强业务行为,核心是结合动态代理(如 JDK Proxy 或 CGLIB)与反射获取注解信息,在代理对象的方法调用前后插入自定义逻辑。
注解需声明为 RUNTIME 保留策略
只有 @Retention(RetentionPolicy.RUNTIME) 的注解才能在运行时通过反射读取。例如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecution {
String value() default "";
}
若注解未加 @Retention(RetentionPolicy.RUNTIME),代理中调用 method.getAnnotation(...) 会始终返回 null。
用 JDK 动态代理拦截方法并解析注解
JDK 代理要求目标类实现接口。代理逻辑中可在 InvocationHandler.invoke() 内获取被调用方法及其注解:
- 通过
method.getAnnotation(LogExecution.class)判断是否标注了目标注解 - 若有,执行前置日志、权限校验、耗时统计等增强逻辑
- 调用
method.invoke(target, args)执行原方法 - 再执行后置处理(如记录返回值、清理资源)
注意:代理对象只能拦截接口方法调用;私有方法、static 方法、final 方法无法被 JDK 代理增强。
对无接口类使用 CGLIB 代理
当目标类没有实现接口时,可用 CGLIB 创建子类代理。它通过字节码生成继承原类的子类,并重写非 final 方法:
- 重写方法前,仍可通过
MethodProxy.getSuperMethod()获取原始Method对象 - 再调用
method.getAnnotation(...)解析注解 - CGLIB 要求被代理类不能是 final,方法也不能是 final
Spring AOP 默认优先用 JDK 代理,无接口时自动降级为 CGLIB —— 其底层正是这样统一处理注解驱动增强的。
实际增强逻辑需手动编写,框架不自动生效
像 @Transactional 或 @Cacheable 这类功能,不是注解自己“工作”,而是 Spring 容器启动时扫描这些注解,注册对应 Advisor 和 Advice,再由代理机制织入。你若想自定义,就得:
- 定义注解
- 写一个
InvocationHandler或MethodInterceptor - 在创建代理时显式传入该处理器
- 确保代理对象被实际使用(不能直接 new 目标类)
跳过 Spring 等框架手写时,务必检查代理对象是否真正参与调用链 —— 常见错误是创建了代理却调用了原始对象实例。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











