当前位置: 首页 > news >正文

【原创】遨游springmvc之HandlerMethodReturnValueHandler

为什么80%的码农都做不了架构师?>>>   hot3.png

1.前言

在springmvc中,很多人都知道@ResponseBody,加了它那就会返回对应的数据结果(json),而不是一张jsp或者其他视图。如果不加,那么它就返回了一个具体的视图,如jsp。那么让我们来深入探析下这里面的究竟。

在前一篇我们主要讲述了springmvc对传入参数的接口HandlerMethodArgumentResolver。那么springmvc同样也也提供了一系列对响应返回值进行处理的接口,核心接口类就是本篇要介绍的HandlerMethodReturnValueHandler。

2.原理

2.1 接口说明

public interface HandlerMethodReturnValueHandler {

	/**
	 * Whether the given {@linkplain MethodParameter method return type} is
	 * supported by this handler.
	 * @param returnType the method return type to check
	 * @return {@code true} if this handler supports the supplied return type;
	 * {@code false} otherwise
	 */
	boolean supportsReturnType(MethodParameter returnType);

	/**
	 * Handle the given return value by adding attributes to the model and
	 * setting a view or setting the
	 * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true}
	 * to indicate the response has been handled directly.
	 * @param returnValue the value returned from the handler method
	 * @param returnType the type of the return value. This type must have
	 * previously been passed to {@link #supportsReturnType} which must
	 * have returned {@code true}.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @throws Exception if the return value handling results in an error
	 */
	void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}

HandlerMethodReturnValueHandler是在spring3.1之后引入的

它主要包含2个方法:

1.supportsReturnType()决定了哪类类型的返回值将启用该返回值处理器

2.handleReturnValue则是主要处理返回值的处理逻辑,并且将处理好的值返回给model,还可以处理该返回什么视图

 

2.2 依赖

HandlerMethodReturnValueHandler的实现大多类都是已ReturnValueHandler或者Processor(包含了参数处理)结尾

 

2.3 源码分析

2.3.1 初始化HandlerMethodReturnValueHandler

在初始化RequestMappingHandlerAdapter时候,springmvc默认初始化了一系列返回值处理器,并且提供了自动以的HandlerMethodReturnValueHandler的入口。

	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

		// Single-purpose return value types
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		handlers.add(new ModelMethodProcessor());
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		handlers.add(new HttpHeadersReturnValueHandler());
		handlers.add(new CallableMethodReturnValueHandler());
		handlers.add(new DeferredResultMethodReturnValueHandler());
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		// Annotation-based return value types
		handlers.add(new ModelAttributeMethodProcessor(false));
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		// Multi-purpose return value types
		handlers.add(new ViewNameMethodReturnValueHandler());
		handlers.add(new MapMethodProcessor());

		// Custom return value types 加入自定义的返回值处理器
		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		// Catch-all
		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

 

HandlerMethodReturnValueHandler的匹配是按照初始化的顺序,请看下面table罗列的处理器和对应的处理类型。

处理器处理类型
针对一中类型
ModelAndViewMethodReturnValueHandlerModelAndView
ModelMethodProcessorModel
ViewMethodReturnValueHandlerView
ResponseBodyEmitterReturnValueHandlerResponseEntity<ResponseBodyEmitter>
StreamingResponseBodyReturnValueHandlerResponseEntity<StreamingResponseBody>
HttpHeadersReturnValueHandlerHttpHeaders
 CallableMethodReturnValueHandler Callable
DeferredResultMethodReturnValueHandlerDeferredResult、ListenableFuture、CompletionStage
AsyncTaskMethodReturnValueHandlerWebAsyncTask
针对注解
ModelAttributeMethodProcessor@ModelAttribute(require=false)
RequestResponseBodyMethodProcessor@ResponseBody
处理多种类型
ViewNameMethodReturnValueHandlervoid、CharSequence(V4.2)
MapMethodProcessorMap
自定义返回值处理器
ModelAndViewResolverMethodReturnValueHandler默认处理,如果以上的都不满足就会进入
ModelAttributeMethodProcessor@ModelAttribute(require=true)

 

2.3.2 RequestResponseBodyMethodProcessor

public boolean supportsReturnType(MethodParameter returnType) {
//支持类型
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}

mavContainer.setRequestHandled(true);//请求是否已经完全在处理程序中处理好,如果是fasle,则继续流转到对应的视图,如果设置为true,则不会再进过视图这一 层直接响应,默认是false。

