概述
该过滤器将当前请求暴露到当前线程,具体是通过org.springframework.context.i18n.LocaleContextHolder
和RequestContextHolder
。这样当前请求随后的处理过程中,就可以在当前线程中获取的当前请求的信息,而无需把请求对象作为参数到处传递 。
这里注意一个概念,缺省情况下,
Servlet
容器对一个请求的整个处理过程,是由同一个线程完成的,中途不会切换线程。但这个线程在处理完一个请求后,会被放回到线程池用于处理其他请求。
该过滤器由web.xml
或者WebMvcAutoConfiguration
注册。
Spring
的org.springframework.web.context.request.RequestContextListener
和org.springframework.web.servlet.DispatcherServlet
也做了同样的工作:将当前请求暴露到当前线程。该过滤器RequestContextFilter
主要是用于第三方serlvet
比如JSF FacesServlet
。在Spring
自己的Web
应用中,如果一个请求最终被DispatcherServlet
处理,起始DispatcherServlet
中往当前线程中设置请求的逻辑已经已经足够了,但是在一个Web
应用中,并不是所有的请求都最终会被DispatcherServlet
处理,比如匿名用户访问一个登录用户才能访问的资源,此时请求只会被安全过滤器处理,而不会到达DispatcherServlet
,在这种情况下,该过滤器RequestContextFilter
就起了担当了相应的职责。
该过滤器继承自OncePerRequestFilter
,也就是说,它在整个请求处理过程中最多只会被应用一次。
Springboot
提供了一个OrderedRequestContextFilter
继承自RequestContextFilter
应用在基于Springboot
的Servlet Web
应用中。OrderedRequestContextFilter
在RequestContextFilter
的功能上增加了接口OrderedFilter
定义的过滤器顺序,并且缺省使用优先级(-105
)。在整个Servlet
过滤器链中,过滤器的顺序数字越小,表示越先被调用。
源代码分析
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class RequestContextFilter extends OncePerRequestFilter {
// 是否向子线程传播 LocaleContext 和 RequestAttributes,缺省为false,不传播
private boolean threadContextInheritable = false;
// 设置是否向子线程传播 LocaleContext 和 RequestAttributes
public void setThreadContextInheritable(boolean threadContextInheritable) {
this.threadContextInheritable = threadContextInheritable;
}
// 返回 false, 表示对异步派发线程也建立request context
@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}
// 返回 false, 表示对error派发也建立request context
@Override
protected boolean shouldNotFilterErrorDispatch() {
return false;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 封装当前请求/响应对象到一个 ServletRequestAttributes
ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
// 将封装了的 ServletRequestAttributes 对象 attributes,当前请求的 locale 分别记录到对应当前线程的
// RequestContextHolder 和 LocaleContextHolder
initContextHolders(request, attributes);
// 上面的逻辑为当前请求的处理在当前线程中做了locale和请求/响应对象的记录,下面继续执行
// 过滤器链
try {
filterChain.doFilter(request, response);
}
finally {
// 请求处理完成,再次返回到该过滤器,此时要清除当前线程中记录的locale和请求/响应对象
// 信息,因为当前线程可能被放回线程池供处理其他请求使用
resetContextHolders();
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
attributes.requestCompleted();
}
}
// 将封装了的 ServletRequestAttributes 对象 attributes,当前请求的 locale 分别记录到对应当前线程的
// RequestContextHolder 和 LocaleContextHolder
private void initContextHolders(HttpServletRequest request, ServletRequestAttributes requestAttributes) {
LocaleContextHolder.setLocale(request.getLocale(), this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
// 清除当前线程中记录的locale和请求/响应对象
private void resetContextHolders() {
LocaleContextHolder.resetLocaleContext();
RequestContextHolder.resetRequestAttributes();
}
}