Spring Boot 1.5升级至2.0时Ehcache抛出NotSerializableException问题
核心原因
你遇到的java.io.NotSerializableException: org.springframework.integration.support.MessageBuilder异常,本质是Ehcache在开启copyOnRead或copyOnWrite时,要求缓存对象必须实现Serializable接口,而MessageBuilder本身并没有实现该接口。
在Spring Boot 1.5中,HTTP网关的handler返回的是已构建完成的GenericMessage(它实现了Serializable),所以缓存逻辑正常;但升级到Spring Boot 2.0后,你的通知链顺序和处理逻辑出现了问题:缓存拦截器提前拦截了目标方法的返回值——也就是未完成构建的MessageBuilder,而非经过转换后的可序列化GenericMessage。
看你的XML通知链配置:
<int-http:request-handler-advice-chain> <ref bean="genericXMLMessageAdvice" /> <ref bean="cacheAdvice" /> </int-http:request-handler-advice-chain>
Spring Integration的通知链执行顺序是先执行链中靠前的advice,再通过invocation.proceed()调用后续逻辑。这意味着cacheAdvice会先拦截目标方法的返回值(此时是MessageBuilder),而genericXMLMessageAdvice的转换逻辑还未执行,最终导致不可序列化的MessageBuilder被存入缓存。
解决方案
方案1:调整通知链顺序(推荐)
把cacheAdvice放在genericXMLMessageAdvice之前,确保缓存拦截器处理的是经过转换后的GenericMessage:
<int-http:request-handler-advice-chain> <ref bean="cacheAdvice" /> <ref bean="genericXMLMessageAdvice" /> </int-http:request-handler-advice-chain>
调整后,执行流程变为:
cacheAdvice启动拦截- 调用
invocation.proceed()进入genericXMLMessageAdvice genericXMLMessageAdvice将目标方法返回的MessageBuilder转换为GenericMessagecacheAdvice拿到可序列化的GenericMessage并完成缓存
方案2:直接在缓存拦截器中完成转换
如果不想调整通知链顺序,可以修改cacheAdvice的逻辑,在缓存前手动将MessageBuilder转换为GenericMessage:
@Bean public MethodInterceptor cacheAdvice() { CacheInterceptor interceptor = createGatewayInterceptor(TWENTY_FOUR_HOUR_CACHE); // 包装原有拦截逻辑,先转换再缓存 return invocation -> { Object result = invocation.proceed(); // 处理MessageBuilder,转换为可序列化的GenericMessage if (result instanceof MessageBuilder) { result = ((MessageBuilder<?>) result).build(); } // 调用原有缓存逻辑 MethodInvocation proxyInvocation = new MethodInvocation() { @Override public Object proceed() throws Throwable { return result; } // 实现其他MethodInvocation方法,复用原有参数和方法信息 @Override public Object getThis() { return invocation.getThis(); } @Override public Method getMethod() { return invocation.getMethod(); } @Override public Object[] getArguments() { return invocation.getArguments(); } }; return interceptor.invoke(proxyInvocation); }; }
方案3:关闭Ehcache的copyOnRead/copyOnWrite(不推荐)
如果不需要Ehcache的复制保证功能,可以修改Ehcache配置文件,移除copyOnRead和copyOnWrite属性。但此方案会牺牲缓存数据的线程安全性和一致性,仅作为临时应急方案。
验证
修改后重启应用,通过断点或日志确认缓存的对象类型为GenericMessage,此时NotSerializableException应该会消失,缓存逻辑恢复正常。
内容的提问来源于stack exchange,提问作者user5758361

