SpringBoot统一返回处理出现cannot be cast to java.lang.String异常解决
作者:伏加特遇上西柚
一 问题出现背景:
在使用 @RestControllerAdvice 和实现 ResponseBodyAdvice 做 controller 层统一返回封装时。当返回字符串时会报 “cannot be cast to java.lang.String” 异常,返回其他类型就无任何问题。

二 解决方案
如果返回的是字符串直接手动封装返回对象转成json字符串返回即可。

完整代码
@RestControllerAdvice
public class ResponseResult implements ResponseBodyAdvice<Object> {
/**
* 支持注解@ResponseNotIntercept,使某些方法无需使用Result封装
*
* @param returnType 返回类型
* @param converterType 选择的转换器类型
* @return true 时会执行beforeBodyWrite方法,false时直接返回给前端
*/
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
if (returnType.getDeclaringClass().isAnnotationPresent(ResponseNotIntercept.class)) {
//若在类中加了@ResponseNotIntercept 则该类中的方法不用做统一的拦截
return false;
}
if (returnType.getMethod().isAnnotationPresent(ResponseNotIntercept.class)) {
//若方法上加了@ResponseNotIntercept 则该方法不用做统一的拦截
return false;
}
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
// 提供一定的灵活度,如果body已经被包装了,就不进行包装
return body;
}
if (body instanceof String) {
//解决返回值为字符串时,不能正常包装
return JSON.toJSONString(Result.success(body));
}
return Result.success(body);
}
}三 异常原因分析
原因:
SpringMVC 默认会注册一些自带的 HttpMessageConvertor (从先后顺序排列分别为ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter,SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter) ,后端服务使用Restful API的形式,前后端得规范一般是json格式, SpringMVC 自带 MappingJackson2HttpMessageConverter ,在依赖中引入 jackson 包后,容器会把 MappingJackson2HttpMessageConverter 自动注册到 messageConverters 链的末尾
当返回的数据是非字符串时使用的 MappingJackson2HttpMessageConverter 写入返回对象。当返回的数据是字符串时,此处得方法是要去循环遍历 HttpMessageConverter 集,因为 StringHttpMessageConverter 会先被遍历到,这时会认为 StringHttpMessageConverter 可以使用,在返回 Result 是使用 ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage) ;此方法是父类方法 body 参数类型为 Object ,实际调用的为 StringHttpMessageConverter 中的 addDefaultHeaders(HttpHeaders headers, String s, @Nullable MediaType type) 方法,使用 String 类型的 s 来接收 Result 类型的 body ,类型不匹配则出现 Result cannot be cast to java.lang.String 异常。
源码详细分析:
正常返回:
步骤一:遍历 messageConverters 去判断到 MappingJackson2HttpMessageConverter 是 GenericHttpMessageConverter 类型的 converter ;
步骤二:进一步判断到 MappingJackson2HttpMessageConverter 可以写入对象类型的数据。
步骤三:调用 beforeBodyWriter 方法将原有的 TestVO 对象数据封装到 Result 对象中。
步骤四:调用 MappingJackson2HttpMessageConverter 中的 wirte 方法(代码中用接口类型接收的)

步骤五:通过 MappingJackson2HttpMessageConverter 继承关系发现其write方法在父类 AbstractHttpMessageConverter 中,在 write 方法中调用本类中的 addDefaultHeaders 方法向输出消息添加默认报头。(此处应注意)
步骤六:将封装好的Result对象返回给前端

返回为字符串异常
步骤一:遍历 messageConverters 去判断到 StringHttpMessageConverter 是null;
步骤二:进一步判断到 StringHttpMessageConverter 可以写入String类型的数据。
步骤三:调用 beforeBodyWriter 方法将原有的 String 类型数据封装到 Result 对象中。
步骤四:调用 StringHttpMessageConverter 中的 wirte 方法(代码中用接口类型接收的)

步骤五:
调用父类 AbstractHttpMessageConverter 中的 write 方法,由于 StringHttpMessageConverter 重写了 addDefaultHeaders 方法,故 write 中调用子类中的 addDefaultHeaders 。由于父类中参数t为对象类型,对应子类中接收的s为String类型故会出现类型转换异常 Result cannot be cast to java.lang.String (此处应注意)

总结
到此这篇关于SpringBoot统一返回处理出现cannot be cast to java.lang.String异常解决的文章就介绍到这了,更多相关cannot be cast to java.lang.String异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
