Spring Cache(Redis实现)序列化带@Validated的配置类问题
解决Spring Cache序列化含@Validated的ConfigurationProperties类问题
这个问题我之前也碰到过,核心原因是**@Validated注解会让Spring为MyProps生成一个AOP代理对象**(通常是CGLIB代理),而这个代理类默认没有正确实现Serializable接口,或者在序列化/反序列化过程中,代理的额外逻辑会导致失败——毕竟你的ClassThatNeedsToBeSerialized持有这个代理后的MyProps实例,序列化它的时候自然会出问题。
下面给你几个可行的解决方案,按推荐程度排序:
方案一:移除类上的@Validated,改用手动校验
这是最稳妥的方式,避免代理生成带来的序列化问题。我们可以在启动时手动绑定并校验MyProps,而不是依赖@Validated的AOP增强:
@Configuration public class MyPropsConfig { @Bean @ConfigurationProperties(prefix = "myprops") public MyProps myProps(Validator validator) { MyProps props = new MyProps(); // 手动触发校验 Set<ConstraintViolation<MyProps>> violations = validator.validate(props); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } return props; } }
然后修改MyProps类,去掉@Validated:
public class MyProps implements Serializable { // 你的属性和校验注解(比如@NotNull、@Size等)保留 }
这样MyProps不会被代理,序列化时完全是普通的Serializable对象,不会有任何问题。
方案二:让代理对象支持序列化
如果你一定要保留@Validated在类上,可以通过自定义序列化逻辑,让序列化时使用原始的MyProps对象而非代理。在MyProps类中添加以下方法:
@ConfigurationProperties(prefix = "myprops") @Validated public class MyProps implements Serializable { // 你的属性和校验注解 // 序列化时替换为原始目标对象 private Object writeReplace() throws ObjectStreamException { if (this instanceof Advised) { try { // 获取代理背后的真实MyProps实例 return ((Advised) this).getTargetSource().getTarget(); } catch (Exception e) { throw new StreamCorruptedException("Failed to unwrap proxy"); } } return this; } }
这个方法利用了Java序列化的writeReplace()机制,在序列化前把代理对象替换成真实的MyProps实例,反序列化时就能正常恢复了。
方案三:改用JDK动态代理(仅适用于MyProps是接口的情况)
如果MyProps是一个接口而非类,Spring会使用JDK动态代理(而非CGLIB)。JDK动态代理生成的类默认实现了Serializable,但你需要确保接口本身继承Serializable:
public interface MyProps extends Serializable { // 你的属性方法 } @ConfigurationProperties(prefix = "myprops") @Validated public class MyPropsImpl implements MyProps { // 实现类逻辑 }
不过这种方式只适用于你能把MyProps改成接口的场景,灵活性不如前两种方案。
内容的提问来源于stack exchange,提问作者Robin-Hoodie
相关产品推荐
相关产品推荐

