在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方法的参数的更多相关文章

  1. SpringMVC 支持使用Servlet原生API作为目标方法的参数

    具体支持一下类型: * HttpServletRequest * HttpServletResponse * HttpSession * java.security.Pricipal * Locale ...

  2. SpringMVC之使用Servlet原生API作为参数

    SpringMVC的handler接收如下的ServletAPI类型的参数: • HttpServletRequest • HttpServletResponse • HttpSession • ja ...

  3. 将前端请求中的数据绑定到Spring MVC响应方法中参数的四种方法

    一.映射URL绑定的占位符到方法参数 1.方法 使用@PathVariable注解 2.代码示例 a.接收请求方法 @RequestMapping(value = "/deleteInfo/ ...

  4. 获取Servlet原生API

    1.请求 <a href="param/test1">Servlet原生API</a> 2.处理方法 @RequestMapping("/para ...

  5. springmvc使用pojo和servlet原生api作为参数

    一.Pojo作为参数: 实体: package com.hy.springmvc.entities; public class User { private String username; priv ...

  6. 注解 @RequestParam,@RequestHeader,@CookieValue,Pojo,servlet原生API

    1.@RequestParam 我们的超链接:<a href="springMvc/testRequestParam">testRequestParam</a&g ...

  7. SpringBoot系列: Spring MVC视图方法的补充

    SpringMVC 视图方法的参数, 已经在这个文章中写得非常清楚了, 链接为 https://www.cnblogs.com/morethink/p/8028664.html 这篇文章做一些补充. ...

  8. spring mvc: 属性方法名称解析器(多动作控制器)MultiActionController/ControllerClassNameHandlerMapping/PropertiesMethodNameResolver

    spring mvc: 属性方法名称解析器(多动作控制器) 加入控制器是StudentContrller.java,里面有3个方法 index,add,remove 那么访问地址是: http://l ...

  9. spring mvc 控制器方法传递一些经验对象的数组

    由于该项目必须提交一个表单,其中多个对象,更好的方法是直接通过在控制器方法参数的数组. 因为Spring mvc框架在反射生成控制方法的參数对象的时候会调用这个类的getDeclaredConstru ...

随机推荐

  1. Java String常用方法

    字符串查找 两种查找字符串的方法,indexOf(String s)和lastIndexOf(String s). String str = "tyson-json"; int i ...

  2. 基于Ubuntu的LNMP环境搭建

    装备的工具 Ubuntu16.04 , Xshell 使用Xshell链接到Ubuntu 使用xshell链接Ubuntu不是必须的,只是为了操作的方便,同时默认是你的Ubuntu已经安装好了 在Ub ...

  3. 【Python】 Selenium 模拟浏览器 寻路

    selenium 最开始我碰到SE,是上学期期末,我们那个商务小组做田野调查时发的问卷的事情.当时在问卷星上发了个问卷,但是当时我对另外几个组员的做法颇有微词,又恰好开始学一些软件知识了,就想恶作剧( ...

  4. Android学习笔记2——shape

    Android有很多特别的xml文件,如常用的selector.style以及shape,熟练使用这些xml可以是我们的项目变得更个性化. 一.子标签(corners.gradient.padding ...

  5. Java多线程:synchronized关键字和Lock

    一.synchronized synchronized关键字可以用于声明方法,也可以用来声明代码块,下面分别看一下具体的场景(摘抄自<大型网站系统与Java中间件实践>) 案例一:其中fo ...

  6. 使用属性升级MyBank

    一.访问修饰符  private   :使用private访问修饰符修饰的属性或者方法只能在本类中使用 public    :可以在任何类中访问到 二.this 关键字:代表当前类,this.属性:代 ...

  7. JavaScript(第三十天)【XPath】

    XPath是一种节点查找手段,对比之前使用标准DOM去查找XML中的节点方式,大大降低了查找难度,方便开发者使用.但是,DOM3级以前的标准并没有就XPath做出规范:直到DOM3在首次推荐到标准规范 ...

  8. vim的配置

    修改根目录下.vimrc文件: 1.设定解码,支持中文 set fileencodings=utf-8,ucs-born,gb18030,gbk,gb2312,cp936 set termencodi ...

  9. Week02-Java基本语法与类库

    1. 本周学习总结 本周主要学习了包装类,明白了包装类比基本数据类型的优点(见书面作业4.2),了解了自动装箱,自动拆箱的概念和区别(见书面作业4.1),知道了java中的引用类似C语言中的指针,明白 ...

  10. 在深度linux下安装pip3与jupyter

    前言 以下安装说明基于已经正确安装python3 文件下载 https://pypi.python.org/pypi/pip 下载pip-9.0.1.tar.gz (md5, pgp)文件 安装准备工 ...