桥接方法本身不引发深拷贝重复调用,但若jackson、spring beanutils等框架反射遍历时未过滤isbridge()方法,会误将桥接方法与真实setter一同执行,导致字段重复赋值或初始化逻辑触发两次。

桥接方法本身不会直接引发深拷贝或序列化过程中的“重复调用”问题,但它可能在特定组合场景下间接暴露或放大底层设计缺陷——尤其是当框架(如Jackson、Gson、Spring BeanUtils)依赖反射遍历方法时,误将桥接方法当作业务方法执行,导致字段被多次赋值、初始化逻辑被触发两次,或破坏不可变性约束。
桥接方法如何“意外参与”拷贝流程
Java泛型擦除后,编译器为保持多态会自动生成桥接方法(bridge method),例如:
```java
public class Box
private T value;
public void setValue(T value) { this.value = value; }
}
public class StringBox extends Box
@Override
public void setValue(String value) { super.setValue(value); }
}
```
编译后,StringBox 会额外生成一个桥接方法:public void setValue(Object x) { setValue((String)x); }
若序列化/拷贝框架仅按方法名+参数类型反射调用(未过滤桥接方法),就可能同时调用 setValue(String) 和 setValue(Object),造成重复设值。
哪些框架/场景容易踩这个坑
-
Jackson 2.12 之前版本:默认使用 getter/setter 反射,若类存在泛型继承且重写 setter,桥接方法可能被误识别为可调用方法,尤其在
@JsonUnwrapped或自定义SimpleModule时更易触发 -
Spring BeanUtils.copyProperties():内部通过
PropertyDescriptor获取 writeMethod,但未跳过isBridge()方法,导致桥接 setter 被执行两次(实测在 Spring 5.3.30 中仍存在该行为) -
手动基于 Method.invoke 的深拷贝工具:若遍历
getDeclaredMethods()后未调用Method.isBridge()过滤,就会把桥接方法纳入复制逻辑 - 某些 Lombok 生成的 @Builder 或 @Data 类:配合泛型父类使用时,builder 的 setter 可能触发桥接方法链,导致构造过程中字段被覆盖两次
排查与验证方法
- 在拷贝前后对目标对象打日志,观察同一字段是否被赋值两次(尤其注意集合类 add()、map.put() 等有副作用的操作)
- 调试时在关键 setter 断点,查看调用栈中是否出现形如
xxx$1.setValue(Object)或BridgeMethod字样 - 运行时检查方法属性:
method.isBridge()返回 true 即为桥接方法,应跳过处理 - 用
javap -c -s YourClass查看字节码,确认是否存在额外的 bridge 方法签名
安全实践建议
- 使用框架时优先选择明确声明支持泛型桥接过滤的版本(如 Jackson 2.14+、Spring 6.0+ 已加强 bridge 方法识别)
- 自研拷贝逻辑中,遍历方法后务必添加
!method.isBridge()条件过滤 - 避免在 setter 中放置非幂等逻辑(如修改静态计数器、发消息、更新缓存);若有,改用构造器或 builder 模式封装
- 对泛型基类的字段,考虑用
final+ 构造器注入替代 setter,从根本上规避反射调用风险
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











