Java反射是动态代理实现AOP的底层核心,缺之不可:它负责发现目标方法、调用原方法(method.invoke)、透传上下文(如注解、参数类型、返回值),整个拦截链由反射对象驱动。

Java 中反射机制是动态代理实现 AOP 的底层支撑,不是“配合使用”的关系,而是“缺它不可”——没有反射,JDK 动态代理连目标方法都调用不了。
反射在动态代理中干了三件关键事
动态代理本身不执行业务逻辑,它只是个调度员;真正干活的,是反射。
- 发现目标方法:InvocationHandler.invoke() 方法接收到 method 参数,这个 Method 对象就是通过反射从接口或类中提取出来的元数据,包含名称、参数类型、返回值、注解等
- 调用原方法:method.invoke(target, args) 这一行代码本质就是反射调用——绕过编译期绑定,在运行时定位并执行目标对象的具体方法
- 透传上下文:反射让代理能拿到完整的调用信息——比如获取 method.getAnnotation(Log.class) 判断是否需要日志,用 method.getParameterTypes() 校验参数,甚至通过 method.getReturnType() 做泛型适配
JDK 动态代理的 AOP 流程靠反射串起来
整个拦截链的生命线,是由反射对象一环扣一环驱动的:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- Proxy.newProxyInstance() 创建代理对象时,需传入 ClassLoader 和接口数组——这些 Class 对象本身就是反射入口
- 代理对象被调用时,触发 InvocationHandler.invoke(),其中 method 是反射获取的,args 是 Object[](反射通用参数容器)
- 在 invoke() 内部,你决定何时调用 method.invoke(target, args) ——这一步把控制权交还给原始逻辑,而反射确保它能准确命中
- 如果要改参数或包装返回值,还得靠反射:用 Array.newInstance() 构造新参数数组,用 method.getReturnType().cast() 安全转换结果
为什么不能只用代理不用反射?
因为 JDK 动态代理生成的代理类,本身没有硬编码任何目标方法名或签名。它只认一个通用接口:InvocationHandler。所有具体行为,都得靠反射现场查、现场调。
立即学习“Java免费学习笔记(深入)”;
- 没有反射,你就无法在运行时知道“当前被调的是 saveUser 还是 deleteUser”
- 没有反射,你就没法检查 @Transactional 注解是否存在,也没法读取 @Log(level = "DEBUG") 的配置值
- 没有反射,method.invoke 就不存在,代理就只能空转,永远调不到真实方法
注意反射带来的实际约束
反射能力强大,但动态代理+反射的组合也有明确边界,这些都源于反射本身的机制限制:
- 只能代理接口实现类:JDK Proxy 底层依赖反射生成的代理类必须继承 Proxy 并实现接口,所以目标类必须有接口;否则要用 CGLIB(基于字节码,非纯反射)
- 无法拦截 final / static / private 方法:虽然反射能访问 private 方法(setAccessible(true)),但代理机制根本不会把它们暴露给 invoke(),因为接口里没有这些方法声明
- 性能损耗真实存在:每次 method.invoke 都有反射解析开销,高频调用场景建议缓存 Method 对象,或升级到 MethodHandle(更轻量的反射替代方案)


















