在springmvc项目中,要在一次请求处理过程中拿到HttpServletRequest对象,一般是在controller方法中,直接引入HttpServletRequest参数。这样,springmvc在调用这个方法的时候,会根据方法的参数类型,传入对应的参数。
但还有一种方法,不需要引入参数,而是通过RequestContextHolder类的getRequestAttributes()方法获取到的RequestAttributes对象直接获取到HttpServletRequest对象,一般是采用这样的代码:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
那么为什么,这样的代码就可以直接获取到HttpServletRequest对象呢?
首先RequestAttributes接口在这里实现的对象是ServletRequestAttributes,也就是说RequestContextHolder.getRequestAttributes()返回的是ServletRequestAttributes对象。所以必然要实例化这个ServletRequestAttributes类。
其构造函数:
public ServletRequestAttributes(HttpServletRequest request, @Nullable HttpServletResponse response) {
this(request);
this.response = response;
}
从这个构造函数就可以看出来,实际上我们获取到的HttpServletRequest对象,就是在这个类实例化的时候传进来保存在这个类中的。看这里的代码,我们可以知道,HttpServletResponse对象也可以用这样的方法获取到。
那么这个ServletRequestAttributes是在哪里实例化的呢?
只需要在这个构造函数中下断点,然后启动springmvc项目,然后从浏览器发起请求,然后往上找堆栈信息,就可以定位到OrderedRequestContextFilter类的doFilterInternal方法。这个方法是从RequestContextFilter继承的。
OrderedRequestContextFilter类是springmvc内置的过滤器,所以每个请求都会经过这个过滤器的处理。
创建好的ServletRequestAttributes对象被传递进initContextHolders方法。
然后看initContextHolders方法中的如下语句:
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
setRequestAttributes方法中执行了如下语句:
requestAttributesHolder.set(attributes);
requestAttributesHolder是ThreadLocal对象:
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
也就是说,RequestAttributes对象保存在了线程本地变量表中。
然后再回头看到一下RequestContextHolder.getRequestAttributes():
RequestAttributes attributes = requestAttributesHolder.get();
这里就是直接从requestAttributesHolder中获取,实际上就是获取属于本线程的RequestAttributes对象。
总结一下:之所以能获取到HttpServletRequest对象,说到底,还是曾经把这个对象保存起来了。做这个事情的是OrderedRequestContextFilter这个过滤器。
在sprintboot中,这个功能,是通过WebMvcAutoConfiguration中注册OrderedRequestContextFilter,而实际上,只需要注册RequestContextFilter就可以了。在springmvc项目中,如果使用web.xml配置项目,那么可能需要在web.xml中手动配置RequestContextFilter。
该过滤器继承自OncePerRequestFilter,也就是说,它在整个请求处理过程中最多只会被应用一次。