Spring MVC 執行流程詳解:一次請求經歷了什么?
引言
在現代 Web 開發中,Spring MVC 作為 Spring 框架的重要組成部分,廣泛應用于構建靈活、可擴展的 Java Web 應用。作為一個基于 MVC(Model-View-Controller)架構的框架,Spring MVC 提供了強大的請求處理機制,能夠幫助開發者高效地構建 Web 層邏輯。然而,對于初學者或者對底層機制不熟悉的開發者來說,Spring MVC 的執行流程往往顯得神秘而復雜。
本文將深入解析 Spring MVC 的執行流程,從用戶發起 HTTP 請求開始,到最終返回響應為止,詳細探討每一個環節的作用和實現原理。通過這篇文章,你將全面了解 Spring MVC 是如何一步步處理請求的,包括 DispatcherServlet 的作用、HandlerMapping 的匹配機制、Controller 的調用方式、視圖解析與渲染等核心組件的工作原理。
無論你是剛剛接觸 Spring MVC 的新手,還是希望深入了解其內部機制的中級開發者,本文都將為你提供清晰的知識體系和實踐指導。
一、Spring MVC 簡介與基本概念
1.1 什么是 Spring MVC?
Spring MVC 是 Spring 框架中的一個模塊,專門用于構建 Web 應用程序。它基于經典的 Model-View-Controller(MVC)設計模式,旨在分離業務邏輯、數據模型和用戶界面,從而提高代碼的可維護性和可測試性。
在 Spring MVC 中:
- Model(模型):負責封裝應用的數據和業務邏輯。
- View(視圖):負責展示數據,通常為 HTML 頁面或 JSON 數據。
- Controller(控制器):接收用戶的請求,協調 Model 和 View,完成業務邏輯并返回適當的視圖。
1.2 Spring MVC 的核心組件
Spring MVC 的運行依賴于多個核心組件協同工作,主要包括:
- DispatcherServlet:前端控制器,是整個請求處理的核心入口。
- HandlerMapping:處理器映射器,負責根據請求 URL 找到對應的 Controller。
- Controller:實際處理請求的類,通常使用
@Controller
或@RestController
注解標記。 - ViewResolver:視圖解析器,負責將邏輯視圖名解析為實際的視圖對象(如 JSP、Thymeleaf 等)。
- View:視圖對象,負責將模型數據渲染成客戶端可識別的格式(如 HTML、JSON 等)。
- HandlerAdapter:處理器適配器,負責調用具體的 Controller 方法。
- HandlerExceptionResolver:異常解析器,用于處理 Controller 拋出的異常。
- LocaleResolver:本地化解析器,用于支持多語言。
- ThemeResolver:主題解析器,用于支持不同的 UI 主題。
這些組件構成了 Spring MVC 的骨架,它們之間通過一定的流程進行協作,從而完成一次完整的請求處理。
二、Spring MVC 的執行流程概覽
Spring MVC 的執行流程可以分為以下幾個主要步驟:
- 用戶發送 HTTP 請求 到服務器。
- DispatcherServlet 接收請求,作為前端控制器,它是所有請求的統一入口。
- HandlerMapping 查找對應的 Controller,根據請求的 URL 匹配合適的處理器。
- HandlerAdapter 調用 Controller,執行具體的業務邏輯。
- Controller 返回 ModelAndView 對象,包含模型數據和視圖信息。
- ViewResolver 解析視圖名稱,找到實際的視圖資源(如 JSP 文件)。
- View 渲染模型數據,生成最終的響應內容(如 HTML 頁面)。
- DispatcherServlet 將響應返回給客戶端。
接下來我們將逐個步驟進行詳細分析。
三、DispatcherServlet:前端控制器
3.1 什么是 DispatcherServlet?
DispatcherServlet
是 Spring MVC 的核心組件之一,它是 Servlet 的子類,繼承自 HttpServlet
。它的職責是接收所有的 HTTP 請求,并協調其他組件完成整個請求的處理過程。
在傳統的 Web 應用中,每個請求都可能由不同的 Servlet 處理。而在 Spring MVC 中,所有的請求都會首先被 DispatcherServlet
攔截,然后由它決定如何分發和處理這些請求。
3.2 DispatcherServlet 的配置
在 web.xml
中,我們需要配置 DispatcherServlet
并指定其映射路徑。例如:
<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/spring-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>dispatcher</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
在這個例子中,我們配置了一個名為 dispatcher
的 DispatcherServlet
,并將其映射到 /
路徑,表示它可以處理所有進入的請求。contextConfigLocation
參數指定了 Spring 配置文件的位置。
3.3 DispatcherServlet 的初始化
當 Web 容器(如 Tomcat)啟動時,DispatcherServlet
會加載其關聯的 Spring 配置文件(通常是 <servlet-name>-servlet.xml
),并初始化一系列的組件,如 HandlerMapping、ViewResolver、HandlerAdapter 等。
這些組件在后續的請求處理過程中將發揮關鍵作用。
四、HandlerMapping:處理器映射器
4.1 HandlerMapping 的作用
HandlerMapping
的作用是根據請求的 URL 找到對應的 Controller 類和方法。Spring MVC 支持多種類型的 HandlerMapping,最常見的是:
- BeanNameUrlHandlerMapping:根據 Bean 名稱來映射 URL。
- SimpleUrlHandlerMapping:手動配置 URL 與 Controller 的映射關系。
- RequestMappingHandlerMapping:基于注解的方式(如
@RequestMapping
、@GetMapping
、@PostMapping
等)來匹配 URL。
其中,RequestMappingHandlerMapping
是默認使用的 HandlerMapping,它會掃描所有帶有 @RequestMapping
注解的類和方法,并建立 URL 與 Controller 的映射關系。
4.2 HandlerMapping 的工作機制
當 DispatcherServlet
接收到請求后,會調用 getHandler()
方法獲取對應的處理器(Handler)。這個方法會遍歷所有的 HandlerMapping 實例,直到找到第一個能夠處理該請求的 Handler。
以 RequestMappingHandlerMapping
為例,它會檢查請求的 URL 是否與某個 @RequestMapping
注解的方法匹配。如果匹配成功,則返回一個封裝了目標 Controller 及其方法的 HandlerExecutionChain
對象。
例如:
@GetMapping("/hello")
public String sayHello() {return "hello";
}
當訪問 /hello
時,RequestMappingHandlerMapping
會找到這個方法,并將其封裝為一個 Handler。
五、HandlerAdapter:處理器適配器
5.1 HandlerAdapter 的作用
HandlerAdapter
的作用是調用具體的 Controller 方法,并處理其返回值。由于 Spring MVC 支持多種類型的 Controller(如基于注解的 Controller、基于接口的 Controller 等),因此需要不同的 HandlerAdapter 來適配不同類型的處理器。
常見的 HandlerAdapter 包括:
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
AnnotationMethodHandlerAdapter
(舊版本)RequestMappingHandlerAdapter
(新版本)
目前最常用的是 RequestMappingHandlerAdapter
,它用于處理基于注解的 Controller 方法。
5.2 HandlerAdapter 的調用流程
一旦 DispatcherServlet
獲取到了 Handler,它就會調用 getHandlerAdapter()
方法,找到能夠處理當前 Handler 的適配器。隨后調用 handle()
方法,真正執行 Controller 中的方法。
例如,在 RequestMappingHandlerAdapter
內部,會調用 invokeHandlerMethod()
方法來反射執行 Controller 方法,并處理參數綁定、返回值處理等工作。
六、Controller:處理請求的邏輯中心
6.1 Controller 的定義方式
在 Spring MVC 中,Controller 可以通過以下幾種方式定義:
- 使用
@org.springframework.stereotype.Controller
注解標注的類。 - 使用
@org.springframework.web.bind.annotation.RestController
注解,適用于 RESTful API。 - 實現
Controller
接口(不推薦)。 - 實現
HttpRequestHandler
接口(也不推薦)。
推薦使用 @Controller
或 @RestController
,因為它們更簡潔、易讀,并且與 Spring 的自動掃描機制兼容。
6.2 Controller 的請求處理
Controller 的方法可以通過各種注解來指定請求類型、URL 映射、參數綁定等行為,例如:
@RestController
public class HelloController {@GetMapping("/hello")public String sayHello(@RequestParam String name) {return "Hello, " + name;}
}
在這個例子中:
@RestController
表示這是一個 REST 控制器,返回值直接寫入 HTTP 響應體。@GetMapping("/hello")
表示該方法處理 GET 請求,路徑為/hello
。@RequestParam String name
表示從請求參數中提取name
參數。
當用戶訪問 /hello?name=Tom
時,Spring MVC 會自動將 name
參數注入到方法中,并返回 "Hello, Tom"
。
七、ModelAndView:模型與視圖的載體
7.1 ModelAndView 的結構
ModelAndView
是 Spring MVC 中用于封裝模型數據和視圖信息的對象。它包含兩個部分:
- Model:存儲模型數據,通常是一個 Map 結構,鍵值對形式。
- View:表示視圖信息,可以是視圖名稱或實際的視圖對象。
7.2 ModelAndView 的創建與使用
Controller 可以通過返回 ModelAndView
對象來同時傳遞模型數據和指定視圖。例如:
@Controller
public class UserController {@GetMapping("/user/{id}")public ModelAndView getUser(@PathVariable Long id) {User user = userService.findById(id);ModelAndView modelAndView = new ModelAndView("userDetail");modelAndView.addObject("user", user);return modelAndView;}
}
在這個例子中,userDetail
是視圖名稱,modelAndView.addObject("user", user)
將用戶對象添加到模型中。
八、ViewResolver:視圖解析器
8.1 ViewResolver 的作用
ViewResolver
的作用是將邏輯視圖名解析為實際的視圖對象。Spring MVC 提供了多種內置的 ViewResolver,如:
InternalResourceViewResolver
:用于解析 JSP 視圖。ThymeleafViewResolver
:用于解析 Thymeleaf 模板。FreeMarkerViewResolver
:用于解析 FreeMarker 模板。VelocityViewResolver
:用于解析 Velocity 模板。
8.2 ViewResolver 的配置與使用
以 InternalResourceViewResolver
為例,通常在 Spring 配置文件中這樣配置:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/views/" /><property name="suffix" value=".jsp" />
</bean>
這樣,當 Controller 返回 "userDetail"
時,ViewResolver
會將其解析為 /WEB-INF/views/userDetail.jsp
。
九、View:視圖的渲染
9.1 View 的作用
View
是 Spring MVC 中用于渲染模型數據的實際視圖對象。它負責將模型數據轉換為客戶端可以理解的格式(如 HTML、JSON 等)。
常見的 View 實現包括:
JstlView
:用于渲染 JSP 頁面。JsonView
:用于輸出 JSON 格式的數據。AbstractTemplateView
:用于模板引擎(如 Thymeleaf、Freemarker)。
9.2 View 的渲染過程
當 ViewResolver
找到對應的 View
后,DispatcherServlet
會調用 render()
方法,將模型數據傳入視圖進行渲染。
例如,對于 JSP 頁面,模型數據會被放入 request.setAttribute()
中,然后通過 JSP EL 表達式顯示出來:
<h1>User Detail</h1>
<p>Name: ${user.name}</p>
<p>Email: ${user.email}</p>
十、異常處理與錯誤頁面
10.1 HandlerExceptionResolver 的作用
HandlerExceptionResolver
是 Spring MVC 中用于處理 Controller 拋出異常的組件。它可以在發生異常時返回特定的視圖或錯誤碼。
常見的實現類包括:
SimpleMappingExceptionResolver
:根據異常類型映射到特定視圖。ResponseStatusExceptionResolver
:處理帶有@ResponseStatus
注解的異常。DefaultHandlerExceptionResolver
:處理 Spring 自身拋出的標準異常。
10.2 全局異常處理(@ControllerAdvice)
從 Spring 3.2 開始,可以使用 @ControllerAdvice
注解實現全局異常處理:
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(ResourceNotFoundException.class)public ModelAndView handleResourceNotFound() {ModelAndView modelAndView = new ModelAndView("error/404");return modelAndView;}@ExceptionHandler(Exception.class)public ModelAndView handleGeneralError() {ModelAndView modelAndView = new ModelAndView("error/general");return modelAndView;}
}
這樣可以集中處理異常,避免在每個 Controller 中重復編寫異常處理邏輯。
十一、國際化支持(LocaleResolver)
11.1 LocaleResolver 的作用
LocaleResolver
用于確定當前請求的語言環境(Locale),從而支持多語言切換。常見的實現類包括:
AcceptHeaderLocaleResolver
:基于 HTTP 請求頭中的Accept-Language
字段。CookieLocaleResolver
:基于 Cookie 中保存的 Locale。SessionLocaleResolver
:基于 Session 中保存的 Locale。
11.2 配置 LocaleResolver
在 Spring 配置文件中配置:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"><property name="defaultLocale" value="en"/>
</bean>
還可以配合 LocaleChangeInterceptor
實現動態切換語言:
<mvc:interceptors><bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"><property name="paramName" value="lang"/></bean>
</mvc:interceptors>
這樣,用戶可以通過 ?lang=en
或 ?lang=zh
來切換語言。
十二、攔截器(HandlerInterceptor)
12.1 攔截器的作用
攔截器(HandlerInterceptor
)允許我們在請求到達 Controller 之前或之后執行一些通用邏輯,例如權限驗證、日志記錄、性能監控等。
12.2 攔截器的生命周期
攔截器有三個主要方法:
preHandle()
:在 Controller 方法執行前調用,返回值為布爾值,決定是否繼續執行后續流程。postHandle()
:在 Controller 方法執行后調用,但視圖尚未渲染。afterCompletion()
:在整個請求完成后調用,可用于釋放資源。
12.3 自定義攔截器示例
@Component
public class LoggingInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Request URL: " + request.getRequestURL());return true; // 繼續執行}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("Post Handle method called.");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("Request completed.");}
}
在配置文件中注冊攔截器:
<mvc:interceptors><bean class="com.example.LoggingInterceptor"/>
</mvc:interceptors>
十三、總結:一次請求的完整流程回顧
現在我們已經詳細介紹了 Spring MVC 的各個核心組件及其作用。為了加深理解,我們可以再次回顧一次完整的請求流程:
- 用戶在瀏覽器中輸入 URL,發送 HTTP 請求。
- 請求被
DispatcherServlet
攔截。 DispatcherServlet
調用HandlerMapping
,查找匹配的 Controller。- 找到對應的 Controller 后,
DispatcherServlet
調用HandlerAdapter
來執行 Controller 方法。 - Controller 方法執行完畢后返回
ModelAndView
。 ViewResolver
解析視圖名稱,定位實際的視圖資源。View
渲染模型數據,生成 HTML、JSON 等響應內容。- 最終響應通過
DispatcherServlet
返回給客戶端。 - 整個過程中還可能涉及異常處理、攔截器、國際化等功能。
十四、結語
通過對 Spring MVC 執行流程的深入剖析,我們可以更好地理解其背后的機制和組件之間的協作關系。掌握這些知識不僅有助于我們寫出更高效的代碼,也能在調試和優化性能時提供有力的支持。
如果你正在學習 Spring MVC 或者正在開發企業級 Web 應用,建議你結合源碼閱讀和實際項目經驗,進一步加深對這些組件的理解。隨著你對 Spring 生態系統的不斷深入,你會發現 Spring MVC 的強大之處遠不止于此。