【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---HttpMessageConverter 消息转换器详解(下)

简介: 【小家Spring】Spring MVC容器的web九大组件之---HandlerAdapter源码详解---HttpMessageConverter 消息转换器详解(下)

FastJsonHttpMessageConverter


它和Gson和fastjson类似,只不过它内部引擎用的是Ali的FastJson库


// Fastjson for Spring MVC Converter. Compatible Spring MVC version 3.2+
// @since 1.2.10
public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object>
        implements GenericHttpMessageConverter<Object> {
    public FastJsonHttpMessageConverter() {
        super(MediaType.ALL);
    }
  // 永远返回true,表示它想支持所有的类型,所有的MediaType,现在这算一个小Bug
    @Override
    protected boolean supports(Class<?> clazz) {
        return true;
    }
  // 它竟然对泛型Type都没有任何的实现,这也是一个小bug
  // 包括读写的时候  对泛型类型都没有做很好的处理~~~
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        return super.canRead(contextClass, mediaType);
    }
    public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
        return super.canWrite(clazz, mediaType);
    }
  // 这是处理读的方法,主要依赖于JSON.parseObject这个方法解析成一个object
    private Object readType(Type type, HttpInputMessage inputMessage) {
        try {
            InputStream in = inputMessage.getBody();
            return JSON.parseObject(in,
                    fastJsonConfig.getCharset(),
                    type,
                    fastJsonConfig.getParserConfig(),
                    fastJsonConfig.getParseProcess(),
                    JSON.DEFAULT_PARSER_FEATURE,
                    fastJsonConfig.getFeatures());
        } catch (JSONException ex) {
            throw new HttpMessageNotReadableException("JSON parse error: " + ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
        }
    }
}


总体来说,如果你是FastJson的死忠粉,你可以替换掉默认的Jackson的实现方式。但是由于FastJson在效率在对标Jackson并没有多少优势,所以绝大多数情况下,我并不建议修改Spring MVC处理json的默认行为


ResourceRegionHttpMessageConverter


和org.springframework.core.io.support.ResourceRegion有关,它只能写为一个ResourceRegion或者一个它的List


只能写不能读,读方法都会抛异常~


// 这个类很简单,就是对Resource的一个包装  所以它和`application/octet-stream`也是有关的
// @since 4.3
public class ResourceRegion {
  private final Resource resource;
  private final long position;
  private final long count;
  ...
}


若你报错说ResourceRegionHttpMessageConverter类找不到,请检查你的Spring版本。因此此类@since 4.3


自定义消息转换器PropertiesHttpMessageConverter处理Properties类型数据



自定义的主要目的是加深对消息转换器的理解。此处我们仍然是通过继承AbstractHttpMessageConverter方式来扩展:


public class PropertiesHttpMessageConverter extends AbstractHttpMessageConverter<User> {
    // 用于仅仅只处理我自己自定义的指定的MediaType
    private static final MediaType DEFAULT_MEDIATYPE = MediaType.valueOf("application/properties");
    public PropertiesHttpMessageConverter() {
        super(DEFAULT_MEDIATYPE);
        setDefaultCharset(StandardCharsets.UTF_8);
    }
    // 要求入参、返回值必须是User类型我才处理
    @Override
    protected boolean supports(Class<?> clazz) {
        return clazz.isAssignableFrom(User.class);
    }
    @Override
    protected User readInternal(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        InputStream is = inputMessage.getBody();
        Properties props = new Properties();
        props.load(is);
        // user的三个属性
        String id = props.getProperty("id");
        String name = props.getProperty("name");
        String age = props.getProperty("age");
        return new User(Integer.valueOf(id), name, Integer.valueOf(age));
    }
    @Override
    protected void writeInternal(User user, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        OutputStream os = outputMessage.getBody();
        Properties properties = new Properties();
        // 属性判空此处我就不做了~~~
        properties.setProperty("id", user.getId().toString());
        properties.setProperty("name", user.getName());
        properties.setProperty("age", user.getAge().toString());
        properties.store(os, "user comments");
    }
}


其实发现,处理代码并不多。需要注意的是:此处我们只处理我们自定义的application/properties-user这一种MediaType即可,职责范围放到最小。

接下来就是要注册进Spring MVC里:


@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 因为此转换器职责已经足够单一,所以放在首位是木有问题的~
        converters.add(0, new PropertiesHttpMessageConverter());
        // 若放在末尾,将可能不会生效~~~~(比如如果Fastjson转换器 处理所有的类型的话,所以放在首位最为保险)
        //converters.add(0, new PropertiesHttpMessageConverter());
    }
}


这里需要注意的是,为了避免意外,一定要注意自定义消息转换器的注册顺序问题。至于为什么,在参考阅读的博文里已经详细解释了~~~


编写Handler处理器如下:


    @ResponseBody
    @RequestMapping(value = "/test/properties", method = RequestMethod.POST)
    public User upload(@RequestBody User user) {
        System.out.println(user);
        return user;
    }


下面可以用postman模拟访问了,就能看到如下效果


image.png


这样就大功告成了,我们自定义的消息处理器,只处理我们我们指定的MediaType、指定的Class类型,可以帮助我们实现某些个性化逻辑


Spring MVC默认注册哪些HttpMessageConverter?



说明:此处情况完全以Spring MVC版本讲解,和Spring Boot无关。


Spring 版本号为:5.1.6.RELEASE


不开启该注解:@EnableWebMvc


image.png

开启该注解:@EnableWebMvc


image.png


可以看到@EnableWebMvc注解的“威力”还是蛮大的,一下子让Spring MVC变强不少,所以一般情况下,我是建议开启它的。


当然如果是在Spring Boot环境下使用Spring MVC,到时候会再具体问题具体分析~~~

在纯Spring环境下,我是无理由建议标注@EnableWebMvc上此注解的

而且从上面可以看出,若我们classpath下有Jackson的包,那装配的就是MappingJackson2HttpMessageConverter,若没有jackson包有gson包,那装配的就是gson转换器。

小细节


  1. 如果一个Controller类里面所有方法的返回值都需要经过消息转换器,那么可以在类上面加上@ResponseBody注解或者将@Controller注解修改为@RestController注解,这样做就相当于在每个方法都加上了@ResponseBody注解了(言外之意别的方式都是不会经历消息转换器的)
  2. @ResponseBody和@RequestBody都可以处理Map类型的对象。如果不确定参数的具体字段,可以用Map接收。@ReqeustBody同样适用。(List也是木有问题的)
  3. 方法上的和类上的@ResponseBody都可以被继承
  4. 默认的xml转换器Jaxb2RootElementHttpMessageConverter需要类上有@XmlRootElement注解才能被转换(虽然很少使用但此处还是指出)

 

@Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return (AnnotationUtils.findAnnotation(clazz, XmlRootElement.class) != null && canWrite(mediaType));
    }
  1. 返回值类型可声明为基类的类型,不影响转换(比如我们返回值是Object都是木有关系的)。但参数的类型必需为特定的类型(最好不要用接口类型,当然有的时候也是可以的比如Map/List/Resource等等)。这是显而易见的


最后


请求和响应都有对应的body,而这个body就是需要关注的主要数据。


请求体与请求的查询参数或者表单参数是不同的:

请求体的表述一般就是一段字符串(当然也可能是二进制),而查询参数可以看作url的一部分,这两个是位于请求报文的不同地方

表单参数可以按照一定格式放在请求体中,也可以放在url上作为查询参数。


响应体则是浏览器渲染页面的依据,对于一个普通html页面得响应,响应体就是这个html页面的源代码。

请求体和响应体都是需要配合Content-Type头部使用的,这个头部主要用于说明body中得字符串是什么格式的,比如:text,json,xml等。


  • 对于请求报文,只有通过此头部,服务器才能知道怎么解析请求体中的字符串
  • 对于响应报文,浏览器通过此头部才知道应该怎么渲染响应结果,是直接打印字符串还是根据代码渲染为一个网页


还有一个与body有关的头部是Accept,这个头部标识了客户端期望得到什么格式的响应体。服务器可根据此字段选择合适的结果表述。


对于HttpServletRequest和HttpServletResponse,可以分别调用getInputStream和getOutputStream来直接获取body,但是获取到的仅仅只是一段字符串。

而对于Java来说,处理一个对象肯定比处理一个字符串要方便得多,也好理解得多。


所以根据Content-Type头部,将body字符串转换为java对象是常有的事。反过来,根据Accept头部,将java对象转换客户端期望格式的字符串也是必不可少的工作。


因此本文讲述的消息转换器HttpMessageConverter就是专门来实现请求体/响应体到Java对象之间的转换的,具有非常重要的意义


相关文章
|
9月前
|
Web App开发 前端开发 JavaScript
前端新利器:CSS容器查询——让组件真正“自适应
前端新利器:CSS容器查询——让组件真正“自适应
518 83
|
6月前
|
缓存 安全 Java
《深入理解Spring》过滤器(Filter)——Web请求的第一道防线
Servlet过滤器是Java Web核心组件,可在请求进入容器时进行预处理与响应后处理,适用于日志、认证、安全、跨域等全局性功能,具有比Spring拦截器更早的执行时机和更广的覆盖范围。
|
前端开发 API 开发者
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
本文由黑臂麒麟(6年前端经验)撰写,介绍ArkTS开发中的常用基础组件与布局组件。基础组件包括Text、Image、Button等,支持样式设置如字体颜色、大小和加粗等,并可通过Resource资源引用统一管理样式。布局组件涵盖Column、Row、List、Grid和Tabs等,支持灵活的主轴与交叉轴对齐方式、分割线设置及滚动事件监听。同时,Tabs组件可实现自定义样式与页签切换功能。内容结合代码示例,适合初学者快速上手ArkTS开发。参考华为开发者联盟官网基础课程。
1181 75
harmonyOS基础- 快速弄懂HarmonyOS ArkTs基础组件、布局容器(前端视角篇)
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
669 70
|
8月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
1096 0
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
602 7
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
339 0
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
732 0
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
443 0