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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…