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 ...
随机推荐
- sklearn包中有哪些数据集你都知道吗?
注册了博客园一晃有3个月了,同时接触机器学习也断断续续的算是有1个月了.今天就用机器学习神器sklearn包的相关内容作为我的开篇文章吧. 本文将对sklearn包中的数据集做一个系统介绍,并简单说一 ...
- UnderScore.jsAPI记录
Collection Functions (Arrays or Objects) each _.each(list, iterator, [context]) 遍历list中的所有元素 ...
- 微信公众号的localStorage的大坑
业务流程是:工厂端分享一个邀请合作的二维码,商户这边用手机扫一扫后,关注微信公众号(已关注的老用户自动进入公众号)然后进入到公众号在面板上收到消息,合作邀请(图文字有点不对,请忽略!) 接下来,在点击 ...
- Access第一周总结
数据库[DataBase]是存放数据的仓库,是长期存在计算机的,有组织的.大量的.可共享的数据集合:数据模型的概念有:实体[Entity].属性[Attribute].关键字[Key].域[Domai ...
- 设计模式 --> (15)职责链模式
职责链模式 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 示例 考虑员工要求加薪.公司的管理者一共有 ...
- hibernate的一级和二级缓存
一级缓存就是Session级别的缓存,close后就没了. 二级缓存就是SessionFactory级别的缓存,全局缓存,要配置其他插件. 什么样的数据适合存放到第二级缓存中? 1.很少被修改的数据 ...
- Kali Linux下安装Nessus扫描器
一.官网下载Nessus(http://www.tenable.com/products/nessus/select-your-operating-system),这里需要查找自己对应的版本,如下图一 ...
- 测试驱动开发实践3————testSave之新增用户
内容指引 1.确定新增用户的业务规则 2.根据业务规则设计测试用例 3.为测试用例赋值并驱动开发 一.确定新增用户的规则 1.注册用户允许通过"用户名+密码"."手机号+ ...
- Java基础笔记(7)----三个修饰符
abstract抽象 方法 抽象方法:abstract修饰的方法,只有声明 而没有方法的实现(连{}都没有). 语法:修饰符 返回值类型 方法名(形参列表); 注意:抽象方法 必须定义在 抽象类中. ...
- 微信小程序中实现微信支付
最近在做微信小程序,今天刚好做到小程序里的微信支付这块,踩过不少坑,特此写个博客记录下,希望能帮到其它人吧. 我总结了一下,小程序中的微信支付和之前其它的公众号里的微信支付有两个区别,第一就是小程序必 ...