Java反射机制不直接拦截方法调用或参与override路由,而是通过Class对象读取并强行调用已存在方法;真正的动态绑定由JVM虚方法表和invokevirtual指令实现,反射仅提供元数据支撑,拦截需依赖动态代理(如JDK Proxy或CGLIB)与反射协同完成。

Java反射机制本身不直接“拦截”方法调用,也不参与方法覆盖(override)的动态路由决策;真正负责运行期方法路由的是JVM的虚方法表(vtable)机制。反射只是在运行时**读取、发现、甚至强行调用**已存在的方法——包括被子类覆盖后的方法,但它不改变JVM原有的多态分派逻辑。
反射不替代多态,但能绕过访问限制调用覆盖后的方法
当子类覆盖父类方法后,JVM在运行期根据对象实际类型(而非引用类型)决定调用哪个版本,这是动态绑定(dynamic dispatch),由字节码中的invokevirtual指令驱动。反射并不介入这一过程,但它可以:
- 通过
Class.getDeclaredMethod("methodName", ...)获取子类中重写后的方法对象(Method),哪怕该方法是private或protected; - 用
method.invoke(obj, args)显式触发该方法,此时obj的实际类型决定了最终执行哪个覆盖版本; - 若传入的是父类引用指向子类实例(如
Parent p = new Child();),反射调用p.getClass().getMethod("foo")拿到的仍是Child.foo(),因为getClass()返回的是运行时真实类。
真正的“动态拦截”靠的是代理+反射组合,不是纯反射
单纯使用Class和Method无法在每次方法调用前自动插入逻辑(比如打日志、校验)。要实现拦截,必须借助动态代理:
-
JDK动态代理:基于接口,内部使用
InvocationHandler,其invoke()方法会在代理对象调用任意接口方法时被触发——这里才是真正的“拦截点”; - CGLIB代理:针对类,通过字节码生成子类并重写方法,在方法入口插入拦截逻辑;
- 两者都依赖反射获取目标方法签名与参数,但拦截行为由代理框架控制,反射只提供元数据支撑。
覆盖方法在反射中的可见性与调用逻辑
子类覆盖父类方法后,反射获取方式直接影响调用结果:
立即学习“Java免费学习笔记(深入)”;
-
getDeclaredMethod():只查当前类声明的方法(含private),不会向上查找父类——所以childClass.getDeclaredMethod("foo")返回的是Child.foo(); -
getMethod():只查public方法,且会沿继承链向上查找,但返回的是**最具体子类中覆盖后的public版本**(前提是该方法在子类中仍是public); - 调用时传入的对象实例类型决定最终执行体——反射只是“发起者”,JVM仍按原有虚方法机制路由。
一个典型场景:框架如何结合反射与代理做统一拦截
Spring AOP、MyBatis等框架的拦截本质是:
- 用反射扫描目标类所有方法,识别带
@Transactional或@Select等注解的方法; - 为这些方法生成代理(JDK或CGLIB),并在
InvocationHandler.invoke()中插入增强逻辑; - 当业务代码调用
service.doSomething(),实际执行的是代理对象的方法,而代理内部再通过反射调用原始目标方法(可能已是子类覆盖版); - 整个链路中,反射用于发现和准备,代理负责拦截时机,JVM负责最终的方法路由。



















