Spring MVC的RequestContextHolder使用誤區 good


JShop簡介:jshop是一套使用Java語言開發的B2C網店系統,致力於為個人和中小企業提供免費、好用的網店系統。

項目主頁:http://git.oschina.net/dinguangx/jshop

在線演示:

    在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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM