开发者社区> 木叶_之荣> 正文

SpringMVC之分析HandlerMethodArgumentResolver请求对应处理器方法参数的解析过程(二)

简介: 在上一篇文章中我们分析了SpringMVC对简单对象和@RequestParam注解的解析过程,这一章中我们继续分析其他形式的参数解析过程。 ServletRequestMethodArgumentResolver 下面来看一下我们的第一个请...
+关注继续查看

在上一篇文章中我们分析了SpringMVC对简单对象和@RequestParam注解的解析过程,这一章中我们继续分析其他形式的参数解析过程。

ServletRequestMethodArgumentResolver

下面来看一下我们的第一个请求:http://localhost:8086/allRequestFormat/requestAndResponseRequest?userName=zhangsan
对应的后台处理代码是:
    @RequestMapping("requestAndResponseRequest")
    public String requestAndResponseRequest(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("userName" + request.getParameter("userName"));
        return "这是一个接收Request和Response的请求";
    }
我们的请求处理方法中的两个参数是HttpServletRequest和HttpServletResponse类型,那么SpringMVC在调用requestAndResponseRequest这个方法的时候是怎么解析到这两个参数的值的呢?我们先看HttpServletRequest这个参数,这个参数是ServletRequestMethodArgumentResolver这个类来解析的。在上一篇文章中,我们知道通过调用argumentResolvers.supportsParameter这个方法来判断HandlerMethodArgumentResolver的实现类是否支持对应的参数的解析,和resolveArgument方法来实现真正的参数解析。
所以我们先看一下ServletRequestMethodArgumentResolver这个类中的supportsParameter这个方法的内容:
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) ||
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				Principal.class.isAssignableFrom(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				"java.time.ZoneId".equals(paramType.getName()));
	}
从上面的代码中我们可以看到如果参数类型为WebRequest类型、ServletRequest类型、MultipartRequest类型、HttpSession类型、Principal类型、InputStream类型、Reader类型或者HttpMethod类型、Locale类型、TimeZone类型、ZoneId类型则使用这个参数解析器进行参数的解析工作。
下面我们在看一下ServletRequestMethodArgumentResolver是怎么进行参数解析工作的:
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		Class<?> paramType = parameter.getParameterType();
		//如果是WebRequest的子类
		if (WebRequest.class.isAssignableFrom(paramType)) {
			//不是NativeWebRequest的实现类,则抛出异常
			if (!paramType.isInstance(webRequest)) {
				throw new IllegalStateException(
						"Current request is not of type [" + paramType.getName() + "]: " + webRequest);
			}
			//直接返回传进来的NativeWebRequest
			return webRequest;
		}
		//从传进来的webRequest中获取HttpServletRequest对象
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		//如果是ServletRequest的类型或者为MultipartRequest的类型
		if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
			//判断类型是否一致
			Object nativeRequest = webRequest.getNativeRequest(paramType);
			if (nativeRequest == null) {
				throw new IllegalStateException(
						"Current request is not of type [" + paramType.getName() + "]: " + request);
			}
			return nativeRequest;
		}
		else if (HttpSession.class.isAssignableFrom(paramType)) {
			//从request中得到HttpSession对象
			HttpSession session = request.getSession();
			if (session != null && !paramType.isInstance(session)) {
				throw new IllegalStateException(
						"Current session is not of type [" + paramType.getName() + "]: " + session);
			}
			return session;
		}
		else if (InputStream.class.isAssignableFrom(paramType)) {
			//从request中得到InputStream对象
			InputStream inputStream = request.getInputStream();
			if (inputStream != null && !paramType.isInstance(inputStream)) {
				throw new IllegalStateException(
						"Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
			}
			return inputStream;
		}
		else if (Reader.class.isAssignableFrom(paramType)) {
			//从request中得到Reader对象
			Reader reader = request.getReader();
			if (reader != null && !paramType.isInstance(reader)) {
				throw new IllegalStateException(
						"Request body reader is not of type [" + paramType.getName() + "]: " + reader);
			}
			return reader;
		}
		else if (Principal.class.isAssignableFrom(paramType)) {
			//从request中得到Reader对象
			Principal userPrincipal = request.getUserPrincipal();
			if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
				throw new IllegalStateException(
						"Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
			}
			return userPrincipal;
		}
		else if (HttpMethod.class == paramType) {
			//从request中得到请求类型的值
			return HttpMethod.resolve(request.getMethod());
		}
		else if (Locale.class == paramType) {
			//从Request上下文中获取国际化对象
			return RequestContextUtils.getLocale(request);
		}
		else if (TimeZone.class == paramType) {
			//从Request上下文中获取时区对象
			TimeZone timeZone = RequestContextUtils.getTimeZone(request);
			return (timeZone != null ? timeZone : TimeZone.getDefault());
		}
		else if ("java.time.ZoneId".equals(paramType.getName())) {
			//JDK1.8时区
			return ZoneIdResolver.resolveZoneId(request);
		}
		else {
			// Should never happen...
			throw new UnsupportedOperationException(
					"Unknown parameter type [" + paramType.getName() + "] in " + parameter.getMethod());
		}
	}
从上面的代码中我们发现最主要的是NativeWebRequest这个对象,我们可以从这个对象中获取到HttpServletRequest,从HttpServletRequest中获取一系列的其他对象的值。我们可以看一下NativeWebRequest这个对象的值是怎么创建的。在RequestMappingHandlerAdapter#invokeHandlerMethod中有这样一段代码:
ServletWebRequest webRequest = new ServletWebRequest(request, response);
在ServletWebRequest这个对象中持有request和response这两个对象的引用。ServletWebRequest的UML类图关系如下:

