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

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

在线演示:

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

  1.  
    public class RequestHolder {
  2.  
    public static HttpServletRequest getRequest(){
  3.  
    HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  4.  
    return req;
  5.  
    }
  6.  
     
  7.  
    public static HttpServletResponse getResponse(){
  8.  
    HttpServletResponse resp = ((ServletWebRequest)RequestContextHolder.getRequestAttributes()).getResponse();
  9.  
    return resp;
  10.  
    }
  11.  
    }

在大部分的情况下,它都能很好地工作,但在商品管理编辑中,新增商品时,却出现了意外的问题:通过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

  1.  
     
  2.  
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  3.  
    HttpServletRequest processedRequest = request;
  4.  
    ......
  5.  
    processedRequest = checkMultipart(request);
  6.  
    multipartRequestParsed = processedRequest != request;
  7.  
    ......
  8.  
    // Actually invoke the handler.
  9.  
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  10.  
    ......
  11.  
    }
  12.  
     
  13.  
    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
  14.  
    if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
  15.  
    if (request instanceof MultipartHttpServletRequest) {
  16.  
    logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
  17.  
    "this typically results from an additional MultipartFilter in web.xml");
  18.  
    }
  19.  
    else {
  20.  
    return this.multipartResolver.resolveMultipart(request);
  21.  
    }
  22.  
    }
  23.  
    // If not returned before: return original request.
  24.  
    return request;
  25.  
    }

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

  1.  
     
  2.  
    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
  3.  
    throws ServletException, IOException {
  4.  
    ......
  5.  
     
  6.  
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
  7.  
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
  8.  
     
  9.  
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  10.  
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
  11.  
     
  12.  
    initContextHolders(request, localeContext, requestAttributes);
  13.  
     
  14.  
    try {
  15.  
    doService(request, response);
  16.  
    }
  17.  
    ......
  18.  
    }
  19.  
     
  20.  
     
  21.  
    protected ServletRequestAttributes buildRequestAttributes(
  22.  
    HttpServletRequest request, HttpServletResponse response, RequestAttributes previousAttributes) {
  23.  
     
  24.  
    if (previousAttributes == null || previousAttributes instanceof ServletRequestAttributes) {
  25.  
    return new ServletRequestAttributes(request);
  26.  
    }
  27.  
    else {
  28.  
    return null; // preserve the pre-bound RequestAttributes instance
  29.  
    }
  30.  
    }
  31.  
     
  32.  
     
  33.  
    private void initContextHolders(
  34.  
    HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
  35.  
     
  36.  
    if (localeContext != null) {
  37.  
    LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
  38.  
    }
  39.  
    if (requestAttributes != null) {
  40.  
    RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
  41.  
    }
  42.  
    if (logger.isTraceEnabled()) {
  43.  
    logger.trace("Bound request context to thread: " + request);
  44.  
    }
  45.  
    }
  46.  
     

从这里可以看到,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

