說明
- 本文基于 jdk 8, spring-framework 5.2.x 編寫。
- @author JellyfishMIX - github / blog.jellyfishmix.com
- LICENSE GPL-2.0
DispatcherServlet 的繼承實作層次
關注點應放在 Servlet, GenericServlet, HttpServlet, HttpServletBean, FrameworkServlet, DispatcherServlet

Servlet
javax.servlet.Servlet
package javax.servlet;
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
Servlet 接口提供處理請求的能力。請求來自于 web 容器,例如 tomcat, JBoss, Jetty 等。spring-web 應用和 web 容器共同遵守 Servlet 規範。Servlet 位于 javax.servlet 包内,不是 spring 的領域。
GenericServlet
javax.servlet.GenericServlet
package javax.servlet;
public abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable {
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
}
GenericServlet 沒有實作 service 方法,沒有處理請求的邏輯。實作了 init 方法,但是個模闆方法,由子類實作,例如 spring 的 HttpServletBean 就實作了該模闆方法。GenericServlet 位于 javax.servlet 包内,不是 spring 的領域。
HttpServlet
javax.servlet.http.HttpServlet
- 基于 Servlet 規範的應用可以響應所有類型的請求(不僅僅是 HTTP,也可以是 WebSocket, FTP 等),但通常用來處理 HTTP 請求。
- Servlet 接口有一個非常重要的實作類 HttpServlet,實作了 service 方法,SpringMVC 架構中的 DispatcherServlet 就是繼承自 HttpServlet。HttpServlet 位于 javax.servlet.http 包内,不是 spring 的領域。
package javax.servlet.http;
public abstract class HttpServlet extends GenericServlet {
}
HttpServlet#service 方法
javax.servlet.http.HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
- 轉換為 HttpServletRequest 和 HttpServletResponse。
- 判斷是何種類型的 http 請求,調用對應的 doXX 方法。GET 請求調用 doGet,POST 請求調用 doPost。doXX 方法是 HttpServlet 接口定義的。
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
FrameworkServlet
org.springframework.web.servlet.FrameworkServlet
- FrameworkServlet 在 spring 的包中,進入到 spring 的領域。
- FrameworkServlet 重寫了 doGet(), doPost() 等 doXX 方法,doXX 方法調用了 processRequest() 方法。
package org.springframework.web.servlet;
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
}
FrameworkServlet#processRequest 方法
org.springframework.web.servlet.FrameworkServlet#processRequest
- i18n 國際化設定。
- 建立 ServletRequestAttributes 對象,初始化上下文 holders,将 request, response 對象放入到線程上下文中。
- 調用 doService() 方法,處理請求。核心方法,由子類實作。
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// i18n 國際化設定
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
// 建立 ServletRequestAttributes 對象,初始化上下文 holders,将 request, response 對象放入到線程上下文中
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) {
// ...
}
catch (Throwable ex) {
// ...
} finally {
// ...
}
}
FrameworkServlet#initServletBean 方法
org.springframework.web.servlet.FrameworkServlet#initServletBean
- FrameworkServlet 還實作了 HttpServletBean 提供的模版方法 initServletBean,初始化 WebApplicationContext。調用 initFrameworkServlet 模闆方法,供子類實作初始化工作。
protected final void initServletBean() throws ServletException {
// 日志代碼移除,保留核心代碼
// 初始化 WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 模闆方法,供子類實作初始化工作
initFrameworkServlet();
}
DispatcherServlet#doService 方法
org.springframework.web.servlet.DispatcherServlet#doService
- 首先判斷是不是 include 請求,如果是則對 request 的 attribute 做個快照備份。
- 為 request 設定 WebApplicationContext, 國際化 i18n 相關 resolver, theme 主題相關 resolver
- 重定向屬性處理,對 redirect 請求的支援。
- 調用 DispatcherServlet#doDispatch 方法,核心方法,請求分發給對應的 handler 并處理。
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
// 首先判斷是不是 include 請求,如果是則對 request 的 attribute 做個快照備份
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
// 為 request 設定 WebApplicationContext
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
// 為 request 設定國際化 i18n 相關 resolver
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
// 為 reqeust 設定 theme 主題相關 resolver
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
// 重定向屬性處理,對 redirect 請求的支援
if (this.flashMapManager != null) {
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 {
// 核心方法,請求分發給對應的 handler 并處理
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
// doDispatch 方法執行完後,如果不是異步調用且未完成,對已備份好的快照進行還原,在做完快照後又對 request 設定了一些屬性
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
DispatcherServlet#doDispatch
org.springframework.web.servlet.DispatcherServlet#doDispatch
- 先檢查請求是不是 multipart 請求,如果是則傳回已處理的 request,否則不對 request 做額外處理。
- 周遊 HandlerMappings 集合,根據 HandlerMapping 擷取 HandlerExecutionChain,即 mappedHandler。
- 根據 mappedHandler 擷取适配的 HandlerAdapter。
- 以責任鍊模式調用 HandlerInterceptor 攔截器的 preHandle 方法(自定義的 spring 攔截器調用位置)。
- 調用 HandlerAdapter#handle 方法,處理請求。實際 Controller 最終在這裡面被調用,處理完後會傳回 ModelAndView。
- 将 HttpServletRequest 請求的 uri 轉換成 ViewName 字元串并設定到 ModelAndView 中。
- 以責任鍊模式調用 HandlerInterceptor 攔截器的 postHandle 方法(自定義的 spring 攔截器調用位置)。
- 渲染 view 視圖,調用攔截器的 afterCompletion() 方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 先檢查請求是不是 multipart 請求,如果是則傳回已處理的 request,否則不對 request 做額外處理
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 周遊 HandlerMappings 集合,根據 HandlerMapping 擷取 HandlerExecutionChain,即 mappedHandler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 根據 mappedHandler 擷取适配的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 以責任鍊模式調用 HandlerInterceptor 攔截器的 preHandle 方法(自定義的 spring 攔截器調用位置)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 調用 HandlerAdapter#handle 方法,處理請求。實際 Controller 最終在這裡面被調用,處理完後會傳回 ModelAndView。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 将 HttpServletRequest 請求的 uri 轉換成 ViewName 字元串并設定到 ModelAndView 中
applyDefaultViewName(processedRequest, mv);
// 以責任鍊模式調用 HandlerInterceptor 攔截器的 postHandle 方法(自定義的 spring 攔截器調用位置)
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 渲染 view 視圖,調用攔截器的 afterCompletion() 方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
DispatcherServlet#getHandler 方法
org.springframework.web.servlet.DispatcherServlet#getHandler
- 周遊 handlerMapping 從中獲得 HandlerExecutionChain
- DispatcherServlet 初始化時注冊的 handlerMapping, @see DispatcherServlet#initStrategies
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// DispatcherServlet 初始化時注冊的 handlerMapping, @see DispatcherServlet#initStrategies
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
DispatcherServlet#getHandlerAdapter 方法
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
根據 mappedHandler 擷取适配的 HandlerAdapter。
- 根據 HandlerExecutionChain 擷取适配的 HandlerAdapter。責任鍊模式,周遊 HandlerAdapter 中調用 HandlerAdapter#support() 方法,判斷目前 HandlerAdapter 是否支援該處理器,支援就傳回該 HandlerAdapter。
- DispatcherServlet 初始化時注冊的 HandlerAdapter, @see DispatcherServlet#initStrategies
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
DispatcherServlet#applyDefaultViewName 方法
org.springframework.web.servlet.DispatcherServlet#applyDefaultViewName
将 HttpServletRequest 請求的 uri 轉換成 ViewName 字元串并設定到 ModelAndView 中
- 判斷 ModelAndView 是否需要 view 視圖層。
- getDefaultViewName 方法使用了 RequestToViewNameTranslator,将 HttpServletRequest 請求的 uri 轉換成 ViewName 字元串并設定到 ModelAndView 中。
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
// 判斷 ModelAndView 是否需要 view 視圖層
if (mv != null && !mv.hasView()) {
// getDefaultViewName 方法使用了 RequestToViewNameTranslator,将 HttpServletRequest 請求的 uri 轉換成 ViewName 字元串并設定到 ModelAndView 中
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
@Nullable
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}
@Override
public String getViewName(HttpServletRequest request) {
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
return (this.prefix + transformPath(lookupPath) + this.suffix);
}
DispatcherServlet#processDispatchResult 方法 – 處理 Handler 執行後的結果
org.springframework.web.servlet.DispatcherServlet#processDispatchResult
處理 Handler 執行後的結果,将 ModelAndView 或者 Exception 渲染成視圖,然後調用 render() 生成頁面。
- handler 執行後如果存在 Exception,責任鍊模式調用 HandlerExceptionResolver,将 Exception 處理成 ModelAndView。
- 如果 ModelAndView 不為空,調用 render 方法生成頁面。
- 以責任鍊模式調用 HandlerInterceptor#afterCompletion() 方法。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
// handler 執行後如果存在 Exception,将 Exception 處理為 ModelAndView
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
// 責任鍊模式調用 HandlerExceptionResolver,将 Exception 處理成 ModelAndView
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 如果 ModelAndView 不為空,調用 render 方法生成頁面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ...
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
// 以責任鍊模式調用 HandlerInterceptor#afterCompletion() 方法
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
DispatcherServlet#render 方法 – 生成 View
org.springframework.web.servlet.DispatcherServlet#render
- 責任鍊模式調用 ViewResolver#resolveViewName 方法,生成 View
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 責任鍊模式調用 ViewResolver#resolveViewName 方法,生成 View
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
} else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}