ServletRequestWrapper
Servlet规范从2.3起引入了ServletRequestWrapper包装类,它把调用交给被包装的ServletRequest来执行。这样就可以对ServletRequest进行扩展。例如Tomcat就是将自己的Request类作为包装类的实体。
public class ServletRequestWrapper implements ServletRequest { private ServletRequest request; public ServletRequestWrapper(ServletRequest request) { if (request == null) { throw new IllegalArgumentException("Request cannot be null"); } this.request = request; } public ServletRequest getRequest() { return this.request; } public Object getAttribute(String name) { return this.request.getAttribute(name); }}
为了更好的支持HttpServletRequest,Servlet2.3还支持了提供了HttpServletRequestWrapper,其实现了接口HttpServletRequest,并且继承了ServletRequestWrapper。
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest { public HttpServletRequestWrapper(HttpServletRequest request) { super(request); } private HttpServletRequest _getHttpServletRequest() { return (HttpServletRequest) super.getRequest(); }}
spring request wrapper
SessionRepositoryRequestWrapper继承了HttpServletWrapper,并覆盖了getSession方法,通过spring自己的策略生成session。此外提供了commitSession方法来进行session提交时处理,下一章会专门讲session生命周期。
spirng response wrapper
1. spring定义了新的response wrapper–OnCommittedResponseWrapper,其关联了自实现的字符和字节输出流,并定义了一个模板方法onResponseCommitted,由继承子类来实现。
2. OnCommittedResponseWrapper关联了自己实现的一个字节流和字符流。他们和普通的字节字符流一样也是个包装类。
通过SessionRepositoryFilter对Servlet侵入
spring-session通过Filter将自定义的Request wrapper和Response Wrapper侵入到Servlet容器中。
在Tomcat中Servlet的service方法会由ApplicationFilterChain调用,而FilterChain的参数通过Filter的filterChain.doFilter传入。Spring-session通过自定义的全局拦截器SessionRepositoryFilter(详见第一篇)将request和response侵入进容器。
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { request.setAttribute(SESSION_REPOSITORY_ATTR, sessionRepository); SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, servletContext); SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response); HttpServletRequest strategyRequest = httpSessionStrategy.wrapRequest(wrappedRequest, wrappedResponse); HttpServletResponse strategyResponse = httpSessionStrategy.wrapResponse(wrappedRequest, wrappedResponse); try { filterChain.doFilter(strategyRequest, strategyResponse); } finally { wrappedRequest.commitSession(); } }
SessionRepositoryFilter继承了OncePerRequestFilter,父类的doFilter最终会将具体的处理逻辑交给子类处理。
abstract class OncePerRequestFilter implements Filter { public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null; if (hasAlreadyFilteredAttribute) { filterChain.doFilter(request, response); } else { request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { doFilterInternal(httpRequest, httpResponse, filterChain); } } }}
OncePerRequestFilter是用来保证一次完整的拦截链中,同一个类只会被调用一次。
因为spring无法保证同一个Filter类只有一个实例。有可能一个Filter既有可能在web.xml里配置由容器初始化了,还有可能被作为spring的依赖引入进了DelegatingFilterProxy。这样在一次filter chain中就会存在同一个Filter的多个实例。