最近做项目中需要获取request,从而获取请求ip,request可以从控制层传过来,也可以调用RequestContextHolder获取。获取方式如下:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest()
那么问题来了,问什么调用这个方法就可以呢,值又是什么时候set进去的呢,首先要明确这是springMvc提供的方法,所以得从springMvc的源码看起,spring Mvc核心代码图谱如下:
http请求会出发HttpServlet的doGet或doPost方法,但这两个方法的实现是在FrameworkServlet中,所以看FrameworkServlet源码,先看doGet方法入手
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
只有个processRequest方法,那么重点看下这个方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//先获取LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
//建立新的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
//建立新的ServletRequestAttributes 这个就是存在后来RequestContextHolder的RequestAttributes,
//所以取出来的时候需要强转一下,获取RequestContextHolder的HttpServletRequest
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//具体方法
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response); //这是DispatcherServlet核心实现代码,有兴趣的可以看下,这里不做讨论
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
………… 此处省略
}
}
接下来看下其中的initContextHolders方法到底做了什么
private void initContextHolders(
HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) { //设置的地方requestAttributes
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
}
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();
}
}
}
inheritableRequestAttributesHolder和requestAttributesHolder其实就是RequestContextHolder的属性,就在这个时候被set进去了,
//得到存储进去的request
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<RequestAttributes>("Request attributes");
//可被子线程继承的request
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<RequestAttributes>("Request context");
这里需要对ThreadLocal简单解释一下,ThreadLocal即线程共享变量,线程间互不影响,所以在每个请求里获取的就是当前线程的request.对ThreadLocal的详细解释请看这里;
到这里应该对之前获取的request不足为奇了,希望能帮到爱学习的你!