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 {
//alreadyFilteredAttributeName是个静态变量,由类名+.filtered构成
//这样就能保证同一个类只被调用一次。
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的多个实例。