JDK动态代理不自动识别桥接方法,其派发逻辑完全依赖InvocationHandler中method.invoke(target, args)的调用;JVM根据target实际类型、method签名及解析规则决定是否执行桥接方法,代理本身仅忠实转发。

JDK 动态代理本身不直接处理协变返回类型或桥接方法的派发逻辑,它只是按接口声明的方法签名进行反射调用。真正决定“是否调用桥接方法”的,是 JVM 的方法解析规则和代理类生成时对目标方法的选取方式。
关键点在于:动态代理的派发行为完全取决于你传入的 InvocationHandler 中如何调用目标对象的方法,而不是代理机制自动识别桥接方法。
为什么代理不会“自动选中”桥接方法?
JDK 动态代理生成的 $ProxyN 类,其每个接口方法的实现都长这样(伪代码):
public Integer action(String s) {
return (Integer) h.invoke(this, m_action, new Object[]{s});
}其中 m_action 是通过 Method 对象缓存的——而这个 Method 对象是在运行时通过 Class.getMethod("action", String.class) 获取的。
JVM 在查找 getMethod 时,遵循的是重载解析 + 返回类型匹配规则(JLS §15.12),但注意:
-
getMethod默认只按方法名 + 参数类型匹配,忽略返回类型; - 如果存在多个同名同参但返回类型不同的方法(如
Integer action()和桥接的Object action()),getMethod只会返回其中一个(通常是更具体的那个,即Integer action()),除非你显式用getDeclaredMethod并指定返回类型。
所以:
✅ 代理生成时拿到的 Method 很大概率是子类的特化方法(如 Integer get());
❌ 它不会主动去调用桥接方法,因为桥接方法是编译器生成的合成方法,通常不参与常规反射查找。
那什么时候会走到桥接方法?
只有当调用方持有的是父类型引用(如泛型接口擦除后的原始类型),且该引用被用于反射调用时,才可能触发桥接方法。
立即学习“Java免费学习笔记(深入)”;
例如:
Box<String> box = new StringBox(); // 通过接口变量调用 → 编译后字节码调用的是 Object get() Object obj = box.get(); // 实际执行桥接方法 public Object get() // 但如果用 StringBox 引用: StringBox sb = new StringBox(); String s = sb.get(); // 直接调用 public String get(),不走桥接
在 JDK 动态代理中,如果你的 InvocationHandler 内部是这样写的:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ❌ 错误:硬编码调用目标对象的某个具体方法
if ("get".equals(method.getName())) {
return target.getStringValue(); // 绕过 method.invoke,可能漏掉桥接语义
}
// ✅ 正确:委托给目标对象,让 JVM 自行决定调哪个重载/桥接版本
return method.invoke(target, args);
}只要 method.invoke(target, args) 被调用,JVM 就会根据 target 的实际类、method 的签名(含返回类型)、以及目标类中是否存在匹配方法,来决定最终执行哪个方法——包括桥接方法。
也就是说:
➡️ 桥接方法是否被调用,取决于 target 对象的实际类型 + method 对象的签名 + JVM 运行时解析逻辑;
➡️ 动态代理本身不干预这个过程,它只是把 method 和 args 原样转交。
如何确保协变返回在代理中不失效?
不要在 InvocationHandler 中手动 cast 或绕过 method.invoke
避免写((StringBox) target).get()这类强类型调用,否则会跳过桥接机制。确保 method 对象是从目标接口获取的,而非实现类
例如,用Box.class.getMethod("get")获取,而不是StringBox.class.getMethod("get")。前者返回的是擦除后的Object get()方法(即桥接方法签名),后者返回String get()。框架内省时需过滤桥接方法(如 Spring BeanUtils)
若你在代理中做属性读写(比如封装成 Map),要用!method.isBridge()排除桥接方法,避免重复暴露 getter。-
泛型接口代理时,返回值转型由调用方负责
Box<String> proxy = (Box<String>) Proxy.newProxyInstance(...); String s = proxy.get(); // 编译期认为返回 String,运行时靠桥接+委托保证安全
这里类型安全由编译器保障,JVM 层面仍是
Object get()→String get()委托链。
小结
- JDK 动态代理不“理解”协变或桥接,它只忠实转发
Method.invoke; - 桥接方法是否生效,取决于
target类是否生成了它、method是否匹配其签名、以及invoke调用路径是否经过 JVM 方法解析; - 正确做法是:让
InvocationHandler无差别调用method.invoke(target, args),把多态和桥接决策交给 JVM; - 开发者要避免在代理逻辑中破坏类型契约(如硬编码子类方法、忽略泛型擦除影响)。
不复杂但容易忽略。


















