Spring MVC的RequestContextHolder使用及误区

在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对象却是RequestFacfade的实例。 

        原来在商品新增时,由于使用了文件上传,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的方法入参,即可以正确获取参数值。

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/u014172271/article/details/80717217
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