Spring MVC框架中DispatcherServlet詳解

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中扮演著"指揮官"的角色,其核心職責包括:

  1. 請求分發:根據請求的URL、HTTP方法等信息,通過策略接口找到對應的處理器(Handler)
  2. 組件協調:協調Spring MVC框架中的各類組件,如HandlerMapping、HandlerAdapter、ViewResolver等?
  3. 異常處理:捕獲處理器執行過程中的異常,并通過HandlerExceptionResolver進行處理?
  4. 攔截器管理:執行HandlerInterceptor攔截器鏈,進行預處理和后處理?
  5. 上下文管理:創建并維護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是否檢測所有HandlerMappingfalse
detectAllHandlerAdapters是否檢測所有HandlerAdapterfalse

示例配置

<!-- 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 {// ... 省略其他代碼 ...}
}

關鍵執行步驟

  1. Multipart請求處理:檢查請求是否包含文件上傳,如果是則使用MultipartResolver進行解析
  2. 處理器查找:通過HandlerMapping查找與請求匹配的處理器,返回包含處理器和攔截器鏈的HandlerExecutionChain
  3. 處理器適配:通過HandlerAdapter獲取處理器的執行方式
  4. 攔截器預處理:按順序執行攔截器鏈的preHandle方法
  5. 處理器執行:調用處理器處理請求,返回ModelAndView
  6. 攔截器后處理:逆序執行攔截器鏈的postHandle方法
  7. 視圖解析與渲染:通過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提供了多個擴展點,允許開發者自定義和增強框架功能:

  1. 自定義HandlerMapping:實現HandlerMapping接口,重寫getHandler()方法
  2. 自定義HandlerAdapter:實現HandlerAdapter接口,重寫handle()方法
  3. 自定義ViewResolver:實現ViewResolver接口,重寫resolveViewName()方法
  4. 自定義攔截器:實現HandlerInterceptor接口,重寫preHandle()等方法
  5. 自定義異常處理器:實現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
  1. 檢查處理器映射

    • 確認是否使用了正確的HandlerMapping(如RequestMappingHandlerMapping)
    • 確認是否掃描了包含處理器(Controller)的包
  2. 檢查處理器注解

    • 確認Controller類是否使用了@Controller注解
    • 確認處理方法是否使用了@RequestMapping等注解
    • 確認注解路徑是否與請求路徑匹配
  3. 檢查攔截器配置:確認攔截器是否阻止了請求的處理

6.2 視圖解析失敗的排查思路

問題表現:處理器返回了ModelAndView,但Spring MVC無法解析視圖,導致500錯誤。

排查步驟

  1. 檢查視圖解析器配置:確認是否配置了視圖解析器(如InternalResourceViewResolver)

  2. 檢查視圖名稱格式:確認處理器返回的視圖名稱是否符合視圖解析器的解析規則

  3. 檢查視圖文件位置:確認視圖文件是否存在于指定位置(如/WEB-INF/jsp/)

  4. 檢查視圖文件權限:確認視圖文件是否有讀取權限

  5. 檢查視圖渲染異常:查看日志中是否有視圖渲染相關的異常信息

解決方案示例

// 配置InternalResourceViewResolver
@Bean
public ViewResolver internalResourceViewResolver() {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/jsp/");  viewResolver.setSuffix(".jsp");  return viewResolver;
}

6.3 多個DispatcherServlet的配置沖突

問題表現:當配置多個DispatcherServlet時,可能會出現URL映射沖突、上下文覆蓋等問題。

配置沖突原因

  1. URL映射重疊:多個DispatcherServlet的url-pattern重疊,導致請求被多個Servlet處理?
  2. 命名空間沖突:多個DispatcherServlet的namespace參數相同,導致上下文覆蓋?
  3. Bean名稱沖突:多個DispatcherServlet的上下文中存在同名Bean,導致Bean覆蓋?

解決方案

  1. 區分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>
  2. 設置不同命名空間:為每個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>
  3. 設置不同初始化順序:通過load-on-startup參數設置不同的初始化順序,避免上下文加載沖

    <!-- 第一個DispatcherServlet -->
    <load-on-startup>1</load-on-startup><!-- 第二個DispatcherServlet -->
    <load-on-startup>2</load-on-startup>
  4. 分離配置文件:為每個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錯誤,表示請求路徑沒有對應的處理器。

排查思路

  1. 檢查請求路徑:確認請求路徑是否與處理器映射路徑匹配?
  2. 檢查HTTP方法:確認處理器是否支持當前HTTP方法?
  3. 檢查參數匹配:確認請求參數是否符合處理器參數要求?
  4. 檢查靜態資源:確認靜態資源是否被正確配置
  5. 檢查攔截器:確認攔截器是否阻止了請求的處理