public void handleReturnValue(Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		mavContainer.setRequestHandled(true);//如果没有视图,则必须设置为true,否则会返回视图层
		ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
		ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

		// Try even with null return value. ResponseBodyAdvice could get involved.
        //输出
		writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
	}

 

3.实例

我们来实现一个类似@ResponseBody的功能。

3.1 KingsResponseBody

@Target ( {ElementType.TYPE, ElementType.METHOD})
@Retention (RetentionPolicy.RUNTIME)
@Documented
public @interface KingsResponseBody {
    String type() default "json";
}

 

3.2 CustomerHandlerMethodReturnValueHandler

public class CustomerHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), KingsResponseBody.class) || returnType.hasMethodAnnotation(KingsResponseBody.class));
    }
    
    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        KingsResponseBody anno = returnType.getMethodAnnotation(KingsResponseBody.class);
        mavContainer.setRequestHandled(true);
        String type = anno.type();
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        response.setContentType("text/json;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            Gson jb = new Gson();
            out.write(jb.toJson(returnValue));
            out.flush();
        } catch (IOException e) {
            throw e;
        }
    }
}

有时候我们还可以往响应头里面丢一些内容什么的,具体的实现根据需求而定。

 

3.3 配置

    <mvc:annotation-driven >
        <mvc:return-value-handlers>
            <bean class="com.kings.template.mvc.CustomerHandlerMethodReturnValueHandler"/>
        </mvc:return-value-handlers>
    </mvc:annotation-driven>

 

3.4 控制器

@Controller
public class HandlerMethodReturnValueHandlerDemoController {
    
    @RequestMapping (value="/returnvalue/1",method=GET)
    @KingsResponseBody
    public List<Person> demo() {
        Person p1 = new Person();
        p1.setName("WS");
        Person p2 = new Person();
        p2.setName("Kings");
        return Lists.newArrayList(p1,p2);
    }
}

 

访问:http://localhost:8080/returnvalue/1

返回:

[{"name":"WS"},{"name":"Kings"}]

 

4. 总结

当我们需要统一处理springmvc返回值的时候我们就可以考虑使用HandlerMethodReturnValueHandler,而且也非常简单,比如某些场景会需要我们在响应里面加入特定一些什么响应头或者需要转化为指定格式。

 

发现一个机制的导航?

转载于:https://my.oschina.net/kings0/blog/735449

相关文章:

  • css 样式表分类总结
  • Babel6.x 转换ES6
  • SharpGL学习笔记(五) 视口变换
  • win2012配置
  • shell运算(加、减、乘、除)
  • 配置 linux-bridge mechanism driver - 每天5分钟玩转 OpenStack(77)
  • Android listview的item设定高度
  • 解决使用Handler时Can't create handler inside thread that has not called Looper.prepare()
  • Spring注解解释(@Primary、@Qualifier)
  • storm-kafka(storm spout作为kafka的消费端)
  • js没有重载
  • 【索引】Oracle之不可见索引和虚拟索引的比对
  • 分区
  • class文件概述
  • 关于 LVM 逻辑卷管理
  • 【技术性】Search知识
  • Fastjson的基本使用方法大全
  • happypack两次报错的问题
  • Java 网络编程(2):UDP 的使用
  • Javascript弹出层-初探
  • JavaScript设计模式之工厂模式
  • php的插入排序,通过双层for循环
  • python学习笔记 - ThreadLocal
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • vue总结
  • 阿里云Kubernetes容器服务上体验Knative
  • 闭包,sync使用细节
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 第十八天-企业应用架构模式-基本模式
  • 移动端 h5开发相关内容总结(三)
  • 由插件封装引出的一丢丢思考
  • ionic异常记录
  • k8s使用glusterfs实现动态持久化存储
  • ​如何在iOS手机上查看应用日志
  • # 透过事物看本质的能力怎么培养?
  • #每日一题合集#牛客JZ23-JZ33
  • (2022 CVPR) Unbiased Teacher v2
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (免费领源码)Python#MySQL图书馆管理系统071718-计算机毕业设计项目选题推荐
  • (排序详解之 堆排序)
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • .net 逐行读取大文本文件_如何使用 Java 灵活读取 Excel 内容 ?
  • .NET开发者必备的11款免费工具
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • @Pointcut 使用
  • [ 蓝桥杯Web真题 ]-Markdown 文档解析
  • [].slice.call()将类数组转化为真正的数组
  • [<事务专题>]
  • [100天算法】-不同路径 III(day 73)
  • [C++]unordered系列关联式容器
  • [echarts] y轴不显示0