拼多多一面:聊聊 Spring MVC 的工作原理!
都說拼多多是程序員的終點站,這篇文章,我們來分析一道它的 1面題目:聊聊Spring MVC的工作原理。
Spring MVC(Model-View-Controller)是 Spring框架中用于構(gòu)建 Web應(yīng)用程序的模塊之一,它遵循經(jīng)典的MVC設(shè)計模式,將應(yīng)用程序的不同方面分離,以實現(xiàn)更好的組織和可維護(hù)性。下面,我們將從架構(gòu),工作流程,核心組件分析等角度來詳細(xì)分析 Spring MVC的工作原理。
Spring MVC的架構(gòu)
Spring MVC的架構(gòu)主要由以下幾個核心組件構(gòu)成:
- DispatcherServlet:這是Spring MVC的前端控制器,負(fù)責(zé)接收HTTP請求并將其分發(fā)給合適的處理器進(jìn)行處理。它是整個Spring MVC的中央調(diào)度器。
- HandlerMapping:用于將請求映射到具體的處理器(Controller)上。它根據(jù)請求的URL、HTTP方法等信息來確定哪個控制器應(yīng)該處理該請求。
- Controller:處理具體業(yè)務(wù)邏輯的組件,它接收來自DispatcherServlet的請求,調(diào)用業(yè)務(wù)服務(wù)處理后,返回一個ModelAndView對象。
- ModelAndView:它是Spring MVC中用于返回模型數(shù)據(jù)和視圖名稱的對象,控制器通過它將處理結(jié)果傳遞給視圖層。
- ViewResolver:視圖解析器,用于將邏輯視圖名稱解析成具體的視圖對象(如JSP、Thymeleaf等)。
- View:視圖用于渲染最終的結(jié)果給用戶,它可以是多種形式的,比如HTML、JSON、XML等。
整體結(jié)構(gòu)如下圖:
Spring MVC的工作流程
Spring MVC的請求處理流程可以分為以下幾個步驟:
- 請求接收:用戶發(fā)送一個HTTP請求到服務(wù)器,DispatcherServlet作為前端控制器接收到該請求。
- 請求映射:DispatcherServlet調(diào)用HandlerMapping來查找匹配的處理器(Controller)。HandlerMapping根據(jù)請求的URL、請求參數(shù)等進(jìn)行匹配。
- 調(diào)用處理器:找到處理器后,DispatcherServlet將請求轉(zhuǎn)發(fā)給具體的Controller進(jìn)行處理。
- 業(yè)務(wù)處理:Controller執(zhí)行具體的業(yè)務(wù)邏輯操作,通常會調(diào)用服務(wù)層或DAO層的方法處理數(shù)據(jù)。
- 返回ModelAndView:業(yè)務(wù)處理完畢后,Controller返回一個ModelAndView對象,其中包含視圖名和模型數(shù)據(jù)。
- 視圖解析:DispatcherServlet接收ModelAndView后,調(diào)用ViewResolver來解析視圖名,得到具體的視圖對象。
- 視圖渲染:視圖對象根據(jù)模型數(shù)據(jù)進(jìn)行渲染,生成最終的輸出結(jié)果。
- 響應(yīng)返回:渲染完畢后,將結(jié)果返回給用戶,整個請求處理過程結(jié)束。
DispatcherServlet詳解
DispatcherServlet 是 Spring MVC 的核心組件之一,它負(fù)責(zé)處理所有進(jìn)入的 HTTP 請求,并將它們分發(fā)到合適的處理器(控制器)。由于 DispatcherServlet 是一個復(fù)雜的類,這里只是摘要了 DispatcherServlet 核心源碼進(jìn)行分析。
1. DispatcherServlet 初始化
DispatcherServlet 在初始化過程中會加載 Spring 應(yīng)用上下文,并初始化一些關(guān)鍵組件,如 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 方法中實現(xiàn)。該方法負(fù)責(zé)將請求分發(fā)到合適的處理器,并完成請求的處理。
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 來調(diào)用處理器的方法。
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 也具有處理異常的能力,它會使用配置的異常解析器來處理請求過程中發(fā)生的異常。
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 是兩個非常重要的組件,它們負(fù)責(zé)請求的路由和處理,下面我們詳細(xì)分析這兩個組件的作用及其工作機(jī)制。
1.HandlerMapping
HandlerMapping 是 Spring MVC 中的一個接口,它的主要作用是根據(jù)請求的 URL、HTTP 方法等信息將請求映射到具體的處理器(通常是一個控制器方法)。HandlerMapping 的存在使得應(yīng)用程序可以靈活地配置請求路徑與控制器之間的映射關(guān)系。
(1) 常見的 HandlerMapping 實現(xiàn)
BeanNameUrlHandlerMapping:
- 通過 Bean 的名稱來匹配 URL。
- 適用于簡單的 URL 到處理器的映射。
RequestMappingHandlerMapping:
- 使用 @RequestMapping 注解來定義請求路徑和處理器方法之間的關(guān)系。
- 是 Spring MVC 中最常用的映射方式,支持復(fù)雜的 URL 模式、HTTP 方法、請求參數(shù)等匹配。
SimpleUrlHandlerMapping:
- 通過配置文件定義 URL 到處理器的映射。
- 適用于需要在外部配置文件中定義映射關(guān)系的場景。
(2) HandlerMapping 的工作流程
- 請求到達(dá) DispatcherServlet:當(dāng)一個請求到達(dá) DispatcherServlet 時,DispatcherServlet 會調(diào)用 HandlerMapping 來查找處理該請求的處理器。
- 查找處理器:HandlerMapping 接收到請求信息后,根據(jù)其實現(xiàn)方式(如注解、配置文件)查找與請求匹配的處理器。
- 返回處理器信息:一旦找到匹配的處理器,HandlerMapping 返回一個 HandlerExecutionChain 對象,該對象包含處理器實例和相關(guān)的攔截器。
2.Controller
Controller 是 Spring MVC 中用于處理請求的組件。它負(fù)責(zé)接收請求參數(shù)、調(diào)用業(yè)務(wù)邏輯,并返回視圖名稱或響應(yīng)數(shù)據(jù)。Controller 的設(shè)計使得業(yè)務(wù)邏輯與請求處理分離,便于維護(hù)和擴(kuò)展。
(1) Controller 的類型
注解驅(qū)動的控制器:
- 使用 @Controller 和 @RequestMapping 注解來定義控制器類和處理方法。
- 是當(dāng)前 Spring MVC 中最常用的控制器類型。
- 支持多種注解來處理請求參數(shù)、路徑變量、請求體等。
傳統(tǒng)的控制器接口:
- 實現(xiàn) Controller 接口的類。
- 在早期的 Spring MVC 中使用較多,現(xiàn)在多被注解驅(qū)動的控制器所取代。
(2) Controller 的工作流程
- 接收請求:控制器方法通過 @RequestMapping 注解指定的路徑接收特定的請求。
- 處理請求參數(shù):使用注解如 @RequestParam、@PathVariable、@RequestBody 等來處理請求參數(shù)和請求體。
- 調(diào)用業(yè)務(wù)邏輯:控制器通常會調(diào)用服務(wù)層或業(yè)務(wù)邏輯層的方法來處理請求數(shù)據(jù)。
- 返回結(jié)果:控制器方法可以返回一個 ModelAndView 對象、視圖名稱字符串,或者直接返回數(shù)據(jù)(如 JSON、XML)。
- 異常處理:控制器可以通過 @ExceptionHandler 注解來處理方法中拋出的異常。
視圖的渲染
在視圖渲染階段,視圖對象根據(jù)Model中的數(shù)據(jù)進(jìn)行渲染,生成最終的輸出結(jié)果。視圖的類型可以多種多樣,如HTML、JSON、XML等。常見的視圖技術(shù)包括:
- JSP:傳統(tǒng)的Java Server Pages,用于生成動態(tài)HTML。
- Thymeleaf:現(xiàn)代的模板引擎,支持更強(qiáng)的HTML5功能。
- FreeMarker:另一種流行的模板引擎,支持復(fù)雜的模板語法。
總結(jié)
本文,我們分析了 Spring MVC的原理,因為其涉及的內(nèi)容比較多,所以在面試過程中,我們要抓大放小,先掌握其High Level的設(shè)計思想,Spring MVC的核心思想是通過控制器來處理請求,將請求數(shù)據(jù)與業(yè)務(wù)邏輯分離,并將最終的響應(yīng)結(jié)果交給視圖層進(jìn)行展示。
在掌握了High Level的設(shè)計思想之后,我們再去分析它的幾個核心組件,如 DispatcherServlet、HandlerMapping、Controller、ViewResolver等。
另外,在日常工作中,除了應(yīng)付面試,我們應(yīng)該更多地去了解 SpringMVC的底層原理,可以毫不夸張地說,只要是和業(yè)務(wù)的 CRUD打交道,幾乎離不開 Spring MVC,所以多了解其原理,可以幫助我們更深入地掌握它,在日常使用中才能更加的游刃有余!