解決方案示例

// 確保處理器路徑正確
@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錯誤,表示處理器執行過程中發生了異常。

排查思路

  1. 檢查日志:查看Spring日志中是否有異常信息?
  2. 檢查處理器方法:確認處理器方法是否有語法錯誤或邏輯錯誤?
  3. 檢查依賴注入:確認處理器依賴的Bean是否正確注入?
  4. 檢查異常處理:確認是否配置了異常處理器(如HandlerExceptionResolver)
  5. 檢查視圖解析:確認視圖解析器是否正確配置

解決方案示例

// 配置全局異常處理器
@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());}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/92335.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/92335.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/92335.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

DBA急救手冊:拆解Oracle死鎖圖,ORA-00060錯誤秒級定位終極指南

關于“死鎖圖”&#xff08;Deadlock Graph&#xff09;的一點淺見 當 Oracle 檢測到死鎖時&#xff0c;檢測到死鎖的會話中的當前 SQL 將被取消&#xff0c;并執行“語句級回滾”&#xff0c;以釋放資源并避免阻塞所有活動。 檢測到死鎖的會話仍然“存活”&#xff0c;并且事務…

C++中的默認函數學習

今天在學習QT別人的項目時看到有個函數在聲明和調用時參數個數不一樣&#xff0c;查了下是c中的一種函數類型&#xff0c;這個類型的函數可以讓代碼更簡潔、靈活。定義&#xff1a;在函數聲明時&#xff0c;給某些參數預先設定一個默認值。調用函數時&#xff0c;如果省略這些參…

HBase分片技術實現

HBase分片技術實現概述HBase是基于Hadoop的分布式、可擴展的NoSQL數據庫&#xff0c;采用列族存儲模型。HBase的分片機制通過Region自動分割和負載均衡實現水平擴展&#xff0c;支持PB級數據存儲和高并發訪問。HBase架構核心組件HMaster: 集群管理節點&#xff0c;負責Region分…

Python爬蟲實戰:研究awesome-python工具,構建技術資源采集系統

1. 引言 1.1 研究背景 Python 憑借語法簡潔、生態豐富等特點,已成為全球最受歡迎的編程語言之一。截至 2024 年,PyPI(Python Package Index)上的第三方庫數量已突破 45 萬個,涵蓋從基礎工具到前沿技術的全領域需求。然而,海量資源也帶來了 "信息過載" 問題 —…

【實時Linux實戰系列】實時視頻監控系統的開發

隨著技術的不斷發展&#xff0c;實時視頻監控系統在安防、交通管理、工業自動化等領域得到了廣泛應用。實時Linux系統因其高效的實時性和穩定性&#xff0c;成為開發高性能視頻監控系統的理想選擇。掌握基于實時Linux的視頻監控系統開發技能&#xff0c;對于開發者來說不僅能夠…

力扣-11.盛最多水的容器

