JShop简介:jshop是一套使用Java语言开发的B2C网店系统,致力于为个人和中小企业提供免费、好用的网店系统。
项目主页:http://git.oschina.net/dinguangx/jshop
在线演示:
- 前台: http://jshop.ofmall.org:81/jshop
- 后台: http://jshop.ofmall.org:81/jshop/manage/user/login (admin/123456)
在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的方法入参,即可以正确获取参数值。
http://dinguangx.iteye.com/blog/2227049