八、响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析

响应处理——ReturnValueHandler匹配返回值处理器并处理返回值原理解析

  前边已经分析了请求处理中从映射到获取参数值等底层原理,接下来的几篇将学习一下响应处理的相关过程

  这一片学习Spring MVC如何快速返回一个JSON数据

  首先我们的项目已经引入了 web 场景,web 场景中已经自动引入了 json 相关依赖

  现在先写一个简单的controller来测试一下

	//利用返回值处理器里面的消息转换器进行处理
	@ResponseBody  
    @GetMapping(value = "/test/person")
    public Person getPerson(){
        Person person = new Person();
        person.setAge(28);
        person.setBirth(new Date());
        person.setUserName("zhangsan");
        return person;
    }

在这里插入图片描述
  接下来老套路,debug探索一下它的原理

  直接跳过前边请求处理部分,来到真正处理请求的 RequestMappingHandlerAdapter.classinvokeHandlerMethod() 方法

在这里插入图片描述
在前边处理请求参数的时候,有一个参数解析器,同样,在处理返回值时,也有一个 返回值处理器
在这里插入图片描述
在方法返回之前,我们的返回值处理器已经准备好了,只等我们返回值时拿这些处理器来处理

下一步代码到 ServletInvocableHandlerMethod.classinvokeAndHandle 方法
在这里插入图片描述
以前我们是进到 invokeForRequest 方法看它怎么处理请求,现在就直接跳过往下走了

在这里插入图片描述
handleReturnValue 方法传入了我们的返回值对象,还有返回值类型,接下来,就在这个方法里边,利用对应的返回值处理器,帮我们处理返回值

在这里插入图片描述
我们前边的controller标注了 @ResponseBody , RequestResponseBodyMethodProcessor 处理器可以处理 @ResponseBody ,所以拿到了它

在这里插入图片描述
找到了处理器,接下来就看它是怎样处理的

step into 进入 RequestResponseBodyMethodProcessor.classhandleReturnValue 方法

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        mavContainer.setRequestHandled(true);
        //包装原生request、response
        ServletServerHttpRequest inputMessage = this.createInputMessage(webRequest);
        ServletServerHttpResponse outputMessage = this.createOutputMessage(webRequest);
        //传入返回值,返回值类型,以及请求响应,使用消息转换器进行写出操作
        this.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }

进入 writeWithMessageConverters 方法,下边要进行的操作叫做 内容协商 ,浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型,服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据

//省略前边一些判断代码,直接来到内容协商
		MediaType selectedMediaType = null;
		//先看响应头中有没有内容类型
        MediaType contentType = outputMessage.getHeaders().getContentType();
        boolean isContentTypePreset = contentType != null && contentType.isConcrete();
        if (isContentTypePreset) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Found 'Content-Type:" + contentType + "' in response");
            }
			//如果已经有了,就直接用
            selectedMediaType = contentType;
        } else {
        	//如果没有,就拿到原生 request 对象,
            HttpServletRequest request = inputMessage.getServletRequest();
            //获取能接受的内容类型,就是响应头中Accept的内容
            List<MediaType> acceptableTypes = this.getAcceptableMediaTypes(request);
            //最终能生成的内容类型
            List<MediaType> producibleTypes = this.getProducibleMediaTypes(request, valueType, (Type)targetType);
            if (body != null && producibleTypes.isEmpty()) {
                throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);
            }

            List<MediaType> mediaTypesToUse = new ArrayList();
            Iterator var15 = acceptableTypes.iterator();
			//服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据
            MediaType mediaType;
            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                Iterator var17 = producibleTypes.iterator();

                while(var17.hasNext()) {
                    MediaType producibleType = (MediaType)var17.next();
                    if (mediaType.isCompatibleWith(producibleType)) {
                        mediaTypesToUse.add(this.getMostSpecificMediaType(mediaType, producibleType));
                    }
                }
            }

		if (mediaTypesToUse.isEmpty()) {
                if (body != null) {
                    throw new HttpMediaTypeNotAcceptableException(producibleTypes);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
                }

                return;
            }

            MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
            var15 = mediaTypesToUse.iterator();

            while(var15.hasNext()) {
                mediaType = (MediaType)var15.next();
                if (mediaType.isConcrete()) {
                    selectedMediaType = mediaType;
                    break;
                }

经过上边的代码,就拿到了内容类型

拿到了内容类型之后,SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?

在这里插入图片描述
循环判断默认的MessageConverter看哪个能干活
在这里插入图片描述
找到了 MappingJackson2HttpMessageConverter ,它的supports方法直接返回true,啥都支持
在这里插入图片描述
在这里插入图片描述
最终,利用MappingJackson2HttpMessageConverter将对象转为json再写出去
在这里插入图片描述
在这里插入图片描述
总结

1. 返回值处理器判断是否支持这种类型返回值 supportsReturnType

2. 返回值处理器调用 handleReturnValue 进行处理

3. RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。

	利用 MessageConverters 进行处理 将数据写为json
	
		①内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
		
		②服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
		
		③SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
		
			得到MappingJackson2HttpMessageConverter可以将对象写为json
			
			利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

OVER(∩_∩)O~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anton丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值