Spring Boot 2:proxyTargetClass=true配置疑似失效,代理实体无法转换
我之前在Spring Boot + JPA项目里也踩过这个坑,太懂这种明明配置了proxyTargetClass=true却完全没用的挫败感了!先给你理清楚问题根源和解决办法:
为什么proxyTargetClass=true没效果?
首先得搞明白:这个配置是给Spring AOP用的,和Hibernate的实体代理半毛钱关系都没有!
Hibernate为了实现延迟加载、关联实体懒加载等特性,会自己生成实体的代理类——不管你Spring AOP怎么配置,Hibernate的代理逻辑都是独立的。如果你的实体实现了某个接口,Hibernate默认会生成基于接口的代理(而非继承实体类的CGLIB风格代理),这时候你强行把代理对象转成具体实体类,自然会报类型转换错误。
两种靠谱的解决办法
1. 修正equals()方法的实现,适配代理对象
不要直接强转,而是用Hibernate提供的工具方法获取对象的真实类型,再做比较。示例代码如下:
import org.hibernate.Hibernate; import java.util.Objects; @Override public boolean equals(Object o) { // 先判断是否是同一个对象 if (this == o) return true; // 判断是否为null if (o == null) return false; // 获取真实的类对象(绕过Hibernate代理) Class<?> actualClass = Hibernate.getClass(this); Class<?> otherClass = Hibernate.getClass(o); // 类类型不一致直接返回false if (!actualClass.equals(otherClass)) return false; // 这里强转就安全了 YourEntity that = (YourEntity) o; // 建议用主键(比如id)作为equals判断的核心依据 return Objects.equals(getId(), that.getId()); } @Override public int hashCode() { // hashCode也建议用主键生成,避免代理对象影响 return Objects.hash(getId()); }
这样不管对象是真实实体还是Hibernate代理,都能正确获取类型并完成比较。
2. 开启Hibernate字节码增强,彻底避免代理生成
如果你不想改代码,也可以让Hibernate直接修改实体类的字节码来实现懒加载等特性,而不是生成代理类。在Spring Boot的application.properties或application.yml里添加配置:
spring.jpa.hibernate.use_bytecode_enhancement=true
开启后,Hibernate会在编译期增强实体类的字节码,运行时就不会生成代理对象了,你的原有equals()强转逻辑就能正常工作。
额外提醒
尽量不要用instanceof来判断类型(除非你的实体没有子类),因为如果有继承关系,instanceof会返回true,但不同子类的实体应该是不相等的。用Hibernate.getClass()能准确获取对象的真实类型,避免这种问题。
内容的提问来源于stack exchange,提问作者Jim Ott

