1. DispatcherServlet概述
1.1 什么是DispatcherServlet?
DispatcherServlet是Spring MVC框架的核心組件,它本質上是一個Java Servlet,作為前端控制器(Front Controller)負責接收所有HTTP請求,并根據特定規則將請求分發到相應的處理器(Handler)進行處理 。在Spring Web應用中,當用戶發送一個HTTP請求時,首先由Servlet容器(如Tomcat)接收并路由到DispatcherServlet,然后由DispatcherServlet協調其他組件完成請求處理。
1.2 核心作用與職責
DispatcherServlet在Spring MVC中扮演著"指揮官"的角色,其核心職責包括:
- 請求分發:根據請求的URL、HTTP方法等信息,通過策略接口找到對應的處理器(Handler)
- 組件協調:協調Spring MVC框架中的各類組件,如HandlerMapping、HandlerAdapter、ViewResolver等?
- 異常處理:捕獲處理器執行過程中的異常,并通過HandlerExceptionResolver進行處理?
- 攔截器管理:執行HandlerInterceptor攔截器鏈,進行預處理和后處理?
- 上下文管理:創建并維護WebApplicationContext,這是Spring MVC的IoC容器?
前端控制器設計模式是DispatcherServlet實現的核心思想,它將請求處理的流程統一管理,使得具體的業務邏輯可以獨立于請求處理流程之外 。這種設計模式使得Spring MVC具有高度的靈活性和可擴展性。
2. 初始化流程解析
2.1 init方法的執行過程
DispatcherServlet的初始化過程是從Servlet容器(Tomcat等)調用其init()
方法開始的 。這個方法定義在HttpServletBean父類中,主要完成以下工作:
//摘自HttpServletBean類
public final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing.servlet '" + getServletName() + "'"); }// 1. 將ServletConfig參數轉換為Bean屬性try {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this(requiredProperties));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) {logger.error("Failed to.set bean properties.on Servlet '" +getServletName() + "'", ex);throw ex;}// 2. 執行子類的特定初始化initServletBean(); if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}
}
在這個過程中,關鍵步驟是initServletBean()方法,它由FrameworkServlet實現,負責創建WebApplicationContext并初始化Spring MVC組件 。
2.2 WebApplicationContext的創建與加載
WebApplicationContext是Spring MVC的IoC容器,它繼承自ApplicationContext,增加了Web環境相關的功能。創建過程發生在initServletBean()
方法中:
//摘自FrameworkServlet類
protected final void initServletBean() throws ServletException {// ... 省略其他代碼 ...// 1. 創建WebApplicationContextif (this.webApplicationContext == null) {WebApplicationContext wac = createWebApplicationContext(servletConfig);if (!this煥然一新) {this.webApplicationContext = wac;}// ... 省略其他代碼 ...}// 2. 設置上下文屬性if (this.webApplicationContext != null) {this.webApplicationContext.set ServletContext(getServletContext());this.webApplicationContext.set ServletConfig(getServletConfig());this.webApplicationContextRefresh();}// ... 省略其他代碼 ...
}
創建WebApplicationContext時,會根據contextClass
初始化參數決定使用哪個上下文實現類(默認是XmlWebApplicationContext
),并根據contextConfigLocation
參數加載配置 。
WebApplicationContext與根上下文的關系:如果配置了ContextLoaderListener
,則根上下文由它負責創建,而DispatcherServlet的WebApplicationContext會成為根上下文的子上下文,可以訪問根上下文中的Bean,但根上下文不能訪問Servlet上下文中的Bean。
2.3 策略接口的初始化(HandlerMapping、ViewResolver等)
在完成WebApplicationContext的創建后,DispatcherServlet會調用onRefresh()
方法,并在其中執行initStrategies()
方法來初始化各種策略接口
//摘自FrameworkServlet類
protected void onRefresh(ApplicationContext context) {// ... 省略其他代碼 ...// 1. 初始化各種策略initStrategies(context); [ty-reference](16) // ... 省略其他代碼 ...
}//摘自FrameworkServlet類
protected void initStrategies(ApplicationContext context) {// 1. 初始化Multipart解析器 initMultipartResolver(context);// 2. 初始化locale解析器 initLocaleResolver(context);// 3. 初始化theme解析器 initThemeResolver(context);// 4. 初始化HandlerMapping initHandlerMappings(context);// 5. 初始化HandlerAdapter initHandlerAdapters(context);// 6. 初始化HandlerExceptionResolver initHandlerExceptionResolvers(context);// 7. 初始化RequestToViewNameTranslator initRequestToViewNameTranslator(context);// 8. 初始化ViewResolvers initViewResolvers(context);// 9. 初始化FlashMapManager initFlashMapManager(context);
}
策略接口初始化的順序非常重要,它決定了組件的優先級和協作方式。例如,多個HandlerMapping會被按順序查詢,直到找到匹配的處理器為止 。
2.4 初始化參數詳解
在web.xml中配置DispatcherServlet時,可以設置以下關鍵初始化參數:
參數名 | 描述 | 默認值 |
---|---|---|
contextConfigLocation | 指定Spring MVC配置文件的位置 | /WEB-INF/[servlet-name]-servlet.xml |
contextClass | 指定WebApplicationContext的類 | XmlWebApplicationContext |
namespace | 指定WebApplicationContext的命名空間 | [servlet-name]-servlet |
detectAllHandlerMappings | 是否檢測所有HandlerMapping | false |
detectAllHandlerAdapters | 是否檢測所有HandlerAdapter | false |
示例配置:
<!-- web.xml -->
<servlet><servlet-name>mvcdemo</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:mvcdemo-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet><servlet-mapping><servlet-name>mvcdemo</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
3. 請求處理流程詳解
3.1 doService方法的職責
doService()是DispatcherServlet處理請求的核心方法,它定義在FrameworkServlet中,根據HTTP方法調用相應的處理方法(如doGet、doPost等),并最終調用doDispatch()**方法進行請求分發。
//摘自FrameworkServlet類
protected void doService(HttpServletRequest request, HttpServletResponse response)throws Exception {// ... 省略其他代碼 ...// 1. 設置框架對象到請求中request.setAttribute(WEB_APPLICATION_CONTEXT attribute, getWebApplicationContext());request.setAttribute(LOCALE RESOLVER attribute, this.localeResolver);request.setAttribute(THEME RESOLVER attribute, this.themeResolver);request.setAttribute(THEME Source attribute, getThemeSource());// ... 省略其他代碼 ...// 2. 調用doDispatch處理請求doDispatch(request, response); [ty-reference](7) // ... 省略其他代碼 ...
}
3.2 doDispatch方法的執行步驟
**doDispatch()**是Spring MVC請求處理的核心方法,它定義在DispatcherServlet中,負責整個請求處理流程 :
//摘自DispatcherServlet類
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {// 1. 檢查并處理Multipart請求(如文件上傳) processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 2. 獲取HandlerExecutionChain(處理器和攔截器鏈) mappedHandler = getHandler(processedRequest); if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response); return;}// 3. 獲取HandlerAdapter(處理器適配器) HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 4. 執行攔截器的preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return;}// 5. 調用處理器處理請求,獲取ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 6. 處理異步請求 if (asyncManager.isConcurrentHandlingStarted()) {return;}// 7. 應用默認視圖名稱 applyDefaultViewName(processedRequest, mv);// 8. 執行攔截器的postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv);// 9. 渲染視圖processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {// ... 省略其他代碼 ...}catch (Throwable err) {// ... 省略其他代碼 ...}finally {// ... 省略其他代碼 ...}
}
關鍵執行步驟:
- Multipart請求處理:檢查請求是否包含文件上傳,如果是則使用MultipartResolver進行解析
- 處理器查找:通過HandlerMapping查找與請求匹配的處理器,返回包含處理器和攔截器鏈的HandlerExecutionChain
- 處理器適配:通過HandlerAdapter獲取處理器的執行方式
- 攔截器預處理:按順序執行攔截器鏈的preHandle方法
- 處理器執行:調用處理器處理請求,返回ModelAndView
- 攔截器后處理:逆序執行攔截器鏈的postHandle方法
- 視圖解析與渲染:通過ViewResolver解析視圖名稱,渲染模型數據到響應
3.3 攔截器(HandlerInterceptor)的作用與實現
HandlerInterceptor是Spring MVC中的攔截器接口,它定義了三個方法:
public interface HandlerInterceptor {// 1. 預處理(在處理器執行前)boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;// 2. 后處理(在處理器執行后)void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv) throws Exception;// 3. 完成處理(在視圖渲染后)void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
攔截器執行順序:
當有多個攔截器時,preHandle方法按攔截器注冊順序執行,而postHandle和afterCompletion方法則按逆序執行 。
攔截器應用場景:
- 權限驗證:檢查用戶是否有權訪問當前請求
- 日志記錄:記錄請求處理的開始和結束時間
- 性能監控:統計處理器執行時間
- 異常處理:統一處理處理器執行中的異常
示例攔截器:
public class SecurityInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 1. 獲取用戶信息Object user = request.getSession()..getAttribute("user");// 2. 檢查用戶是否登錄if (user == null) {// 3. 未登錄則重定向到登錄頁面response.sendRedirect("/login");return false;}// 4. 檢查權限String url = request.getRequestURI();if (!userHasPermission(user, url)) {// 5. 無權限則返回錯誤信息response.setStatus(HttpServletResponse status SC對于BAD對于REQUEST);return false;}// 6. 放行return true;}// 其他方法實現@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv) {// 處理ModelAndView}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 處理請求完成}private boolean userHasPermission(Object user, String url) {// 權限驗證邏輯return true;}
}
4. 源碼級解析
4.1 DispatcherServlet類的繼承關系
DispatcherServlet的繼承關系體現了Spring框架的設計思想和分層架構:
HttpServlet → HttpServletBean → FrameworkServlet → DispatcherServlet
各層職責:
- HttpServlet:Servlet規范的核心接口,定義了Servlet的基本生命周期方法
- HttpServletBean:將Servlet配置參數轉換為Bean屬性的基類
- FrameworkServlet:提供Spring Web應用的通用功能,如WebApplicationContext的創建和初始化
- DispatcherServlet:實現請求分發的具體邏輯,是Spring MVC的前端控制器
這種繼承結構使得Spring MVC能夠無縫集成到Servlet容器中,同時保持高度的可擴展性和靈活性。
4.2 關鍵方法源碼分析(doDispatch、processDispatchResult)
4.2.1 doDispatch方法源碼分析
doDispatch()是Spring MVC請求處理的核心方法,其源碼(Spring 6.0版本)位于org.springframework.web.servlet.DispatcherServlet
類中,大約從第1200行開始:
//摘自DispatcherServlet.java(Spring 6.0)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {// 1. 處理Multipart請求HttpServletRequest processedRequest = checkMultipart(request);boolean multipartRequestParsed = (processedRequest != request);// 2. 查找處理器HandlerExecutionChain mappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 3. 獲取處理器適配器HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 4. 執行攔截器鏈的preHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 5. 調用處理器處理請求MV mv = null;Exception dispatchException = null;try {mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}catch (Exception ex) {dispatchException = ex;if (ex instanceofognl Exception) {// 保存ognl異常信息processedRequest.setAttribute(ognl Exception attribute, ex);}}catch (Throwable err) {// 處理Error異常dispatchException = newServletException("Handler dispatch failed", err);}// 6. 處理異步請求if (asyncManager.isConcurrentHandlingStarted()) {return;}// 7. 應用默認視圖名稱applyDefaultViewName(processedRequest, mv);// 8. 執行攔截器鏈的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);// 9. 處理分發結果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
關鍵邏輯點:
- Multipart處理:通過
checkMultipart()
方法檢查并處理文件上傳請求 - 處理器查找:通過
getHandler()
方法查找與請求匹配的處理器 - 攔截器鏈執行:通過
applyPreHandle()
和applyPostHandle()
方法執行攔截器鏈 - 異常處理:捕獲異常并傳遞給
processDispatchResult()
方法處理
4.2.2 processDispatchResult方法源碼分析
processDispatchResult()方法負責處理處理器返回的結果,包括視圖解析和渲染:
//摘自DispatcherServlet.java(Spring 6.0)
protected void processDispatchResult(HttpServletRequest request, HttpServletResponse response,HandlerExecutionChain mappedHandler,ModelAndView mv, Exception exception) throws Exception {// 1. 處理異常if (exception != null) {mv = resolveException(request, response, mappedHandler.getHandler(), exception);}// 2. 渲染視圖if (mv != null) {mv = applyViewName Trans later(request, mv, mappedHandler);mv = mappedHandler.applyViewName Trans later(request, mv);mv =暴露Model到請求中(request, mv, mappedHandler);// 3. 解析視圖View view = resolveViewName(mv至少視圖名, mv至少模型, request);if (view != null) {// 4. 渲染視圖view.render(mv至少模型, request, response);}// 5. 執行攔截器鏈的afterCompletion方法mappedHandler.applyAfterCompletion(request, response, mv至少模型);}
}
關鍵邏輯點:
- 異常處理:通過
resolveException()
方法處理處理器執行過程中的異常 - 視圖解析:通過
resolveViewName()
方法將邏輯視圖名解析為具體視圖 - 視圖渲染:調用視圖的
render()
方法將模型數據渲染到響應 - 攔截器完成處理:執行攔截器鏈的afterCompletion方法
4.2.3 攔截器鏈執行源碼分析
攔截器鏈的執行邏輯在HandlerExecutionChain
類中實現:
//摘自HandlerExecutionChain.java
public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {// 1. 按注冊順序執行preHandle方法for (HandlerInterceptor interceptor : this.interceptors) {if (!interceptor.preHandle(request, response, this handler)) {triggerAfterCompletion(request, response, this handler, null);return false;}}return true;
}public void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {// 2. 按逆序執行postHandle方法for (int i = this.interceptors.size() - 1; i >= 0; i--) {HandlerInterceptorInterceptor = this.interceptors.get(i);攔截器.postHandle(request, response, this handler, mv);}
}public void applyAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception {// 3. 按逆序執行afterCompletion方法for (int i = this.interceptors.size() - 1; i >= 0; i--) {HandlerInterceptor攔截器 = this.interceptors.get(i);try {攔截器.afterCompletion(request, response, this handler, ex);}catch (Exception err) {// 記錄錯誤但不拋出handleAfterCompletionError(攔截器, err);}}
}
攔截器鏈執行順序:
- preHandle:按攔截器注冊順序執行
- postHandle:按攔截器注冊逆序執行
- afterCompletion:按攔截器注冊逆序執行
這種順序設計確保了攔截器的執行邏輯符合"進入時按順序,退出時按逆序"的編程模式。
4.3 策略接口設計(HandlerMapping、HandlerAdapter)
4.3.1 HandlerMapping接口設計
HandlerMapping接口定義了如何根據請求找到對應的處理器:
public interface HandlerMapping {// 根據請求查找處理器HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;// 根據處理器查找請求路徑String getHandlerPath(Object handler);// 獲取支持的請求方法Set<HttpMethod> getHandlerMethods(Object handler);
}
Spring提供了多種HandlerMapping實現,包括:
- BeanNameUrlHandlerMapping:根據Bean名稱與URL路徑匹配
- RequestMappingHandlerMapping:根據@RequestMapping注解匹配(默認實現)
- SimpleUrlHandlerMapping:根據簡單URL模式匹配
策略模式應用:通過HandlerMapping接口,Spring MVC可以輕松支持多種請求到處理器的映射策略,開發者也可以實現自定義的HandlerMapping。
4.3.2 HandlerAdapter接口設計
HandlerAdapter接口定義了如何調用處理器處理請求:
public interface HandlerAdapter {// 判斷是否支持某個處理器boolean supports(Object handler);// 調用處理器處理請求ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;// 獲取處理器的最后修改時間long最后一次修改時間(HttpServletRequest request, Object handler);
}
Spring提供了多種HandlerAdapter實現,包括:
- ControllerHandlerAdapter:處理實現Controller接口的處理器
- HttpRequestHandlerAdapter:處理實現HttpRequestHandler接口的處理器
- HandlerMethodAdapter:處理基于方法的處理器(如使用@RequestMapping注解的方法)
適配器模式應用:通過HandlerAdapter接口,Spring MVC可以統一處理不同類型的處理器,使得處理器的實現更加靈活。
4.3.3 ViewResolver接口設計
ViewResolver接口定義了如何將邏輯視圖名解析為具體視圖:
public interface ViewResolver {// 將邏輯視圖名解析為具體視圖View resolveViewName(String viewName, Locale locale,(HttpServletRequest request, HttpServletResponse response)) throws Exception;// 獲取支持的視圖名String[]得到視圖名();
}
Spring提供了多種ViewResolver實現,包括:
- InternalResourceViewResolver:解析為JSP視圖(默認實現)
- UrlBasedViewResolver:根據URL解析視圖
- FreeMarkerViewResolver:解析為FreeMarker視圖
策略模式應用:通過ViewResolver接口,Spring MVC可以支持多種視圖技術,開發者也可以實現自定義的ViewResolver。
4.4 擴展點分析
Spring MVC提供了多個擴展點,允許開發者自定義和增強框架功能:
- 自定義HandlerMapping:實現HandlerMapping接口,重寫getHandler()方法
- 自定義HandlerAdapter:實現HandlerAdapter接口,重寫handle()方法
- 自定義ViewResolver:實現ViewResolver接口,重寫resolveViewName()方法
- 自定義攔截器:實現HandlerInterceptor接口,重寫preHandle()等方法
- 自定義異常處理器:實現HandlerExceptionResolver接口,重寫resolveException()方法
示例:自定義HandlerMapping
public class CustomHandlerMapping implements HandlerMapping {@Overridepublic HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 自定義處理器查找邏輯String uri = request.getRequestURI();// 1. 查找處理器Object handler = findHandler(uri);// 2. 查找攔截器List<HandlerInterceptor> interceptors = findInterceptors(uri);return new HandlerExecutionChain(handler, interceptors);}@Overridepublic String getHandlerPath(Object handler) {// 自定義處理器路徑獲取邏輯return null;}@Overridepublic Set<HttpMethod> getHandlerMethods(Object handler) {// 自定義處理器方法獲取邏輯return null;}private Object findHandler(String uri) {// 查找處理器邏輯return null;}private List<HandlerInterceptor> findInterceptors(String uri) {// 查找攔截器邏輯return null;}
}
5. 配置方式與示例
5.1 傳統web.xml配置
web.xml配置示例:
<!-- web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http:// xmlns . jcp . org / xml / ns / javaee http:// xmlns . jcp . org / xml / ns / javaee / web - app _ 4 _ 0 . xsd"version="4.0"><!-- 1. 配置DispatcherServlet --><servlet><servlet-name>mvcdemo</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:mvcdemo-servlet.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><!-- 2. 配置DispatcherServlet的映射路徑 --><servlet-mapping><servlet-name>mvcdemo</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!-- 3. 配置根上下文(可選) --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param>< listener >< listener-class > org.springframework.web.context.ContextLoaderListener </ listener-class ></ listener >
</web-app>
關鍵配置參數:
- contextConfigLocation:指定Spring MVC配置文件的位置
- load-on-startup:指定Servlet的初始化順序,值大于0表示容器啟動時初始化
- namespace:指定WebApplicationContext的命名空間(默認是servlet-name-servlet)
5.2 Java配置類方式
Java配置類示例:
// 1. 創建Web應用上下文
public class WebAppContext implements WebApplicationInitializer {@Overridepublic void onStartup(ServletContext container) {// 1. 創建根上下文ContextLoaderListener listener = new ContextLoaderListener(newAnnotationConfigWebApplicationContext());listener.getApplicationContext().setConfigLocation("com.example.config rootConfig");container.addListener(listener);// 2. 創建DispatcherServlet上下文DispatcherServlet分散器 = new分散器();分散器.setContextClass(AnnotationConfigWebApplicationContext.class);分散器.setContextConfigLocation("com.example.config mvcConfig");// 3. 注冊DispatcherServletServletRegistration動態注冊 = container.addServlet("mvcdemo",分散器);dynamicRegistration.setLoadOnStartup(1);dynamicRegistration.addMapping("/");// 4. 配置其他Servlet參數分散器.getServletConfig().getInitParameter("namespace", "mvcdemo");}
}
Web MVC配置類:
// 1. 配置Spring MVC組件
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class桂cConfig implements WebMvcConfigurer {// 2. 配置視圖解析器@Overridepublic void configureViewResolvers(List<ViewResolver> viewResolvers) {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); viewResolvers.add(viewResolver);}// 3. 配置靜態資源處理器@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); }// 4. 配置攔截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new SecurityInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/login", "/css/**", "/js/**"); }// 5. 配置異常處理器@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {resolvers.add(new DefaultHandlerExceptionResolver());}// 6. 配置消息轉換器@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(new MappingJackson2HttpMessageConverter());}
}
Java配置方式的優勢:
- 更加靈活,可以通過代碼動態配置
- 更加類型安全,避免XML配置中的字符串錯誤
- 更容易進行單元測試
- 更符合現代Java開發習慣
6. 常見問題與解決方案
6.1 請求未匹配到處理器的調試方法
問題表現:當請求路徑沒有對應的處理器時,Spring MVC會拋出`No mapping found for HTTP request`異常,導致404錯誤 [ty-reference](17) 。排查步驟: 檢查日志級別:將Spring日志級別設置為DEBUG,查看`HandlerExecutionChain`是否找到處理器
?# application.propertieslogging.level.org.springframework.web=DEBUGlogging.level.org.springframework.web.servlet=DEBUG
檢查處理器映射:
- 確認是否使用了正確的HandlerMapping(如RequestMappingHandlerMapping)
- 確認是否掃描了包含處理器(Controller)的包
檢查處理器注解:
- 確認Controller類是否使用了@Controller注解
- 確認處理方法是否使用了@RequestMapping等注解
- 確認注解路徑是否與請求路徑匹配
檢查攔截器配置:確認攔截器是否阻止了請求的處理
6.2 視圖解析失敗的排查思路
問題表現:處理器返回了ModelAndView,但Spring MVC無法解析視圖,導致500錯誤。
排查步驟:
檢查視圖解析器配置:確認是否配置了視圖解析器(如InternalResourceViewResolver)
檢查視圖名稱格式:確認處理器返回的視圖名稱是否符合視圖解析器的解析規則
檢查視圖文件位置:確認視圖文件是否存在于指定位置(如/WEB-INF/jsp/)
檢查視圖文件權限:確認視圖文件是否有讀取權限
檢查視圖渲染異常:查看日志中是否有視圖渲染相關的異常信息
解決方案示例:
// 配置InternalResourceViewResolver
@Bean
public ViewResolver internalResourceViewResolver() {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver;
}
6.3 多個DispatcherServlet的配置沖突
問題表現:當配置多個DispatcherServlet時,可能會出現URL映射沖突、上下文覆蓋等問題。
配置沖突原因:
- URL映射重疊:多個DispatcherServlet的url-pattern重疊,導致請求被多個Servlet處理?
- 命名空間沖突:多個DispatcherServlet的namespace參數相同,導致上下文覆蓋?
- Bean名稱沖突:多個DispatcherServlet的上下文中存在同名Bean,導致Bean覆蓋?
解決方案:
區分URL映射:為每個DispatcherServlet設置不同的url-pattern,避免重疊
<!-- 第一個DispatcherServlet --> <servlet-mapping><servlet-name>apiServlet</servlet-name><url-pattern>/api/*</url-pattern> </servlet-mapping><!-- 第二個DispatcherServlet --> <servlet-mapping><servlet-name>webServlet</servlet-name><url-pattern>/web/*</url-pattern> </servlet-mapping>
設置不同命名空間:為每個DispatcherServlet設置不同的namespace參數,避免上下文沖突
<!-- 第一個DispatcherServlet --> <init-param><param-name>namespace</param-name><param-value>apiContext</param-value> </init-param><!-- 第二個DispatcherServlet --> <init-param><param-name>namespace</param-name><param-value>webContext</param-value> </init-param>
設置不同初始化順序:通過load-on-startup參數設置不同的初始化順序,避免上下文加載沖
<!-- 第一個DispatcherServlet --> <load-on-startup>1</load-on-startup><!-- 第二個DispatcherServlet --> <load-on-startup>2</load-on-startup>
分離配置文件:為每個DispatcherServlet使用不同的配置文件,避免配置沖突
<!-- 第一個DispatcherServlet --> <init-param><param-name>contextConfigLocation</param-name><param-value>classpath:api-servlet.xml</param-value> </init-param><!-- 第二個DispatcherServlet --> <init-param><param-name>contextConfigLocation</param-name><param-value>classpath:web-servlet.xml</param-value> </init-param>
多DispatcherServlet應用場景:
- 前后端分離:一個處理API請求,一個處理Web頁面
- 模塊化應用:不同模塊使用不同的DispatcherServlet
- 多視圖技術:不同視圖技術使用不同的DispatcherServlet
6.4 其他常見問題
6.4.1 404錯誤排查
問題表現:請求返回404錯誤,表示請求路徑沒有對應的處理器。
排查思路:
- 檢查請求路徑:確認請求路徑是否與處理器映射路徑匹配?
- 檢查HTTP方法:確認處理器是否支持當前HTTP方法?
- 檢查參數匹配:確認請求參數是否符合處理器參數要求?
- 檢查靜態資源:確認靜態資源是否被正確配置
- 檢查攔截器:確認攔截器是否阻止了請求的處理
解決方案示例:
// 確保處理器路徑正確
@RestController
@RequestMapping("/api") [ty-reference](12)
public class示范Controller {// 處理GET請求@GetMapping("/hello") [ty-reference](12) public String hello() {return "Hello World";}// 處理POST請求@PostMapping("/greet") [ty-reference](12) public String greet(@RequestParam String name) { [ty-reference](12) return "Hello " + name;}
}
6.4.2 500錯誤排查
問題表現:請求返回500錯誤,表示處理器執行過程中發生了異常。
排查思路:
- 檢查日志:查看Spring日志中是否有異常信息?
- 檢查處理器方法:確認處理器方法是否有語法錯誤或邏輯錯誤?
- 檢查依賴注入:確認處理器依賴的Bean是否正確注入?
- 檢查異常處理:確認是否配置了異常處理器(如HandlerExceptionResolver)
- 檢查視圖解析:確認視圖解析器是否正確配置
解決方案示例:
// 配置全局異常處理器
@ControllerAdvice
public class GlobalExceptionHandler {// 處理所有異常@ExceptionHandler(Exception.class)public ModelAndView handleException(Exception ex) {ModelAndView mv = new ModelAndView("error");mv.addObject("exception", ex);return mv;}// 處理特定異常@ExceptionHandler(NullPointerException.class)public ResponseEntity<String> handleNullPointerException(NullPointerException ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal server error: " + ex.getMessage());}
}