本文共 8921 字,大约阅读时间需要 29 分钟。
目录
HttpServletRequest对象被获取一次之后再次去操作与流相关的操作会抛异常,原因是HttpServletRequest不允许直接对其流的读取进行二次访问,如果要访问我们必须获取到原始的HttpServletRequest或者是由Spring托管的本地化线程的HttpServletRequest对象。RequestContextHolder就可以帮我们办到。
以下是过滤器反编译代码:
/* */ package com.xxx.core.filter;/* */ /* */ import java.io.IOException;/* */ import javax.servlet.Filter;/* */ import javax.servlet.FilterChain;/* */ import javax.servlet.FilterConfig;/* */ import javax.servlet.ServletContext;/* */ import javax.servlet.ServletException;/* */ import javax.servlet.ServletRequest;/* */ import javax.servlet.ServletResponse;/* */ import javax.servlet.annotation.WebFilter;/* */ import javax.servlet.http.HttpServletRequest;/* */ import javax.servlet.http.HttpServletResponse;/* */ import javax.servlet.http.HttpSession;/* */ import org.apache.log4j.Logger;/* */ @WebFilter(filterName="context_Filter", urlPatterns={"/*"})/* */ public class ContextFilter/* */ implements Filter/* */ {/* 34 */ static Logger logger = Logger.getLogger(ContextFilter.class);/* */ /* */ /* 37 */ private static ThreadLocalthreadLocalRequest = new ThreadLocal();/* 38 */ private static ThreadLocal threadLocalResponse = new ThreadLocal();/* */ /* 40 */ private static ThreadLocal threadLocalObject = new ThreadLocal();/* */ /* */ /* */ /* */ public void destroy() {}/* */ /* */ /* */ public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)/* */ throws IOException, ServletException/* */ {/* 50 */ threadLocalRequest.set((HttpServletRequest)arg0);/* 51 */ threadLocalResponse.set((HttpServletResponse)arg1);/* 52 */ arg2.doFilter(arg0, arg1);/* */ }/* */ /* */ /* */ /* */ /* */ /* */ public void init(FilterConfig arg0)/* */ throws ServletException/* */ {}/* */ /* */ /* */ /* */ /* */ public static HttpServletRequest getRequest()/* */ {/* 68 */ HttpServletRequest req = (HttpServletRequest)threadLocalRequest.get();/* 69 */ return req;/* */ }/* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ public static HttpSession getSession()/* */ {/* 81 */ HttpServletRequest req = (HttpServletRequest)threadLocalRequest.get();/* 82 */ if (req == null) {/* 83 */ return null;/* */ }/* 85 */ return req.getSession();/* */ }/* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ public ServletContext getServletContext()/* */ {/* 97 */ if (null == getSession()) {/* 98 */ return null;/* */ }/* 100 */ return getSession().getServletContext();/* */ }/* */ /* */ public static void setSchema(String value)/* */ {/* 105 */ if (null == getRequest()) {/* 106 */ threadLocalObject.set(value);/* */ } else {/* 108 */ getRequest().setAttribute("_schema_", value);/* */ }/* 110 */ logger.debug(" setSchema >> id=" + Thread.currentThread().getId() + ",name " + Thread.currentThread().getName() + "," + value);/* */ }/* */ /* */ public static String getSchema() {/* 114 */ if (null == getRequest()) {/* 115 */ return (String)threadLocalObject.get();/* */ }/* 117 */ return (String)getRequest().getAttribute("_schema_");/* */ }/* */ }/* Location: D:\DEVELOPEWORKS\dataissue\WebRoot\WEB-INF\lib\forestar-core-3.6.4-SNAPSHOT.jar * Qualified Name: com.forestar.core.filter.ContextFilter * Java Class Version: 7 (51.0) * JD-Core Version: 0.7.1 */
上面表现的处理中我们已经取走了HttpServletRequest对象作为本地化线程的请求保存起来了。我们在其他地方可以这样使用而不用将HttpServletRequest作为参数传递:
HttpServletRequest request = ContextFilter.getRequest();
但是当需要读取流的时候,我们就会遇到问题。此时我们需要获得持有被Spring管理的HttpServletRequest。
获取HttpServletRequest:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder .getRequestAttributes()).getRequest();
RequestContextHolder源码:
public abstract class RequestContextHolder { private static final ThreadLocalrequestAttributesHolder = new NamedThreadLocal ("Request attributes"); private static final ThreadLocal inheritableRequestAttributesHolder = new NamedInheritableThreadLocal ("Request context"); public static void resetRequestAttributes() { requestAttributesHolder.remove(); inheritableRequestAttributesHolder.remove(); } public static void setRequestAttributes(RequestAttributes attributes) { setRequestAttributes(attributes, false); } //将RequestAttributes对象放入到ThreadLocal中,而HttpServletRequest和HttpServletResponse等则封装在RequestAttributes对象中,在此处就不对RequestAttributes这个类展开。反正我们需要知道的就是要获取RequestAttributes对象,然后再从RequestAttributes对象中获取到我们所需要的HttpServletRequest即可 public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) { if (attributes == null) { resetRequestAttributes(); } else { if (inheritable) { inheritableRequestAttributesHolder.set(attributes); requestAttributesHolder.remove(); } else { requestAttributesHolder.set(attributes); inheritableRequestAttributesHolder.remove(); } } } public static RequestAttributes getRequestAttributes() { RequestAttributes attributes = requestAttributesHolder.get(); if (attributes == null) { attributes = inheritableRequestAttributesHolder.get(); } return attributes; }}
那么在spring-mvc中是怎么实现的呢,我们来简单分析的,想了解具体机制的可以去看看spring-mvc的源码。
我们看下FrameworkServlet这个类,也就是DispatcherServlet的父类,里面有个processRequest方法,根据方法名称我们也可以大概了解到这个是方法用于处理请求的。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //将RequestAttributes设置到RequestContextHolder initContextHolders(request, localeContext, requestAttributes); try { //具体的业务逻辑 doService(request, response); } catch (ServletException ex) { failureCause = ex; throw ex; } catch (IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { //重置RequestContextHolder之前设置RequestAttributes resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } if (logger.isDebugEnabled()) { if (failureCause != null) { this.logger.debug("Could not complete request", failureCause); } else { if (asyncManager.isConcurrentHandlingStarted()) { logger.debug("Leaving response open for concurrent processing"); } else { this.logger.debug("Successfully completed request"); } } } publishRequestHandledEvent(request, response, startTime, failureCause); } } 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); } }
我们可以知道HttpServletRequest是在执行doService方法之前,也就是具体的业务逻辑前进行设置的,然后在执行完业务逻辑或者抛出异常时重置RequestContextHolder移除当前的HttpServletRequest。
更多阅读:
转载地址:http://krxj.baihongyu.com/