所以resolveArgument中的NativeWebRequest其实是ServletWebRequest这个对象。

ServletResponseMethodArgumentResolver

上面分析完了HttpServletRequest,我们还有一个参数类型是HttpServletResponse,那么这个HttpServletResponse这个参数是谁解析的呢?是ServletResponseMethodArgumentResolver来解析的。我们看一下它的supportsParameter方法的实现:
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (ServletResponse.class.isAssignableFrom(paramType) ||
				OutputStream.class.isAssignableFrom(paramType) ||
				Writer.class.isAssignableFrom(paramType));
	}
ServletResponseMethodArgumentResolver解析的参数类型是:ServletResponse类型、OutputStream类型和Writer类型。我们再看下一resolveArgument方法中对参数解析的实现:
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

		if (mavContainer != null) {
			mavContainer.setRequestHandled(true);
		}
		//从webRequest获取HttpServletResponse对象,我们前面说了webRequest持有HttpServletResponse对象的引用
		HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
		Class<?> paramType = parameter.getParameterType();
		//ServletResponse类型
		if (ServletResponse.class.isAssignableFrom(paramType)) {
			Object nativeResponse = webRequest.getNativeResponse(paramType);
			if (nativeResponse == null) {
				throw new IllegalStateException(
						"Current response is not of type [" + paramType.getName() + "]: " + response);
			}
			return nativeResponse;
		}//从HttpServletResponse中获取OutputStream对象
		else if (OutputStream.class.isAssignableFrom(paramType)) {
			return response.getOutputStream();
		}//从HttpServletResponse中获取Writer对象
		else if (Writer.class.isAssignableFrom(paramType)) {
			return response.getWriter();
		}
		else {
			// should not happen
			Method method = parameter.getMethod();
			throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method);
		}
	}
这个参数的解析过程和ServletRequestMethodArgumentResolver中对象参数的解析过程如出一辙。我们对HttpServletRequest和HttpServletResponse参数的解析就先到这里了。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
《 自动化测试最佳实践:来自全球的经典自动化测试案例解析》一一3.1 本案例研究的背景
本节书摘来自华章出版社《 自动化测试最佳实践:来自全球的经典自动化测试案例解析 》一 书中的第3章,第3. 1 节,作者:(英)Dorothy Graham Mark Fewster 著 ,更多章节内容可以访问云栖社区“华章计算机”公众号查看
1059 0
JavaWeb项目的中文乱码的原因以及Servlet中处理GET请求和POST请求编码过滤器
一、乱码原因 ①传输方和接收方采用的编码不一致。传输方对参数采用的是UTF-8编码而接收方却用GBK进行解析,当然是乱码。 ②Tomcat服务器默认采用的ISO8859-1编码得到参数值。虽然①中采用了同样的编码方式,但经过tomcat一处理,也会出现乱码(GET方式)   二、解决办法 方法一 每次传输都手动设置编码(GET方式传输数据) 传输方 String name =
1345 0
[裴礼文数学分析中的典型问题与方法习题参考解答]4.5.9
设 $f(x)$ 为连续实值函数, 对所有 $x$, 有 $f(x)\geq 0$, 且 $\dps{\int_0^\infty f(x)\rd x0,}\ \exists\ N_1,\st n\geq N_1\ra \int_{N_1}^n f(x)\rd xN_1,\st n\geq N\ra ...
654 0
《 自动化测试最佳实践:来自全球的经典自动化测试案例解析》一一3.3 如何实施TiP
本节书摘来自华章出版社《 自动化测试最佳实践:来自全球的经典自动化测试案例解析 》一 书中的第3章,第3. 节,作者:(英)Dorothy Graham Mark Fewster 著 ,更多章节内容可以访问云栖社区“华章计算机”公众号查看
1160 0
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.3
证明级数 $$\bex 1 +\frac{1}{\sqrt{3}} -\frac{1}{\sqrt{2}} +\frac{1}{\sqrt{5}} +\frac{1}{\sqrt{7}} -\frac{1}{\sqrt{4}} +\frac{1}{\sqrt{9}} +\frac{1}{\sqrt{...
585 0
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.4
证明: 当 $p\geq1$ 时, $$\bex \vsm{n}\frac{1}{(n+1)\sqrt[p]{n}}
512 0
[裴礼文数学分析中的典型问题与方法习题参考解答]4.5.10
证明 $\dps{\lim_{x\to\infty}\int_0^\infty \frac{e^{-tx}}{1+t^2}\rd t=0}$.   证明: 由 $$\beex \bea 0&\leq \int_0^\infty \frac{e^{-tx}}{1+t^2}\rd t=-\frac{...
700 0
[裴礼文数学分析中的典型问题与方法习题参考解答]4.5.17
已知积分 $\dps{\int_0^\infty \frac{\sin \beta x}{x}\rd x=\frac{\pi }{2}\sgn \beta}$ (见例 7.1.38), 求积分 $\dps{\int_0^\infty \frac{\sin x\cos xt}{x}\rd x}$.
592 0
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.5
证明: 若删去调和级数中所有分母含有数字 $9$ 的项, 则新级数收敛, 且和小于 $80$.   证明: 对 $m=1,2,\cdots$, $[10^{m-1},10^m)$ 中的自然数的十进制表示中没有 $9$ 的个数为 $8\cdot 9^{m-1}$ (除首位可能为 $1,2,\cdo...
864 0
153
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载