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. Git入门指南九:远程仓库的使用【转】

    转自:http://blog.csdn.net/wirelessqa/article/details/20152651 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 十三 ...

  2. Educational Codeforces Round 42 (Rated for Div. 2) E. Byteland, Berland and Disputed Cities(贪心)

    E. Byteland, Berland and Disputed Cities time limit per test2 seconds memory limit per test256 megab ...

  3. Q1:spring-boot中Controller路径无法被访问的问题

    在学习spring-boot入门的第一个例子就是spring-boot-web的一个在页面上输出hello-world的例子,在运行这个例子的时候我遇到了下面这个简单的问题,但是第一次解决还是花了我很 ...

  4. java高并发核心要点|系列1|开篇

    在java高并发编程,有几个很重要的内容: 1.CAS算法 2.CPU重排序 3.缓存行伪共享 我们先来说说高并发世界中的主要关键问题是什么? 是数据共享. 因为多线程之间要共享数据,就会遇到各种问题 ...

  5. idea控制台搜索框

    https://blog.csdn.net/honnyee/article/details/82772948

  6. 使用 Maven Profile 和 Filtering 打各种环境的包(转)

    http://tunzao.me/articles/maven-profile/ https://blog.csdn.net/syani/article/details/52237470

  7. CentOS6.X系统启动流程

    1.硬件启动阶段 BIOS自检  BIOS的功能由两部分组成,分别是POST码和Runtime服务.POST阶段完成后它将从存储器中被清除,而Runtime服务会被一直保留,用于目标操作系统的启动.B ...

  8. JVM调优总结(三)——分代垃圾回收详述

    为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对象, ...

  9. 模板引擎ejs

    1.网站 https://ejs.co/ https://ejs.bootcss.com/ 2.app.js var http=require("http"); var ejs = ...

  10. 【知识】location.search获取中文时候会被编码成一串字符

    [转码] 例如:case.html?id='这个是页面的标题' 当想要使用location.search获取?id='这个是页面的标题'的时候,包含的中文会被编码成一串字符串. 所以我们需要进行解码, ...