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

Spring MVC的RequestContextHolder使用误区

JShop简介:jshop是一套使用Java语言开发的B2C网店系统,致力于为个人和中小企业提供免费、好用的网店系统。

项目主页:http://git.oschina.net/dinguangx/jshop

在线演示:

  • 前台: http://jshop.ofmall.org:81/jshop
  • 后台: http://jshop.ofmall.org:81/jshop/manage/user/login (admin/123456)

    在spring mvc中,为了随时都能取到当前请求的request对象,可以通过RequestContextHolder的静态方法getRequestAttributes()获取Request相关的变量,如request, response等。 
        在jshop中,对RequestContextHolder的使用进一步封装,简化为RequestHolder类,如下:

public class RequestHolder {
    public static HttpServletRequest getRequest(){ HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); return req; } public static HttpServletResponse getResponse(){ HttpServletResponse resp = ((ServletWebRequest)RequestContextHolder.getRequestAttributes()).getResponse(); return resp; } }

        在大部分的情况下,它都能很好地工作,但在商品管理编辑中,新增商品时,却出现了意外的问题:通过RequestHolder.getRequest().getParameter()得不到参数值,通过debug发现,
通过spring mvc的method注入的request对象实际为MultipartHttpServletRequest,而通过RequestHolder.getRequest()获取到的request对象却是org.apache.catalina.connector.RequestFacade的实例。 

public class RequestFacade implements HttpServletRequest 

 

原来在商品新增时,由于使用了文件上传,form表单的enctype类型为”multipart/form-data”,
spring mvc对文件上传的处理类实际却为spring-mvc.xml文件中配置的CommonsMultipartResolver
该类先判断当前请求是否为multipart类型,如果是的话,将request对象转为MultipartHttpServletRequet,相关的源码见DisptcherServlet


    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        ......
                processedRequest = checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
        ......
                // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ...... } protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException { if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) { if (request instanceof MultipartHttpServletRequest) { logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " + "this typically results from an additional MultipartFilter in web.xml"); } else { return this.multipartResolver.resolveMultipart(request); } } // If not returned before: return original request. return request; }

那么,RequestContextHolder中的request又是从哪来的呢? 
继续翻看DispatcherServlet的源码,从其父类FrameworkServlet中找到的processRequest()以相关方法源码:


    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException { ...... RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } ...... } protected ServletRequestAttributes buildRequestAttributes( HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) { if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) { return new ServletRequestAttributes(request); } else { return null; // preserve the pre-bound RequestAttributes instance } } private void initContextHolders( HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) { if (localeContext != null) { LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable); } if (requestAttributes != null) { RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); } if (logger.isTraceEnabled()) { logger.trace("Bound request context to thread: " + request); } } 

从这里可以看到,initContextHolder()方法中完成了RequestContextHolder的requestAttributes设置,
而doService()在这之后调用,DispatcherServlet中的processRequest()方法即在doService()之中,
所以从RequestContextHolder中获取到的就是原来的RequestFacade对象,而不是经过spring mvc处理之后的MultipartHttpServletRequest对象,
其后果就是,从RequestContextHolder获取request后,无法直接通过getParameter()获取参数值。

最便捷的解决办法: 
直接将HttpServletRequest作为spring mvc的方法入参,即可以正确获取参数值

Jshop简介:http://git.oschina.net/dinguangx/jshop

http://dinguangx.iteye.com/blog/2227049

 

相关文章:

  • 面对问题时尽量避免的两种思维
  • jQuery API 中文文档
  • 在iptables防火墙下开启vsftpd的端口
  • 包装类型的存在便于了集合类添加原生数据
  • linux mysql 相关操作命令
  • SSH 原理和公匙私匙
  • Programming C#.Classes and Objects.只读字段
  • 设计模式 博客
  • CentOS安装Docker报错文件冲突的解决方法
  • 20141111
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • Python用format格式化字符串
  • 【转】NSString属性什么时候用copy,什么时候用strong?
  • JS函数的属性
  • 链接与导航
  • ➹使用webpack配置多页面应用(MPA)
  • Android框架之Volley
  • axios 和 cookie 的那些事
  • C语言笔记(第一章:C语言编程)
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • Flannel解读
  • java2019面试题北京
  • JS学习笔记——闭包
  • Quartz初级教程
  • vue-router 实现分析
  • 码农张的Bug人生 - 见面之礼
  • 听说你叫Java(二)–Servlet请求
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • ​iOS安全加固方法及实现
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (12)Hive调优——count distinct去重优化
  • (2)MFC+openGL单文档框架glFrame
  • (3)llvm ir转换过程
  • (3)选择元素——(17)练习(Exercises)
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (Matlab)使用竞争神经网络实现数据聚类
  • (pytorch进阶之路)扩散概率模型
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (分享)自己整理的一些简单awk实用语句
  • (附源码)ssm智慧社区管理系统 毕业设计 101635
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (一) springboot详细介绍
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .Net - 类的介绍
  • .NET CLR Hosting 简介
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 中让 Task 支持带超时的异步等待
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .net反编译的九款神器
  • .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
  • .NET值类型变量“活”在哪?
  • .NET中的十进制浮点类型,徐汇区网站设计