Spring MVC的RequestContextHolder使用误区的更多相关文章

  1. Spring MVC的RequestContextHolder使用误区 good

    JShop简介:jshop是一套使用Java语言开发的B2C网店系统,致力于为个人和中小企业提供免费.好用的网店系统. 项目主页:http://git.oschina.net/dinguangx/js ...

  2. Spring MVC 中RequestContextHolder获取request和response

    1.最简单方式:处理方法入参 例如: @RequestMapping("/test") @ResponseBody public void saveTest(HttpServlet ...

  3. spring mvc 异常统一处理方式

    springMVC提供的异常处理主要有两种方式: 一种是直接实现自己的HandlerExceptionResolver: 另一种是使用注解的方式实现一个专门用于处理异常的Controller——Exc ...

  4. spring mvc DispatcherServlet详解之前传---前端控制器架构

    前端控制器是整个MVC框架中最为核心的一块,它主要用来拦截符合要求的外部请求,并把请求分发到不同的控制器去处理,根据控制器处理后的结果,生成相应的响应发送到客户端.前端控制器既可以使用Filter实现 ...

  5. Spring MVC中各个filter的用法

    转载:http://blog.csdn.net/qyp1314/article/details/42023725 Spring MVC中各个filter的用法 2014-12-19 09:08 105 ...

  6. spring、spring mvc、mybatis框架整合基本知识

    学习了一个多月的框架知识了,这两天很想将它整合一下.网上看了很多整合案例,基本都是基于Eclipse的,但现在外面公司基本都在用Intellij IDEA了,所以结合所学知识,自己做了个总结,有不足之 ...

  7. Spring MVC 中获取session的几种方法

    Spring MVC 中使用session是一种常见的操作,但是大家上网搜索一下可以看到获取session的方式方法五花八门,最近,自己终结了一下,将获取session的方法记录下来,以便大家共同学习 ...

  8. Spring MVC 后端获取前端提交的json格式字符串并直接转换成control方法对应的参数对象

    场景: 在web应用开发中,spring mvc凭借出现的性能和良好的可扩展性,导致使用日渐增多,成为事实标准,在日常的开发过程中,有一个很常见的场景:即前端通过ajax提交方式,提交参数为一个jso ...

  9. Servlet的生命周期以及在Spring MVC中调用流程

    接触Web时间比较久,虽然知道Servlet的生命周期但是理解却还是不够,今天刚好debug代码涉及这块就利用余下时间研究了一下. Servlet的生命周期以及处理浏览器请求的过程.Servlet接口 ...

随机推荐

  1. Spring学习之设计模式,动态代理和gclib动态代理

    传统的代理模式是静态代理,也就是在方法区域中写入方法. 而动态代理的作用是,不修改实现类的代码,能够在代码的前后或者抛出异常的前后执行某个方法. 动态代理类的实现 //Interface public ...

  2. 将.py文件转换成.exe文件

    1.安装pyinstaller模块 pip install pyinstaller 2.打开python目录下的scripts文件夹 cmd下,使用pyinstaller -F test.py,运行后 ...

  3. 清北学堂北京大学吴耀轩神仙讲课day5摘要

    今天讲图论 图是啥?(白纸上的符号?) 对于一个拥有n个顶点的无向连通图,它的边数一定多于n-1条.若从中选择n-1条边,使得无向图仍然连通,则由n个顶点及这 n-1条边(弧)组成的图被称为原无向图的 ...

  4. linux 生成密钥和公钥,实现免密登录

    1. 在相应的用户根目录下生成密钥公钥,输入如下命令: ssh-keygen -t rsa 2. 直接三次回车:会生成两个文件:id_rsa / id_rsa.pub,分别为密钥和公钥 3.  打开公 ...

  5. linux 文件查找 find命令详解

    一,从索引库查找文件:locate 索引库:操作系统会周期性的遍历根文件系统,然后生成索引库 手动更新索引库:updatedb 语法:locate [OPTION]... PATTERN... 只匹配 ...

  6. idea 导出可以直接运行的jar 文件

    刚开始采用的maven插件是 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId ...

  7. Kendo UI使用教程:Bower Packages

    [Kendo UI最新试用版下载] Kendo UI目前最新提供Kendo UI for jQuery.Kendo UI for Angular.Kendo UI Support for React和 ...

  8. shiro框架学习-7- Shiro权限控制注解和编程方式

    讲解权限角色控制 @RequiresRoles, @RequiresPermissions等注解的使用和编程式控制 配置文件的方式 使用ShiroConfig 注解方式 @RequiresRoles( ...

  9. 【BZOJ3876】 [Ahoi2014]支线剧情

    Description [故事背景] 宅男JYY非常喜欢玩RPG游戏,比如仙剑,轩辕剑等等.不过JYY喜欢的并不是战斗场景,而是类似电视剧一般的充满恩怨情仇的剧情.这些游戏往往 都有很多的支线剧情,现 ...

  10. springboot(六) Maven打包引入本地jar包

       springboot Maven打包引入本地jar包 最近在做项目的时候,有一些jar包不存在maven的依赖库中,所以需要自己引入本地jar包来达到需求,那么我们该如何去将本地的jar包引入s ...