SpringMVC(八):使用Servlet原生API作为Spring MVC hanlder方法的参数
在SpringMVC开发中,是有场景需要在Handler方法中直接使用ServletAPI。
在Spring MVC Handler的方法中都支持哪些Servlet API作为参数呢?
--Response
* <li>{@link ServletResponse}
* <li>{@link OutputStream}
* <li>{@link Writer}--Request
* <li>{@link WebRequest}
* <li>{@link ServletRequest}
* <li>{@link MultipartRequest}
* <li>{@link HttpSession}
* <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
* <li>{@link Principal}
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link HttpMethod} (as of Spring 4.0)
* <li>{@link Locale}
* <li>{@link TimeZone} (as of Spring 4.0)
* <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)
备注: 不同的SpringMVC版本,可能这里的标准不一致,我这里的SpringMVC版本是5.02。
为什么Spring MVC handler方法中支持上边的ServletAPI呢?
下边先看一个Spring MVC handler方法中使用servlet API作为参数的例子,之后调试来解析为什么。
新建一个handler类:TestServletAPI.java
package com.dx.springlearn.hanlders; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; @Controller
public class TestServletAPI {
private final String SUCCESS = "success"; @RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request, HttpServletResponse response) {
System.out.println("testServletAPI,request:" + request + ",response:" + response);
return SUCCESS;
}
}
备注:在第15行打上断掉,后边debug调试时需要这么做。
在index.jsp上添加链接:
<a href="testServletAPI">test ServletAPI</a>
测试打印结果为:
testServletAPI,request:org.apache.catalina.connector.RequestFacade@534683c2,response:org.apache.catalina.connector.ResponseFacade@77dbf19f
断点分析:

下边截图中是handler参数类型解析映射的位置:

下边截图的函数中针对不同的参数解析映射方式不同:

下图可以说明两个问题:
1)除了上图中providedArgs方式处理handler方法参数映射解析外,其他解析器都是实现了HandlerMethodArgumentResolver接口类;
2)其中跟ServletAPI接口类有关的handler方法参数映射解析器包含(该结论是调试时确定,但从图中不能完全确定):
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver,
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver

