這一次搞懂SpringMVC原理
阿新 • • 發佈:2020-06-17
@[toc]
# 前言
前面幾篇文章,學習了Spring IOC、Bean例項化過程、AOP、事務的原始碼和設計思想,瞭解了Spring的整體執行流程,但如果是web開發,那麼必不可少的還有Spring MVC,本篇主要分析在請求呼叫過程中SpringMVC的實現原理,通過本篇要搞懂它是怎麼解決請求、引數、返回值對映等問題的。
# 正文
## 請求入口
我們都知道前端呼叫後端介面時,都會通過**Servlet**進行轉發,而**Servlet**的宣告週期包含下面四個階段:
- 例項化(new)
- 初始化(init)
- 執行(service呼叫doGet/doPost)
- 銷燬(destroy)
前兩個階段在Spring啟動階段就做好了(init根據配置可能是第一次請求時才會呼叫),銷燬是服務關閉的時候進行,本文主要分析的就是請求執行階段。我們知道SpringMVC的核心就是**DispatcherServlet**,該類是對**Servlet**的擴充套件,所以直接從該類的**service**方法開始,但在此類中沒有**service**方法,那肯定是在其父類中,我們先來看看其繼承體系:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200615201159713.png#pic_center)
逐個往上找,在**FrameworkServlet**方法中就有一個**service**方法:
```java
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);
}
}
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) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
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 {
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);
}
}
```
但其主要還是呼叫父類**HttpServlet**中的方法,而該類又會根據不同的請求方式會調到子類中,最後的核心方法就是**DispatcherServlet**中的**doDispatch**方法:
```java
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 {
//檔案上傳
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//這個方法很重要,重點看
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//獲取跟HandlerMethod匹配的HandlerAdapter物件
// Determine handler adapter for the current request.
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;
}
}
//前置過濾器,如果為false則直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//呼叫到Controller具體方法,核心方法呼叫,重點看看
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//中置過濾器
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);
}
//檢視渲染及後置過濾器執行
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);
}
}
}
}
```
MVC的所有處理邏輯都在這個方法中,先總結一下這個方法的實現邏輯,首先根據請求的url拿到快取中的**HandlerMethod**物件和**執行鏈**物件,**HandlerMethod**中封裝了controller物件、方法物件和方法引數等資訊,**執行鏈**則是包含了一個個**HandlerInterceptor**攔截器;然後再通過**HandlerMethod**拿到對應的**HandlerAdapter**,這個物件的作用就是去適配我們的controller;準備工作做完後,首先會執行**前置過濾**,如果被攔截則直接返回,否則就去呼叫controller中的方法執行我們的業務邏輯並返回一個**ModelView**物件;接著執行**中置過濾器**,以及處理全域性異常捕獲器捕獲到異常;最後進行**檢視渲染**返回並執行**後置過濾器**進行資源釋放等工作。
以上就是MVC的整體執行流程,下面就逐個來分析,首先進入**getHandler**方法:
```java
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//handlerMappering例項
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//獲取HandlerMethod和過濾器鏈的包裝類
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
```
是委託給**HandlerMapping**物件的,這是一個介面,主要的實現類是**RequestMappingHandlerMapping**,同樣先來看看其繼承體系:
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200615203440128.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2w2MTA4MDAz,size_16,color_FFFFFF,t_70#pic_center)
這個類是管理請求和處理類之間的對映關係的,你是否疑惑它是在哪裡例項化的呢?下面先來看看MVC元件的初始化。
## 元件初始化
這裡我以自動化配置的註解方式說明,Spring提供了一個@EnableWebMvc,通過前面的學習我們知道在這個註解中必定匯入了一個配置類,點進去可以看到是**DelegatingWebMvcConfiguration**,這個類就是負責MVC的元件和擴充套件實現的初始化,其本身我們先不看,先看其父類**WebMvcConfigurationSupport**,這個類我們應該不陌生,要做一些自定義擴充套件時就需要繼承該類(如攔截器Interceptor),同樣作用的類還有**WebMvcConfigurerAdapter**,這個類是對前者**相對安全**的擴充套件,為什麼是相對安全呢?因為繼承前者會導致自動配置失效,而使用後者則不必擔心此問題,只需要在類上加上@EnableWebMvc註解。
在**WebMvcConfigurationSupport**中我們可以看到很多@Bean標註的方法,也就是mvc元件的例項化,這裡主要看看**requestMappingHandlerMapping**,其餘的可自行閱讀理解,也就是一些Bean的註冊:
```java
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors());
mapping.setContentNegotiationManager(mvcContentNegotiationManager());
mapping.setCorsConfigurations(getCorsConfigurations());
......省略
return mapping;
}
```
這裡主要看**getInterceptors**方法如何獲取攔截器的:
```java
protected final Object[] getInterceptors() {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
//鉤子方法,需要自己定義
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
```
第一次進來會呼叫**addInterceptors**新增攔截器,這是一個模板方法,在子類**DelegatingWebMvcConfiguration**中實現:
```java
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
protected void addInterceptors(InterceptorRegistry registry) {
this.configurers.addInterceptors(registry);
}
public void addInterceptors(InterceptorRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addInterceptors(registry);
}
}
```
可以看到最終是呼叫**WebMvcConfigurer**的**addInterceptors**方法,也就是我們對**WebMvcConfigurerAdapter**的自定義擴充套件。看到這裡我們應該明白了MVC的元件是如何新增到IOC容器中的,但是**DispatcherServlet**又是怎麼獲取到它們的呢?回到之前的程式碼中,在**DispatcherServlet**這個類中有一個**onRefresh**方法,這個方法又呼叫了**initStrategies**方法完成了MVC九大元件的註冊:
```java
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts