SpringBoot统一返回处理出现cannot be cast to java.lang.String异常解决

 更新时间:2023年09月01日 10:35:49   作者:伏加特遇上西柚  
这篇文章主要给大家介绍了关于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异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用TraceId在Spring Cloud中实现线上问题快速定位

    使用TraceId在Spring Cloud中实现线上问题快速定位

    在微服务架构中,服务间的互相调用使得问题定位变得复杂,在此背景下,TraceId为我们提供了一个在复杂环境中追踪请求路径和定位问题的工具,本文不仅介绍TraceId的基本概念,还将结合真实场景,为您展示如何在Spring Cloud中应用它
    2023-09-09
  • springboot应用服务启动事件的监听实现

    springboot应用服务启动事件的监听实现

    本文主要介绍了springboot应用服务启动事件的监听实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • elasticsearch索引index之engine读写控制结构实现

    elasticsearch索引index之engine读写控制结构实现

    这篇文章主要为大家介绍了elasticsearch索引index之engine读写控制结构实现,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • SpringBoot使用Feign调用其他服务接口

    SpringBoot使用Feign调用其他服务接口

    这篇文章主要介绍了SpringBoot使用Feign调用其他服务接口,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 5个JAVA入门必看的经典实例

    5个JAVA入门必看的经典实例

    这篇文章主要为大家详细介绍了5个JAVA入门必看的经典实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • Java 线程的生命周期完整实例分析

    Java 线程的生命周期完整实例分析

    这篇文章主要介绍了Java 线程的生命周期,结合完整实例形式分析了java线程周期相关的加锁、释放锁、阻塞、同步等原理与操作技巧,需要的朋友可以参考下
    2019-10-10
  • Java集合Map常见问题_动力节点Java学院整理

    Java集合Map常见问题_动力节点Java学院整理

    这篇文章主要为大家详细整理了Java集合Map常见问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • 将RestTemplate的编码格式改为UTF-8,防止乱码问题

    将RestTemplate的编码格式改为UTF-8,防止乱码问题

    这篇文章主要介绍了将RestTemplate的编码格式改为UTF-8,防止乱码问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • SpringBoot + Shiro前后端分离权限

    SpringBoot + Shiro前后端分离权限

    这篇文章主要为大家详细介绍了SpringBoot + Shiro前后端分离权限,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • Java8中新特性Optional、接口中默认方法和静态方法详解

    Java8中新特性Optional、接口中默认方法和静态方法详解

    Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级。下面这篇文章主要给大家介绍了关于Java8中新特性Optional、接口中默认方法和静态方法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-12-12

最新评论