通过查看org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver,
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver源代码,就可以明白为什么Spring MVC handler方法只支持那些Servlet API:
org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver.java
package org.springframework.web.servlet.mvc.method.annotation; import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import javax.servlet.ServletResponse; import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; /**
* Resolves response-related method argument values of types:
* <ul>
* <li>{@link ServletResponse}
* <li>{@link OutputStream}
* <li>{@link Writer}
* </ul>
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class ServletResponseMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (ServletResponse.class.isAssignableFrom(paramType) ||
OutputStream.class.isAssignableFrom(paramType) ||
Writer.class.isAssignableFrom(paramType));
} /**
* Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
* {@code false} to indicate that the method signature provides access
* to the response. If subsequently the underlying method returns
* {@code null}, the request is considered directly handled.
*/
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { if (mavContainer != null) {
mavContainer.setRequestHandled(true);
} Class<?> paramType = parameter.getParameterType(); // ServletResponse, HttpServletResponse
if (ServletResponse.class.isAssignableFrom(paramType)) {
return resolveNativeResponse(webRequest, paramType);
} // ServletResponse required for all further argument types
return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class));
} private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeResponse = webRequest.getNativeResponse(requiredType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeResponse;
} private Object resolveArgument(Class<?> paramType, ServletResponse response) throws IOException {
if (OutputStream.class.isAssignableFrom(paramType)) {
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(paramType)) {
return response.getWriter();
} // Should never happen...
throw new UnsupportedOperationException("Unknown parameter type: " + paramType);
} }
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver.java
package org.springframework.web.servlet.mvc.method.annotation; import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.security.Principal;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.PushBuilder; import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils; /**
* Resolves request-related method argument values of the following types:
* <ul>
* <li>{@link WebRequest}
* <li>{@link ServletRequest}
* <li>{@link MultipartRequest}
* <li>{@link HttpSession}
* <li>{@link PushBuilder} (as of Spring 5.0 on Servlet 4.0)
* <li>{@link Principal}
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link HttpMethod} (as of Spring 4.0)
* <li>{@link Locale}
* <li>{@link TimeZone} (as of Spring 4.0)
* <li>{@link java.time.ZoneId} (as of Spring 4.0 and Java 8)
* </ul>
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver { @Nullable
private static Class<?> pushBuilder; static {
try {
pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder",
ServletRequestMethodArgumentResolver.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Servlet 4.0 PushBuilder not found - not supported for injection
pushBuilder = null;
}
} @Override
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) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
} @Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Class<?> paramType = parameter.getParameterType(); // WebRequest / NativeWebRequest / ServletWebRequest
if (WebRequest.class.isAssignableFrom(paramType)) {
if (!paramType.isInstance(webRequest)) {
throw new IllegalStateException(
"Current request is not of type [" + paramType.getName() + "]: " + webRequest);
}
return webRequest;
} // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
return resolveNativeRequest(webRequest, paramType);
} // HttpServletRequest required for all further argument types
return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
} private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeRequest;
} @Nullable
private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
if (HttpSession.class.isAssignableFrom(paramType)) {
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 (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {
return PushBuilderDelegate.resolvePushBuilder(request, paramType);
}
else if (InputStream.class.isAssignableFrom(paramType)) {
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)) {
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)) {
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) {
return HttpMethod.resolve(request.getMethod());
}
else if (Locale.class == paramType) {
return RequestContextUtils.getLocale(request);
}
else if (TimeZone.class == paramType) {
TimeZone timeZone = RequestContextUtils.getTimeZone(request);
return (timeZone != null ? timeZone : TimeZone.getDefault());
}
else if (ZoneId.class == paramType) {
TimeZone timeZone = RequestContextUtils.getTimeZone(request);
return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());
} // Should never happen...
throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
} /**
* Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.
*/
private static class PushBuilderDelegate { @Nullable
public static Object resolvePushBuilder(HttpServletRequest request, Class<?> paramType) {
PushBuilder pushBuilder = request.newPushBuilder();
if (pushBuilder != null && !paramType.isInstance(pushBuilder)) {
throw new IllegalStateException(
"Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);
}
return pushBuilder; }
} }
SpringMVC(八):使用Servlet原生API作为Spring MVC hanlder方法的参数的更多相关文章
- SpringMVC 支持使用Servlet原生API作为目标方法的参数
具体支持一下类型: * HttpServletRequest * HttpServletResponse * HttpSession * java.security.Pricipal * Locale ...
- SpringMVC之使用Servlet原生API作为参数
SpringMVC的handler接收如下的ServletAPI类型的参数: • HttpServletRequest • HttpServletResponse • HttpSession • ja ...
- 将前端请求中的数据绑定到Spring MVC响应方法中参数的四种方法
一.映射URL绑定的占位符到方法参数 1.方法 使用@PathVariable注解 2.代码示例 a.接收请求方法 @RequestMapping(value = "/deleteInfo/ ...
- 获取Servlet原生API
1.请求 <a href="param/test1">Servlet原生API</a> 2.处理方法 @RequestMapping("/para ...
- springmvc使用pojo和servlet原生api作为参数
一.Pojo作为参数: 实体: package com.hy.springmvc.entities; public class User { private String username; priv ...
- 注解 @RequestParam,@RequestHeader,@CookieValue,Pojo,servlet原生API
1.@RequestParam 我们的超链接:<a href="springMvc/testRequestParam">testRequestParam</a&g ...
- SpringBoot系列: Spring MVC视图方法的补充
SpringMVC 视图方法的参数, 已经在这个文章中写得非常清楚了, 链接为 https://www.cnblogs.com/morethink/p/8028664.html 这篇文章做一些补充. ...
- spring mvc: 属性方法名称解析器(多动作控制器)MultiActionController/ControllerClassNameHandlerMapping/PropertiesMethodNameResolver
spring mvc: 属性方法名称解析器(多动作控制器) 加入控制器是StudentContrller.java,里面有3个方法 index,add,remove 那么访问地址是: http://l ...
- spring mvc 控制器方法传递一些经验对象的数组
由于该项目必须提交一个表单,其中多个对象,更好的方法是直接通过在控制器方法参数的数组. 因为Spring mvc框架在反射生成控制方法的參数对象的时候会调用这个类的getDeclaredConstru ...
随机推荐
- Java String常用方法
字符串查找 两种查找字符串的方法,indexOf(String s)和lastIndexOf(String s). String str = "tyson-json"; int i ...
- 基于Ubuntu的LNMP环境搭建
装备的工具 Ubuntu16.04 , Xshell 使用Xshell链接到Ubuntu 使用xshell链接Ubuntu不是必须的,只是为了操作的方便,同时默认是你的Ubuntu已经安装好了 在Ub ...
- 【Python】 Selenium 模拟浏览器 寻路
selenium 最开始我碰到SE,是上学期期末,我们那个商务小组做田野调查时发的问卷的事情.当时在问卷星上发了个问卷,但是当时我对另外几个组员的做法颇有微词,又恰好开始学一些软件知识了,就想恶作剧( ...
- Android学习笔记2——shape
Android有很多特别的xml文件,如常用的selector.style以及shape,熟练使用这些xml可以是我们的项目变得更个性化. 一.子标签(corners.gradient.padding ...
- Java多线程:synchronized关键字和Lock
一.synchronized synchronized关键字可以用于声明方法,也可以用来声明代码块,下面分别看一下具体的场景(摘抄自<大型网站系统与Java中间件实践>) 案例一:其中fo ...
- 使用属性升级MyBank
一.访问修饰符 private :使用private访问修饰符修饰的属性或者方法只能在本类中使用 public :可以在任何类中访问到 二.this 关键字:代表当前类,this.属性:代 ...
- JavaScript(第三十天)【XPath】
XPath是一种节点查找手段,对比之前使用标准DOM去查找XML中的节点方式,大大降低了查找难度,方便开发者使用.但是,DOM3级以前的标准并没有就XPath做出规范:直到DOM3在首次推荐到标准规范 ...
- vim的配置
修改根目录下.vimrc文件: 1.设定解码,支持中文 set fileencodings=utf-8,ucs-born,gb18030,gbk,gb2312,cp936 set termencodi ...
- Week02-Java基本语法与类库
1. 本周学习总结 本周主要学习了包装类,明白了包装类比基本数据类型的优点(见书面作业4.2),了解了自动装箱,自动拆箱的概念和区别(见书面作业4.1),知道了java中的引用类似C语言中的指针,明白 ...
- 在深度linux下安装pip3与jupyter
前言 以下安装说明基于已经正确安装python3 文件下载 https://pypi.python.org/pypi/pip 下载pip-9.0.1.tar.gz (md5, pgp)文件 安装准备工 ...