拼多多一面:聊聊 Spring MVC 的工作原理!
都說拼多多是程序員的終點站,這篇文章,我們來分析一道它的 1面題目:聊聊Spring MVC的工作原理。
Spring MVC(Model-View-Controller)是 Spring框架中用于構建 Web應用程序的模塊之一,它遵循經典的MVC設計模式,將應用程序的不同方面分離,以實現更好的組織和可維護性。下面,我們將從架構,工作流程,核心組件分析等角度來詳細分析 Spring MVC的工作原理。
Spring MVC的架構
Spring MVC的架構主要由以下幾個核心組件構成:
- DispatcherServlet:這是Spring MVC的前端控制器,負責接收HTTP請求并將其分發給合適的處理器進行處理。它是整個Spring MVC的中央調度器。
- HandlerMapping:用于將請求映射到具體的處理器(Controller)上。它根據請求的URL、HTTP方法等信息來確定哪個控制器應該處理該請求。
- Controller:處理具體業務邏輯的組件,它接收來自DispatcherServlet的請求,調用業務服務處理后,返回一個ModelAndView對象。
- ModelAndView:它是Spring MVC中用于返回模型數據和視圖名稱的對象,控制器通過它將處理結果傳遞給視圖層。
- ViewResolver:視圖解析器,用于將邏輯視圖名稱解析成具體的視圖對象(如JSP、Thymeleaf等)。
- View:視圖用于渲染最終的結果給用戶,它可以是多種形式的,比如HTML、JSON、XML等。
整體結構如下圖:
Spring MVC的工作流程
Spring MVC的請求處理流程可以分為以下幾個步驟:
- 請求接收:用戶發送一個HTTP請求到服務器,DispatcherServlet作為前端控制器接收到該請求。
- 請求映射:DispatcherServlet調用HandlerMapping來查找匹配的處理器(Controller)。HandlerMapping根據請求的URL、請求參數等進行匹配。
- 調用處理器:找到處理器后,DispatcherServlet將請求轉發給具體的Controller進行處理。
- 業務處理:Controller執行具體的業務邏輯操作,通常會調用服務層或DAO層的方法處理數據。
- 返回ModelAndView:業務處理完畢后,Controller返回一個ModelAndView對象,其中包含視圖名和模型數據。
- 視圖解析:DispatcherServlet接收ModelAndView后,調用ViewResolver來解析視圖名,得到具體的視圖對象。
- 視圖渲染:視圖對象根據模型數據進行渲染,生成最終的輸出結果。
- 響應返回:渲染完畢后,將結果返回給用戶,整個請求處理過程結束。
DispatcherServlet詳解
DispatcherServlet 是 Spring MVC 的核心組件之一,它負責處理所有進入的 HTTP 請求,并將它們分發到合適的處理器(控制器)。由于 DispatcherServlet 是一個復雜的類,這里只是摘要了 DispatcherServlet 核心源碼進行分析。
1. DispatcherServlet 初始化
DispatcherServlet 在初始化過程中會加載 Spring 應用上下文,并初始化一些關鍵組件,如 HandlerMapping、HandlerAdapter、ViewResolver 等。
@Override
protected void onRefresh(ApplicationContext context) {
// 初始化策略模式中的各種組件
initStrategies(context);
}
initStrategies 方法用于初始化請求處理所需的各種策略組件:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // 文件上傳解析器
initLocaleResolver(context); // 本地化解析器
initThemeResolver(context); // 主題解析器
initHandlerMappings(context); // 處理器映射
initHandlerAdapters(context); // 處理器適配器
initHandlerExceptionResolvers(context); // 異常解析器
initRequestToViewNameTranslator(context); // 視圖名稱翻譯器
initViewResolvers(context); // 視圖解析器
initFlashMapManager(context); // Flash映射管理器
}
2. 請求處理流程
DispatcherServlet 的核心請求處理邏輯在 doDispatch 方法中實現。該方法負責將請求分發到合適的處理器,并完成請求的處理。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 檢查請求是否為文件上傳請求
if (isMultipartRequest(request)) {
// 處理文件上傳請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
}
// 確定請求的處理器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 確定處理器適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 處理請求并返回ModelAndView
ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 渲染視圖
processDispatchResult(processedRequest, response, mappedHandler, mv, null);
}
3. 處理器映射和適配
DispatcherServlet 使用 HandlerMapping 來查找合適的處理器,并使用 HandlerAdapter 來調用處理器的方法。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler + "]");
}
4. 視圖解析和渲染
DispatcherServlet 使用 ViewResolver 來解析視圖名稱并渲染視圖。
protected void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
throws Exception {
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 使用ViewResolver解析視圖名稱
view = resolveViewName(viewName, mv.getModelInternal(), request);
} else {
view = mv.getView();
}
if (view != null) {
// 渲染視圖
view.render(mv.getModelInternal(), request, response);
}
}
5. 異常處理
DispatcherServlet 也具有處理異常的能力,它會使用配置的異常解析器來處理請求過程中發生的異常。
protected void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
throws Exception {
if (exception != null) {
if (exceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.exceptionResolvers) {
ModelAndView exMv = resolver.resolveException(request, response, mappedHandler.getHandler(), exception);
if (exMv != null) {
mv = exMv;
break;
}
}
}
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
}
HandlerMapping和Controller
在 Spring MVC 框架中,HandlerMapping 和 Controller 是兩個非常重要的組件,它們負責請求的路由和處理,下面我們詳細分析這兩個組件的作用及其工作機制。
1.HandlerMapping
HandlerMapping 是 Spring MVC 中的一個接口,它的主要作用是根據請求的 URL、HTTP 方法等信息將請求映射到具體的處理器(通常是一個控制器方法)。HandlerMapping 的存在使得應用程序可以靈活地配置請求路徑與控制器之間的映射關系。
(1) 常見的 HandlerMapping 實現
BeanNameUrlHandlerMapping:
- 通過 Bean 的名稱來匹配 URL。
- 適用于簡單的 URL 到處理器的映射。
RequestMappingHandlerMapping:
- 使用 @RequestMapping 注解來定義請求路徑和處理器方法之間的關系。
- 是 Spring MVC 中最常用的映射方式,支持復雜的 URL 模式、HTTP 方法、請求參數等匹配。
SimpleUrlHandlerMapping:
- 通過配置文件定義 URL 到處理器的映射。
- 適用于需要在外部配置文件中定義映射關系的場景。
(2) HandlerMapping 的工作流程
- 請求到達 DispatcherServlet:當一個請求到達 DispatcherServlet 時,DispatcherServlet 會調用 HandlerMapping 來查找處理該請求的處理器。
- 查找處理器:HandlerMapping 接收到請求信息后,根據其實現方式(如注解、配置文件)查找與請求匹配的處理器。
- 返回處理器信息:一旦找到匹配的處理器,HandlerMapping 返回一個 HandlerExecutionChain 對象,該對象包含處理器實例和相關的攔截器。
2.Controller
Controller 是 Spring MVC 中用于處理請求的組件。它負責接收請求參數、調用業務邏輯,并返回視圖名稱或響應數據。Controller 的設計使得業務邏輯與請求處理分離,便于維護和擴展。
(1) Controller 的類型
注解驅動的控制器:
- 使用 @Controller 和 @RequestMapping 注解來定義控制器類和處理方法。
- 是當前 Spring MVC 中最常用的控制器類型。
- 支持多種注解來處理請求參數、路徑變量、請求體等。
傳統的控制器接口:
- 實現 Controller 接口的類。
- 在早期的 Spring MVC 中使用較多,現在多被注解驅動的控制器所取代。
(2) Controller 的工作流程
- 接收請求:控制器方法通過 @RequestMapping 注解指定的路徑接收特定的請求。
- 處理請求參數:使用注解如 @RequestParam、@PathVariable、@RequestBody 等來處理請求參數和請求體。
- 調用業務邏輯:控制器通常會調用服務層或業務邏輯層的方法來處理請求數據。
- 返回結果:控制器方法可以返回一個 ModelAndView 對象、視圖名稱字符串,或者直接返回數據(如 JSON、XML)。
- 異常處理:控制器可以通過 @ExceptionHandler 注解來處理方法中拋出的異常。
視圖的渲染
在視圖渲染階段,視圖對象根據Model中的數據進行渲染,生成最終的輸出結果。視圖的類型可以多種多樣,如HTML、JSON、XML等。常見的視圖技術包括:
- JSP:傳統的Java Server Pages,用于生成動態HTML。
- Thymeleaf:現代的模板引擎,支持更強的HTML5功能。
- FreeMarker:另一種流行的模板引擎,支持復雜的模板語法。
總結
本文,我們分析了 Spring MVC的原理,因為其涉及的內容比較多,所以在面試過程中,我們要抓大放小,先掌握其High Level的設計思想,Spring MVC的核心思想是通過控制器來處理請求,將請求數據與業務邏輯分離,并將最終的響應結果交給視圖層進行展示。
在掌握了High Level的設計思想之后,我們再去分析它的幾個核心組件,如 DispatcherServlet、HandlerMapping、Controller、ViewResolver等。
另外,在日常工作中,除了應付面試,我們應該更多地去了解 SpringMVC的底層原理,可以毫不夸張地說,只要是和業務的 CRUD打交道,幾乎離不開 Spring MVC,所以多了解其原理,可以幫助我們更深入地掌握它,在日常使用中才能更加的游刃有余!