- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
11.4 DispatcherServlet 的逻辑处理
根据之前的示例,我们知道在 HttpServlet 类中分别提供了相应的服务方法,它们是 doDelete()、doGet()、doOptions()、doPost()、doPut() 和 doTrace(),它会根据请求的不同形式将程序引导至对应的函数进行处理。这几个函数中最常用的函数无非就是 doGet() 和 doPost(),那么我们就直接查看 DispatcherServlet 中对于这两个函数的逻辑实现。
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
对于不同的方法,Spring 并没有做特殊处理,而是统一将程序再一次地引导至 process Request(request, response) 中。
protected final void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
//记录当前时间,用于计算 web 请求的处理时间
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// Expose current LocaleResolver and request as LocaleContext.
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(buildLocaleContext(request),
this.threadContextInheritable);
// Expose current RequestAttributes to current thread.
RequestAttributes previousRequestAttributes = RequestContextHolder. GetRequest
Attributes();
ServletRequestAttributes requestAttributes = null;
if (previousRequestAttributes == null || previousRequestAttributes. getClass().
equals(ServletRequestAttributes.class)) {
requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes,
this.threadContextInheritable);
}
if (logger.isTraceEnabled()) {
logger.trace("Bound request context to thread: " + request);
}
try {
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 {
// Clear request attributes and reset thread-bound context.
LocaleContextHolder.setLocaleContext(previousLocaleContext,this.threadContext
Inheritable);
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(previousRequestAttributes,
this.threadContextInheritable);
requestAttributes.requestCompleted();
}
if (logger.isTraceEnabled()) {
logger.trace("Cleared thread-bound request context: " + request);
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
this.logger.debug("Successfully completed request");
}
}
if (this.publishEvents) {
// 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));
}
}
}
函数中已经开始了对请求的处理,虽然把细节转移到了 doService 函数中实现,但是我们不难看出处理请求前后所做的准备与处理工作。
(1)为了保证当前线程的 LocaleContext 以及 RequestAttributes 可以在当前请求后还能恢复,提取当前线程的两个属性。
(2)根据当前 request 创建对应的 LocaleContext 和 RequestAttributes,并绑定到当前线程。
(3)委托给 doService 方法进一步处理。
(4)请求处理结束后恢复线程到原始状态。
(5)请求处理结束后无论成功与否发布事件通知。
继续查看 doService 方法。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws
Exception {
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("DispatcherServlet with name '" + getServletName() + "'
processing " + request.getMethod() +
" request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith ("org.Springframework.
web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute (attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request,
response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap
(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
doDispatch(request, response);
}
finally {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
我们猜想对请求处理至少应该包括一些诸如寻找 Handler 并页面跳转之类的逻辑处理,但是,在 doService 中我们并没有看到想看到的逻辑,相反却同样是一些准备工作,但是这些准备工作却是必不可少的。Spring 将已经初始化的功能辅助工具变量,比如 localeResolver、themeResolver 等设置在 request 属性中,而这些属性会在接下来的处理中派上用场。
经过层层的准备工作,终于在 doDispatch 函数中看到了完整的请求处理过程。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
//如果是 MultipartContent 类型的 request 则转换 request 为 MultipartHttpServletRequest 类型的
request
processedRequest = checkMultipart(request);
//根据 request 信息寻找对应的 Handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果没有找到对应的 handler 则通过 response 反馈错误信息
noHandlerFound(processedRequest, response);
return;
}
//根据当前的 handler 寻找对应的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//如果当前 handler 支持 last-modified 头处理
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.
getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is:
" + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified
(lastModified) && isGet) {
return;
}
}
//拦截器的 preHandler 方法的调用
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response,
mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex,
processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
//真正的激活 handler 并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//视图名称转换应用于需要添加前缀后缀的情况
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
//应用所有拦截器的 postHandle 方法
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.
getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv =processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
//如果在 Handler 实例的处理中返回了 view,那么需要做页面的处理
if (mv != null && !mv.wasCleared()) {
//处理页面跳转
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet
with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
//完成处理激活触发器
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,
response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,
response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing
failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest,
response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
doDispatch 函数中展示了 Spring 请求处理所涉及的主要逻辑,而我们之前设置在 request 中的各种辅助属性也都有被派上了用场。下面回顾一下逻辑处理的全过程。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论