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中扮演着"指挥官"的角色,其核心职责包括:
- 请求分发:根据请求的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());}
}