一.FrameworkServlet 主要介绍
Spring MVC 的核心是DispatcherServlet,其父类是FramworkServlet.
1.1 继承关系
1.png- HttpServletBean 继承自HttpServlet
- FrameworkServlet 的核心方法是 service
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
说明:
- 主要是对PATCH 方法做了特殊处理,其它由 super.service 处理。
1.2 FrameworkServlet
HttpServlet 中并未对 doGet/doPost 等进行实质性处理,所以FrameworkServlet 中重写了各种请求对应的方法,如:doDelete、doGet、doOptions、doPost、doPut、doTrace 等,其实就是除了 doHead 之外的其他方法都重写了。
1.3 FrameworkServlet 核心方法 processRequest
方法如下:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
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);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
processRequest 其实主要做了两件事,第一件事就是对 LocaleContext 和 RequestAttributes 的处理,第二件事就是发布事件。方法分为三部分:
1.doService 之前主要是一些准备工作,准备工作主要干了两件事:
第一件事就是从 LocaleContextHolder 和 RequestContextHolder 中分别获取它们原来保存的 LocaleContext 和 RequestAttributes 对象存起来,然后分别调用 buildLocaleContext 和 buildRequestAttributes 方法获取到当前请求的 LocaleContext 和 RequestAttributes 对象,再通过 initContextHolders 方法将当前请求的 LocaleContext 和 RequestAttributes 对象分别设置到 LocaleContextHolder 和 RequestContextHolder 对象中;
第二件事则是获取到异步管理器并设置拦截器。
2.接下来就是 doService 方法,这是一个抽象方法,具体的实现在 DispatcherServlet 中
3.最后就是 finally 中,这个里边干了两件事
第一件事就是将 LocaleContextHolder 和 RequestContextHolder 中对应的对象恢复成原来的样子(参考第一步);
第二件事就是通过 publishRequestHandledEvent 方法发布一个 ServletRequestHandledEvent 类型的消息。
1.3 processRequest 的事件发布
finally 代码块中会调用 publishRequestHandledEvent 方法发送一个 ServletRequestHandledEvent 类型的事件。
private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
long startTime, @Nullable Throwable failureCause) {
if (this.publishEvents && this.webApplicationContext != null) {
// Whether or not we succeeded, publish an event.
long processingTime = System.currentTimeMillis() - startTime;
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause, response.getStatus()));
}
}
二.LocalContext 和 RequestAttributes
2.1 LocalContext
LocaleContext 里面存放着Locale ,即本地化信息.
国际化的时候,如果需要用到Locale 对象,第一反应是从HttpServletRequest 中获取,类似:
Locale locale = req.getLocale();
但是,HttpServletRequest 只存放于Controller 中,如果想要在Service 层获取,就得cong Controller 中传参数过来,比较麻烦。
Spring MVC 提供了 LocaleContextHolder, 这个工具就是用来保存当前请求的LocaleContext, 是基于ThreadLocal 来保存变量,进而确保不同线程之间互不干扰。有了LocaleContextHolder 之后,就可以在任何地方获取Locale 了:
Locale locale = LocaleContextHolder.getLocale();
2.2 RequestAttributes
RequestAttributes 是一个接口,这个接口可以用来 get/set/remove 某一个属性。
RequestAttributes 有诸多实现类,默认使用的是 ServletRequestAttributes,通过 ServletRequestAttributes,我们可以 getRequest、getResponse 以及 getSession。
和 LocaleContext 类似,RequestAttributes 被保存在 RequestContextHolder
在 SpringMVC 中,如果我们需要在 Controller 之外的其他地方使用 request、response 以及 session,其实不用每次都从 Controller 中传递 request、response 以及 session 等对象,我们完全可以直接通过 RequestContextHolder 来获取:
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
HttpServletResponse response = servletRequestAttributes.getResponse();
网友评论