基本概念
首先来看下 SpringMVC 中请求处理的大致流程:
在 Servlet 中 service 方法负责请求处理。该方法在其子类 HttpServlet 中被重写。
在 HttpServlet 中,通过区分不同的请求类型,而采取不同的动作,如 doGet 负责 GET 请求、doPost 负责 Post 请求等。这些方法在其子类 FrameworkServlet 中也被重写。
在 FrameworkServlet 中,不同类型的请求都通过 processRequest 方法完成。
因此以 GET 请求为例,在 SpringMVC 中对于请求处理的流程大致为:
Servlet.service -> HttpServlet.service -> FrameworkServlet.doGet -> FrameworkServlet.processRequest
原理分析
下面来看 SpringMVC 处理请求的起点, processRequest 方法:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 取得当前线程的 LocaleContext 。默认为空
LocaleContext previousLocaleContext =
LocaleContextHolder.getLocaleContext();
// 1.创建 LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
// 取得当前线程的 RequestAttributes 。默认为空。
RequestAttributes previousAttributes =
RequestContextHolder.getRequestAttributes();
// 2.创建 RequestAttributes
ServletRequestAttributes requestAttributes =
buildRequestAttributes(request, response, previousAttributes);
// 异步请求相关,暂不分析
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),
new RequestBindingInterceptor());
// 3.初始化 ContextHolders
initContextHolders(request, localeContext, requestAttributes);
try {
// 4.服务处理
doService(request, response);
} catch (ServletException ex) {
failureCause = ex;
throw ex;
} catch (IOException ex) {
failureCause = ex;
throw ex;
} catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}finally {
// 5. 释放 ContextHolders
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 省略部分代码...
// 6.发布请求完毕事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
分析代码,可以发现该类主要做了以下几件事:
- 创建 LocaleContext
- 创建 RequestAttributes
- 初始化 ContextHolders
- 服务处理
- 释放 ContextHolders
- 发布请求处理完毕事件
流程探究
1.创建 LocaleContext
LocaleContext,该接口表示区域内容。主要作用是封装请求的 Locale 信息。
在 FrameworkServlet 中,默认创建的是它的接口实现类 SimpleLocaleContext。下面看它的源码
protected LocaleContext buildLocaleContext(HttpServletRequest request) {
return new SimpleLocaleContext(request.getLocale());
}
// SimpleLocaleContext 类构造函数
public SimpleLocaleContext(Locale locale) {
// 设置请求的 Locale 信息
this.locale = locale;
}
需要注意的是:该过程,即 buildLocaleContext 方法会在子类中被重写。
2.创建 RequestAttributes
RequestAttributes,该接口封装了本次请求的 rqeust,response 信息。通过它可以很方便的操作 rqeust 的属性。
在 FrameworkServlet 实际创建它的接口实现类 ServletRequestAttributes 。该方法在其子类 DispatcherServlet 中会被重写。
protected ServletRequestAttributes buildRequestAttributes(
HttpServletRequest request,
HttpServletResponse response,
RequestAttributes previousAttributes) {
if (previousAttributes == null ||
previousAttributes instanceof ServletRequestAttributes) {
return new ServletRequestAttributes(request, response);
}else {
return null;
}
}
// ServletRequestAttributes 类构造函数
public ServletRequestAttributes(HttpServletRequest request) {
this.request = request;
}
public ServletRequestAttributes(HttpServletRequest request, HttpServletResponse response) {
this(request);
this.response = response;
}
3.初始化 ContextHolder
ContextHolder 不是存在的接口,这里的 ContextHolder 指的是:LocaleContextHolder、RequestContextHolder 这两个内容持有器的统称。
该过程负责将上面创建的 LocaleContext、RequestAttributes 分别封装进 LocaleContextHolder、RequestContextHolder 。
// 表示是否将 LocaleContext、RequestAttributes 暴露给子线程继承
private boolean threadContextInheritable = false;
private void initContextHolders(
HttpServletRequest request,
LocaleContext localeContext,
RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext,
this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes,
this.threadContextInheritable);
}
// 省略代码...
}
LocaleContextHolder、RequestContextHolder 这两个抽象类其实都是利用 ThreadLocal 将信息存放在线程中,这样就可以在线程的任意生命周期内访问它。
以 RequestContextHolder 为例,来看下它的实现原理:
private static final ThreadLocal<LocaleContext> localeContextHolder =
new NamedThreadLocal<LocaleContext>("Locale context");
private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
new NamedInheritableThreadLocal<LocaleContext>("Locale context");
public static void setLocaleContext(LocaleContext localeContext, boolean inheritable) {
if (localeContext == null) {
// 释放操作
resetLocaleContext();
}else {
// 判断 inheritable,而将信息设置在不同的 ThreadLocal 对象上。
if (inheritable) {
inheritableLocaleContextHolder.set(localeContext);
localeContextHolder.remove();
}
else {
localeContextHolder.set(localeContext);
inheritableLocaleContextHolder.remove();
}
}
}
public static void resetLocaleContext() {
localeContextHolder.remove();
inheritableLocaleContextHolder.remove();
}
因此该过程其实就是将 LocaleContext、RequestAttributes 放进线程的过程。
4.服务处理
该方法在其子类 DispatcherServlet 中作具体实现。
5.释放 ContextHolders
该过程就是将之前添加到线程的 LocaleContext、RequestAttributes 又移除,恢复成添加操作之前的的样子。
private void resetContextHolders(
HttpServletRequest request,
LocaleContext prevLocaleContext,
RequestAttributes previousAttributes) {
LocaleContextHolder.setLocaleContext(prevLocaleContext,
this.threadContextInheritable);
RequestContextHolder.setRequestAttributes(previousAttributes,
this.threadContextInheritable);
// 省略代码...
}
6.发布请求处理完毕事件
// 表示是否发布请求处理完毕事件
private boolean publishEvents = true;
private void publishRequestHandledEvent(
HttpServletRequest request, HttpServletResponse response,
long startTime, Throwable failureCause) {
if (this.publishEvents) {
long processingTime = System.currentTimeMillis() - startTime;
// 获取状态码
int statusCode = (responseGetStatusAvailable ?
response.getStatus() : -1);
// 发布事件
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(
this,
request.getRequestURI(),
request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime,
failureCause,
statusCode));
}
}
总结
在 FrameworkServlet 中包含了 SpringMVC 处理请求的整个过程。不过请求处理的具体细节通过 doService 方法在其子类 DispatcherServlet 中定义。再次验证该类是整个 SpringMVC 的核心。