zoukankan      html  css  js  c++  java
  • Spring MVC的RequestContextHolder使用误区

    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

  • 相关阅读:
    pyqt 设置QTabWidget标签页不可选
    C#分块读取文本数据(FileStream)
    C#IO读写文本txt文件中的数据
    C#IO读写文本txt文件中的数据
    Winform开发主界面菜单的动态树形列表展示
    Winform开发主界面菜单的动态树形列表展示
    C#LinqJoin两个DataTable
    C#LinqJoin两个DataTable
    c#转义字符
    c#转义字符
  • 原文地址:https://www.cnblogs.com/Jeely/p/10811860.html
Copyright © 2011-2022 走看看