題目鏈接 11.盛最多水的容器 class Solution {public int maxArea(int[] height) {int res 0;for (int i 0, j height.length - 1; i < j; ) {res Math.max(res, Math.min(height[i], height[j]) * (j - i));if (height[i] < height[j]) {i;} else {j--;}}return r…

大型音頻語言模型論文總結

大型音頻語言模型&#xff08;Large Audio Language Model, LALM&#xff09;是一類基于深度學習的智能系統&#xff0c;專門針對音頻信號&#xff08;如語音、音樂、環境聲等&#xff09;進行理解、生成、轉換和推理。它借鑒了大型語言模型&#xff08;LLM&#xff09;的“預訓…

如何解決網頁視頻課程進度條禁止拖動?

function skip() {let video document.getElementsByTagName(video)for (let i0; i<video.length; i) {video[i].currentTime video[i].duration} } setInterval(skip,6666)無法拖動視頻進度。 使用F12啟動調試模式。 function skip() {let video document.getElements…

基于deepSeek的流式數據自動化規則清洗案例【數據治理領域AI帶來的改變】

隨著AI大模型的大量普及&#xff0c;對于傳統代碼模式產生了不小的影響&#xff0c;特別是對于大數據領域&#xff0c;傳統的規則引擎驅動的數據治理已經無法滿足數據增長帶來的治理需求。因此主動型治理手段逐漸成為主流&#xff0c;因此本文介紹一個基于deepSeek的流式數據自…

【論文分析】【Agent】SEW: Self-Evolving Agentic Workflows for Automated Code Generatio

1.論文信息標題&#xff1a;SEW: Self-Evolving Agentic Workflows for Automated Code Generatio&#xff1a;用于自動代碼生成的自我進化的代理工作流程收錄的會議/期刊&#xff1a;作者信息&#xff1a;arxiv&#xff1a;&#x1f517;github網站&#xff1a;&#x1f517;g…

MCP 協議:AI 時代的 “萬能轉接頭”,從 “手動粘貼” 到 “萬能接口”:MCP 協議如何重構 AI 工具調用規則?

注&#xff1a;此文章內容均節選自充電了么創始人&#xff0c;CEO兼CTO陳敬雷老師的新書《GPT多模態大模型與AI Agent智能體》&#xff08;跟我一起學人工智能&#xff09;【陳敬雷編著】【清華大學出版社】 清華《GPT多模態大模型與AI Agent智能體》書籍配套視頻課程【陳敬雷…

VUE本地構建生產環境版本用于局域網訪問

&#x1f680;構建生產環境版本用于局域網訪問&#xff08;適用于 Vue 項目&#xff09; 在開發 Vue 項目的過程中&#xff0c;很多人使用 yarn serve 啟動開發服務器進行調試。但開發模式存在以下問題&#xff1a; 訪問速度慢&#xff0c;特別是局域網訪問&#xff1b;熱更新頻…

【密碼學】5. 公鑰密碼

這里寫自定義目錄標題公鑰密碼密碼學中的常用數學知識群、環、域素數和互素數模運算模指數運算費爾馬定理、歐拉定理、卡米歇爾定理素性檢驗歐幾里得算法中國剩余定理&#xff08;CRT&#xff09;離散對數二次剩余循環群循環群的選取雙線性映射計算復雜性公鑰密碼體制的基本概念…

VINS-Fusion+UWB輔助算法高精度實現

VINS-FusionUWB輔助算法高精度實現 摘要 本文詳細介紹了基于VINS-Fusion框架結合UWB輔助的高精度定位算法實現。通過將視覺慣性里程計(VIO)與超寬帶(UWB)測距技術融合&#xff0c;顯著提高了復雜環境下的定位精度和魯棒性。本文首先分析了VINS-Fusion和UWB各自的技術特點&#…

新手向:Python實現簡易計算器

你是否一直想學習編程但不知從何入手&#xff1f;這篇詳細的教程將帶領完全零基礎的讀者&#xff0c;循序漸進地掌握如何用Python實現一個簡易計算器。我們將從最基本的編程概念講起&#xff0c;確保每一位初學者都能跟上進度。準備工作在開始之前&#xff0c;你需要&#xff1…

區塊鏈賦能供應鏈金融:解決信任與效率問題

摘要: 隨著全球經濟一體化和數字化進程的加速,供應鏈金融在實體經濟發展中的作用愈發關鍵。然而,傳統供應鏈金融面臨著信任機制薄弱和效率低下等諸多挑戰。區塊鏈技術憑借其去中心化、不可篡改、可追溯等特性,為供應鏈金融帶來了創新的解決方案,能夠有效解決信任與效率問題…

無人機 × 巡檢 × AI識別:一套可復制的超低延遲低空視頻感知系統搭建實踐

?? 引言&#xff1a;低空感知&#xff0c;正重構數字世界的“底層感官接口” 隨著低空經濟進入規模化部署階段&#xff0c;感知系統不再是“任務輔助”&#xff0c;而是演變為支撐智能化運行的基礎設施核心模塊。從電力巡檢的高空細節識別&#xff0c;到城市安防的區域態勢掌…

STM32U5 外部中斷不響應問題分析

關鍵字&#xff1a; EXTI 1. 問題背景 客戶的終端客戶反饋產品會有偶發性的功能異常。問題比較難以復現。 經過調查&#xff0c;在 BOOT 程序跳轉到 APP1 程序中時相對比較容易復現問題。查看客戶代碼&#xff0c;發現客戶在 BOOT 程序中會對 EXTI 進行初始化&#xff0c;跳…

17.Linux :selinux

Linux &#xff1a; selinux DAC vs MAC 對比模型控制方式決策依據安全強度DAC自主訪問控制文件所有者的權限設置低MAC強制訪問控制系統級安全策略極高SELinux的核心原理是基于 強制訪問控制&#xff08;MAC&#xff09; 模型&#xff0c;通過為系統資源打上安全標簽并制定精細…

如何在不停機的情況下,將MySQL單庫的數據遷移到分庫分表的架構上?

在業務高速發展的過程中&#xff0c;單庫單表的MySQL架構往往會成為系統性能的瓶頸。將單庫遷移到分庫分表架構是一種常見的擴展方案&#xff0c;但如何在保證業務連續性的前提下完成這一遷移是一個挑戰。以下是不停機遷移的幾種主要方案&#xff1a; 一、基于雙寫的遷移方案 1…