SpringMVC原始碼分析-DispatcherServlet
DispatcherServlet 處理流程
在整個 Spring MVC 框架中,DispatcherServlet 處於核心位置,它負責協調和組織不同元件完成請求處理並返回響應工作。在看 DispatcherServlet 類之前,我們先來看一下請求處理的大致流程:
- Tomcat 啟動,對 DispatcherServlet 進行例項化,然後呼叫它的 init() 方法進行初始化,在這個初始化過程中完成了:
- 對 web.xml 中初始化引數的載入;
- 建立 WebApplicationContext (SpringMVC的IOC容器);
- 進行元件的初始化;
- 客戶端發出請求,由 Tomcat 接收到這個請求,如果匹配 DispatcherServlet 在 web.xml 中配置的對映路徑,Tomcat 就將請求轉交給 DispatcherServlet 處理;
- DispatcherServlet 從容器中取出所有 HandlerMapping 例項(每個例項對應一個 HandlerMapping 介面的實現類)並遍歷,每個 HandlerMapping 會根據請求資訊,通過自己實現類中的方式去找到處理該請求的 Handler (執行程式,如Controller中的方法),並且將這個 Handler 與一堆 HandlerInterceptor (攔截器) 封裝成一個 HandlerExecutionChain 物件,一旦有一個 HandlerMapping 可以找到 Handler 則退出迴圈;
- DispatcherServlet 取出 HandlerAdapter 元件,根據已經找到的 Handler,再從所有 HandlerAdapter 中找到可以處理該 Handler 的 HandlerAdapter 物件;
- 執行 HandlerExecutionChain 中所有攔截器的 preHandler() 方法,然後再利用 HandlerAdapter 執行 Handler ,執行完成得到 ModelAndView,再依次呼叫攔截器的 postHandler() 方法;
- 利用 ViewResolver 將 ModelAndView 或是 Exception(可解析成 ModelAndView)解析成 View,然後 View 會呼叫 render() 方法再根據 ModelAndView 中的資料渲染出頁面;
- 最後再依次呼叫攔截器的 afterCompletion() 方法,這一次請求就結束了。
DispatcherServlet 繼承自 HttpServlet,它遵循 Servlet 裡的“init-service-destroy”三個階段,首先我們先來看一下它的 init() 階段。
public class DispatcherServlet extends FrameworkServlet {
...
}
初始化
HttpServletBean 的 init() 方法
DispatcherServlet 的 init() 方法在其父類 HttpServletBean 中實現的,它覆蓋了 GenericServlet 的 init() 方法,主要作用是載入 web.xml 中 DispatcherServlet 的 配置,並呼叫子類的初始化。下面是 init() 方法的具體程式碼:
@Override
public final void init() throws ServletException {
// ServletConfigPropertyValues 是靜態內部類,使用 ServletConfig 獲取 web.xml 中配置的引數
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 使用 BeanWrapper 來構造 DispatcherServlet
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}catch (BeansException ex) {
throw ex;
}
}
// 讓子類實現的方法,這種在父類定義在子類實現的方式叫做模版方法模式
initServletBean();
}
web.xml 中 DispatcherServlet 的 配置
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:servlet-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
FrameworkServlet 的 initServletBean() 方法
在 HttpServletBean 的 init() 方法中呼叫了 initServletBean() 這個方法,它是在 FrameworkServlet 類中實現的,主要作用是建立 WebApplicationContext 容器(有時也稱上下文),並載入 SpringMVC 配置檔案中定義的 Bean 到改容器中,最後將該容器新增到 ServletContext 中。下面是 initServletBean() 方法的具體程式碼:
@Override
protected final void initServletBean() throws ServletException {
try {
// 初始化 WebApplicationContext (即SpringMVC的IOC容器)
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
} catch (ServletException ex) {
} catch (RuntimeException ex) {
}
}
WebApplicationContext 繼承於 ApplicationContext 介面,從容器中可以獲取當前應用程式環境資訊,它也是 SpringMVC 的 IOC 容器。下面是 initWebApplicationContext() 方法的具體程式碼:
protected WebApplicationContext initWebApplicationContext() {
// 獲取 ContextLoaderListener 初始化並註冊在 ServletContext 中的根容器,即 Spring 的容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 因為 WebApplicationContext 不為空,說明該類在構造時已經將其注入
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
// 將 Spring 的容器設為 SpringMVC 容器的父容器
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 如果 WebApplicationContext 為空,則進行查詢,能找到說明上下文已經在別處初始化。
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果 WebApplicationContext 仍為空,則以 Spring 的容器為父上下文建立一個新的。
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 模版方法,由 DispatcherServlet 實現
onRefresh(wac);
}
if (this.publishContext) {
// 釋出這個 WebApplicationContext 容器到 ServletContext 中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
下面是查詢 WebApplicationContext 的 findWebApplicationContext() 方法程式碼:
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
// 從 ServletContext 中查詢已經發布的 WebApplicationContext 容器
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
DispatcherServlet 的 onRefresh() 方法
建立好 WebApplicationContext(上下文) 後,通過 onRefresh(ApplicationContext context) 方法回撥,進入 DispatcherServlet 類中。onRefresh() 方法,提供 SpringMVC 的初始化,具體程式碼如下:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化檔案上傳處理類
initMultipartResolver(context);
//初始化本地化Resolver
initLocaleResolver(context);
//初始化主題Resolver
initThemeResolver(context);
//初始化一些個與處理的HandlerMappings
initHandlerMappings(context);
initHandlerAdapters(context);
//初始化異常處理的handler
initHandlerExceptionResolvers(context);
//初始化請求路徑轉換為ViewName 的Translator
initRequestToViewNameTranslator(context);
//初始化ViewResolvers 這個就是針對檢視處理的Resolvers 比如jsp處理Resolvers 或者freemarker處理Resolvers
initViewResolvers(context);
//初始化 主要管理flashmap,比如RedirectAttributes 的屬性會放到這個裡面,預設使用的是SessionFlashMapManager
initFlashMapManager(context);
}
在 initStrategies() 方法中進行了各個元件的初始化,先來看一下這些元件的初始化方法,稍後再來詳細分析這些元件。
initHandlerMappings 方法
initHandlerMappings() 方法從 SpringMVC 的容器及 Spring 的容器中查詢所有的 HandlerMapping 例項,並把它們放入到 handlerMappings 這個 list 中。這個方法並不是對 HandlerMapping 例項的建立,HandlerMapping 例項是在上面 WebApplicationContext 容器初始化,即 SpringMVC 容器初始化的時候建立的。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 從 SpringMVC 的 IOC 容器及 Spring 的 IOC 容器中查詢 HandlerMapping 例項
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// 按一定順序放置 HandlerMapping 物件
OrderComparator.sort(this.handlerMappings);
}
} else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果沒有 HandlerMapping,則載入預設的
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
initHandlerAdapters 方法
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
OrderComparator.sort(this.handlerAdapters);
}
} else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
} catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
// Ensure we have at least some HandlerAdapters, by registering
// default HandlerAdapters if no other adapters are found.
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
處理請求
HttpServlet 提供了 doGet()、doPost() 等方法,DispatcherServlet 中這些方法是在其父類 FrameworkServlet 中實現的,程式碼如下:
@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);
}
這些方法又都呼叫了 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);
// 返回當前繫結到執行緒的 RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 根據請求構建ServletRequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 獲取當前請求的 WebAsyncManager,如果沒有找到則建立
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 使 LocaleContext 和 requestAttributes 關聯
initContextHolders(request, localeContext, requestAttributes);
try {
// 由 DispatcherServlet 實現
doService(request, response);
} catch (ServletException ex) {
} catch (IOException ex) {
} catch (Throwable ex) {
} finally {
// 重置 LocaleContext 和 requestAttributes,解除關聯
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}// 釋出 ServletRequestHandlerEvent 事件
publishRequestHandledEvent(request, startTime, failureCause);
}
}
DispatcherServlet 的 doService() 方法主要是設定一些 request 屬性,並呼叫 doDispatch() 方法進行請求分發處理,doDispatch() 方法的主要過程是通過 HandlerMapping 獲取 Handler,再找到用於執行它的 HandlerAdapter,執行 Handler 後得到 ModelAndView ,ModelAndView 是連線“業務邏輯層”與“檢視展示層”的橋樑,接下來就要通過 ModelAndView 獲得 View,再通過它的 Model 對 View 進行渲染。doDispatch() 方法如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 獲取當前請求的WebAsyncManager,如果沒找到則建立並與請求關聯
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 檢查是否有 Multipart,有則將請求轉換為 Multipart 請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 遍歷所有的 HandlerMapping 找到與請求對應的 Handler,並將其與一堆攔截器封裝到 HandlerExecution 物件中。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 遍歷所有的 HandlerAdapter,找到可以處理該 Handler 的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 處理 last-modified 請求頭
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;
}
}
// 遍歷攔截器,執行它們的 preHandle() 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// 執行實際的處理程式
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
// 遍歷攔截器,執行它們的 postHandle() 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
// 處理執行結果,是一個 ModelAndView 或 Exception,然後進行渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
} catch (Error err) {
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// 遍歷攔截器,執行它們的 afterCompletion() 方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
HandlerMapping
作用是根據當前請求的找到對應的 Handler,並將 Handler(執行程式)與一堆 HandlerInterceptor(攔截器)封裝到 HandlerExecutionChain 物件中。在 HandlerMapping 介面的內部只有一個方法,如下:
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerMapping 是由 DispatcherServlet 呼叫,DispatcherServlet 會從容器中取出所有 HandlerMapping 例項並遍歷,讓 HandlerMapping 例項根據自己實現類的方式去嘗試查詢 Handler,而 HandlerMapping 具體有哪些實現類下面就會詳細分析。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 這些 HandlerMapping 在容器初始化時建立,在 initHandlerMappings 時放入集合中
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
另外上面說到的 Handler 有可能是一個 HandlerMethod(封裝了 Controller 中的方法)物件,也有可能是一個 Controller 物件、 HttpRequestHandler 物件或 Servlet 物件,而這個 Handler 具體是什麼物件,也是與所使用的 HandlerMapping 實現類有關。如下圖所示,可以看到 HandlerMapping 實現類有兩個分支,分別繼承自 AbstractHandlerMethodMapping(得到 HandlerMethod)和 AbstractUrlHandlerMapping(得到 HttpRequestHandler、Controller 或 Servlet),它們又統一繼承於 AbstractHandlerMapping。
先來看一下 AbstractHandlerMapping,它實現了 HandlerMapping 介面中的 getHandler() 方法,原始碼如下所示
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
相關推薦
SpringMVC原始碼分析-DispatcherServlet
DispatcherServlet 處理流程
在整個 Spring MVC 框架中,DispatcherServlet 處於核心位置,它負責協調和組織不同元件完成請求處理並返回響應工作。在看 DispatcherServlet 類之前,我們先來看一下請求處理的
SpringMVC原始碼分析--容器初始化(五)DispatcherServlet
上一篇部落格SpringMVC原始碼分析--容器初始化(四)FrameworkServlet我們已經瞭解到了SpringMVC容器的初始化,SpringMVC對容器初始化後會進行一系列的其他屬性的初始化操作,在SpringMVC初始化完成之後會呼叫onRefresh(wac
SpringMVC原始碼分析4:DispatcherServlet如何找到正確的Controller
SpringMVC是目前主流的Web MVC框架之一。
我們使用瀏覽器通過地址 http://ip:port/contextPath/path進行訪問,SpringMVC是如何得知使用者到底是訪問哪個Controller中的方法,這期間到底發生了什麼。
本文將分析SpringMVC是如何處理請求與
SpringMVC原始碼分析2:SpringMVC設計理念與DispatcherServlet
轉自:https://my.oschina.net/lichhao/blog
SpringMVC簡介
SpringMVC作為Struts2之後異軍突起的一個表現層框架,正越來越流行,相信javaee的開發者們就算沒使用過SpringMVC,也應該對其略有耳聞。我試圖通過對SpringMVC的設計思想和原始碼實
SpringMVC原始碼分析3:DispatcherServlet的初始化與請求轉發
在我們第一次學Servlet程式設計,學java web的時候,還沒有那麼多框架。我們開發一個簡單的功能要做的事情很簡單,就是繼承HttpServlet,根據需要重寫一下doGet,doPost方法,跳轉到我們定義好的jsp頁面。Servlet類編寫完之後在web.xml裡註冊這個Servlet類。
除此之外
JAVA springMVC原始碼分析
就簡單的寫一寫,面試可能會問,我一箇中級java開發,不會太多東西,大佬繞道!
建立一個Servlet工程,分三層寫好:
…
2.建立web.xml
<?xml version="1.0" encoding="UTF-8"?>
<disp
看透SpringMVC原始碼分析與實踐(一)
一、網站架構及其演變過程
1.軟體的三大型別
軟體分為三個型別:單機軟體、BS結構的軟體(瀏覽器-服務端)、CS結構的軟體(客戶端-服務端)。
2.BS的基礎結構
&nb
看透SpringMVC原始碼分析與實踐(二)
一、Tomcat的頂層結構及啟動過程
1.Tomcat的頂層結構
Tomcat中最頂層的容器叫Server,代表整個伺服器,Server至少包含一個Service用於具體的服務。Service主要包含兩部分,Connector和Conta
springMVC原始碼分析[email protected
@SessionAttribute作用於處理器類上,用於在多個請求之間傳遞引數,類似於Session的Attribute,但不完全一樣,一般來說@SessionAttribute設定的引數只用於暫時的傳遞,而不是長期的儲存,長期儲存的資料還是要放到Session中。通過@Se
SpringMVC原始碼分析1:SpringMVC概述
轉載自:https://blog.csdn.net/a724888/article/details/76014532
Web MVC簡介
1.1、Web開發中的請求-響應模型:
在Web世界裡,具體步驟如下:
1、 Web瀏覽器(如IE)發起請求,如訪問http:/
springMVC原始碼分析--國際化LocaleResolver(一)
springMVC給我們提供了國際化支援,簡單來說就是設定整個系統的執行語言,然後根據系統的執行語言來展示對應語言的頁面,一般我們稱之為多語言。springMVC國際化機制就是可以設定整個系統的執行語言,其定義了一個國際化支援介面LocaleResolver
SpringMVC原始碼分析--容器初始化(四)FrameworkServlet
一下SpringMVC配置檔案的地址contextConfigLocation的配置屬性,然後其呼叫的子類FrameworkServlet的initServletBean方法。
其實FrameworkServlet是springMVC初始化IOC容器的核心,通過讀取配置的c
springMVC原始碼分析--容器初始化(一)ContextLoaderListener
在spring Web中,需要初始化IOC容器,用於存放我們注入的各種物件。當tomcat啟動時首先會初始化一個web對應的IOC容器,用於初始化和注入各種我們在web執行過程中需要的物件。當tomcat啟動的時候是如何初始化IOC容器的,我們先看一下在web.xml中經常看
springMVC原始碼分析--異常處理機制HandlerExceptionResolver執行原理(二)
上一篇部落格springMVC原始碼分析--異常處理機制HandlerExceptionResolver簡單示例(一)中我們簡單地實現了一個異常處理例項,接下來我們要介紹一下HandlerExceptionResolver是如何捕獲到Controller中丟擲的異常並展示到前
springMVC原始碼分析--異常處理機制HandlerExceptionResolver簡單示例(一)
springMVC對Controller執行過程中出現的異常提供了統一的處理機制,其實這種處理機制也簡單,只要丟擲的異常在DispatcherServlet中都會進行捕獲,這樣就可以統一的對異常進行處理。 springMVC提供了一個HandlerExcepti
springMVC原始碼分析--ControllerBeanNameHandlerMapping(八)
在上一篇部落格springMVC原始碼分析--AbstractControllerUrlHandlerMapping(六)中我們介紹到AbstractControllerUrlHandlerMapping定義了抽象方法buildUrlsForHandler,接下來我們看看在其
springMVC原始碼分析--攔截器HandlerExecutionChain(三)
上一篇部落格springMVC原始碼分析--HandlerInterceptor攔截器呼叫過程(二)中我們介紹了HandlerInterceptor的執行呼叫地方,最終HandlerInterceptor呼叫的地方是在HandlerExecutionChain中,接下來我們就
springMVC原始碼分析--ControllerClassNameHandlerMapping(九)
在上一篇部落格springMVC原始碼分析--AbstractControllerUrlHandlerMapping(六)中我們介紹到AbstractControllerUrlHandlerMapping定義了抽象方法buildUrlsForHandler,接下來我們看看在其
springMVC原始碼分析--動態樣式ThemeResolver(二)
ThemeResolver的體系結構如下:1、介面ThemeResolver中定義的介面是比較簡單的,提供兩個介面:(1)resolveThemeName獲取樣式名(2)setThemeName設定樣式名public interface ThemeResolver {
/
springMVC原始碼分析--AbstractHandlerMethodMapping獲取url和HandlerMethod對應關係(十)
在之前的部落格 springMVC原始碼分析--AbstractHandlerMapping(二)中我們介紹了AbstractHandlerMethodMapping的父類AbstractHandlerMapping,其定義了抽象方法getHandlerInternal(Ht