文章目录
- 引言
- 一、DispatcherServlet概述
- 二、DispatcherServlet初始化过程
- 三、请求接收与处理器匹配
- 四、请求参数绑定与处理器执行
- 五、视图解析与渲染
- 六、异常处理机制
- 总结
引言
SpringMVC框架是Java Web开发中最流行的MVC框架之一,其核心组件DispatcherServlet作为前端控制器,负责协调整个请求处理流程。理解DispatcherServlet的工作原理对于掌握SpringMVC框架至关重要,不仅有助于开发高质量的Web应用,还能在遇到问题时快速定位和解决。本文将深入剖析SpringMVC的请求处理流程,揭示DispatcherServlet的内部运作机制,通过代码示例展示各个环节的实现细节,帮助开发者全面把握SpringMVC的核心工作流程,从而更加高效地进行Web应用开发。
一、DispatcherServlet概述
DispatcherServlet是SpringMVC框架的核心,它扮演着前端控制器(Front Controller)的角色,是整个请求处理流程的调度中心。作为一个标准的Servlet,DispatcherServlet继承自HttpServlet,遵循Servlet生命周期,在Web容器启动时初始化并加载SpringMVC相关配置。它的主要职责是接收HTTP请求,并根据请求信息将其分发给相应的处理器(Handler),随后协调视图解析器(ViewResolver)渲染结果,最终返回响应给客户端。这种集中式的请求处理机制极大地简化了Web开发的复杂性,实现了松耦合的MVC架构模式。
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
/**
* 通过编程方式注册DispatcherServlet
* 这通常在Servlet 3.0+环境的WebApplicationInitializer实现类中完成
*/
public class MyWebAppInitializer implements org.springframework.web.WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 创建Spring Web应用上下文
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
// 注册配置类
context.register(WebConfig.class);
// 创建并注册DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", dispatcherServlet);
// 配置DispatcherServlet
registration.setLoadOnStartup(1); // 设置启动优先级
registration.addMapping("/"); // 设置URL映射模式
registration.setAsyncSupported(true); // 启用异步支持
// 可以添加初始化参数
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
二、DispatcherServlet初始化过程
DispatcherServlet的初始化是整个SpringMVC框架启动的关键环节。当Web容器启动时,DispatcherServlet会执行初始化流程,创建Spring应用上下文(WebApplicationContext),并加载各种处理请求所需的组件。初始化过程主要分为两个阶段:Servlet标准初始化和SpringMVC特定初始化。在标准初始化阶段,容器调用DispatcherServlet的init()方法;在SpringMVC特定初始化阶段,它会初始化九大组件,包括HandlerMapping、HandlerAdapter、ViewResolver等。这些组件共同构成了SpringMVC的核心处理能力,为后续的请求处理提供必要支持。
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* SpringMVC配置类,配置DispatcherServlet所需的核心组件
*/
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
/**
* 配置HandlerMapping,负责请求与处理器的映射
*/
@Bean
public HandlerMapping handlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
// 可以设置相关属性
mapping.setOrder(0); // 设置优先级
mapping.setUseSuffixPatternMatch(false); // 禁用后缀模式匹配
return mapping;
}
/**
* 配置HandlerAdapter,负责执行处理器
*/
@Bean
public HandlerAdapter handlerAdapter() {
return new RequestMappingHandlerAdapter();
}
/**
* 配置ViewResolver,负责解析视图
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// 可以配置其他组件,如LocaleResolver、ThemeResolver等
}
三、请求接收与处理器匹配
当客户端发送HTTP请求到达Web服务器后,DispatcherServlet首先接收请求并开始处理流程。DispatcherServlet通过调用doService()方法,将请求委托给doDispatch()方法进行具体处理。在这个阶段,DispatcherServlet会遍历所有注册的HandlerMapping实现,查找与当前请求匹配的Handler(处理器)。常用的HandlerMapping包括RequestMappingHandlerMapping(基于注解)和SimpleUrlHandlerMapping(基于URL配置)。一旦找到匹配的Handler,DispatcherServlet还会获取相关的拦截器(Interceptor),组合形成HandlerExecutionChain(处理器执行链),为后续的请求处理做准备。
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 控制器示例,演示请求映射
*/
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public String listUsers(@RequestParam(required = false) String filter) {
// 处理获取用户列表请求
return "User list filtered by: " + (filter != null ? filter : "none");
}
@GetMapping("/{id}")
public String getUserById(@PathVariable Long id) {
// 处理获取特定用户请求
return "User details for ID: " + id;
}
}
/**
* 自定义拦截器,将被添加到HandlerExecutionChain中
*/
@Component
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("LoggingInterceptor.preHandle: " + request.getRequestURI());
// 返回true继续处理,返回false中断处理
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
System.out.println("LoggingInterceptor.postHandle: " + request.getRequestURI());
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("LoggingInterceptor.afterCompletion: " + request.getRequestURI());
if (ex != null) {
System.out.println("Exception occurred: " + ex.getMessage());
}
}
}
/**
* 配置拦截器,将其注册到SpringMVC框架
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**") // 指定拦截路径
.excludePathPatterns("/api/public/**"); // 指定排除路径
}
}
四、请求参数绑定与处理器执行
找到匹配的Handler后,DispatcherServlet需要调用合适的HandlerAdapter来执行处理器。在SpringMVC中,最常用的是RequestMappingHandlerAdapter,它负责执行基于@RequestMapping注解的控制器方法。在执行前,HandlerAdapter会进行请求参数的解析与绑定,将HTTP请求中的参数转换为控制器方法所需的参数。这个过程涉及多种类型转换器和参数解析器,如RequestParamMethodArgumentResolver、PathVariableMethodArgumentResolver等。参数绑定完成后,HandlerAdapter调用控制器方法,执行业务逻辑,并获取处理结果(ModelAndView或返回值)。这一阶段是整个请求处理的核心环节。
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import javax.validation.Valid;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 演示参数绑定的控制器
*/
@Controller
@RequestMapping("/products")
public class ProductController {
/**
* 初始化WebDataBinder,用于自定义参数绑定规则
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
// 注册自定义日期编辑器
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
/**
* 展示创建产品表单
*/
@GetMapping("/new")
public String showCreateForm(Model model) {
model.addAttribute("product", new Product());
return "product/create";
}
/**
* 处理产品创建请求
* 演示多种参数绑定方式
*/
@PostMapping
public String createProduct(
@Valid @ModelAttribute("product") Product product, // 表单对象绑定和验证
BindingResult bindingResult, // 验证结果
@RequestParam(required = false) String category, // 请求参数
@CookieValue(value = "sessionId", required = false) String sessionId, // Cookie值
@RequestHeader("User-Agent") String userAgent, // 请求头
Model model // 模型
) {
// 检查验证结果
if (bindingResult.hasErrors()) {
return "product/create";
}
// 执行业务逻辑
System.out.println("Creating product: " + product.getName());
System.out.println("Category: " + category);
System.out.println("Session ID: " + sessionId);
System.out.println("User Agent: " + userAgent);
// 添加成功消息到模型
model.addAttribute("message", "Product created successfully!");
return "redirect:/products";
}
}
/**
* 产品实体类
*/
public class Product {
private Long id;
private String name;
private Double price;
private Date releaseDate;
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Double getPrice() { return price; }
public void setPrice(Double price) { this.price = price; }
public Date getReleaseDate() { return releaseDate; }
public void setReleaseDate(Date releaseDate) { this.releaseDate = releaseDate; }
}
五、视图解析与渲染
当控制器方法执行完毕后,它通常会返回一个逻辑视图名或直接返回数据。对于返回逻辑视图名的情况,DispatcherServlet需要将其解析为具体的视图对象。视图解析过程由ViewResolver接口的实现类负责,如InternalResourceViewResolver(用于JSP)、ThymeleafViewResolver(用于Thymeleaf模板)等。DispatcherServlet会遍历已注册的ViewResolver,查找能够解析当前逻辑视图名的解析器。一旦找到合适的视图对象,就会调用其render()方法,将模型数据(Model)渲染到视图中,生成响应内容。对于REST风格的接口,返回值可能被直接转换为JSON/XML,这种情况下不需要视图解析步骤。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 视图解析器配置类
*/
@Configuration
@EnableWebMvc
public class ViewResolverConfig implements WebMvcConfigurer {
/**
* 配置JSP视图解析器
*/
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
resolver.setOrder(2); // 设置优先级
return resolver;
}
/**
* 配置FreeMarker视图解析器
*/
@Bean
public ViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(true);
resolver.setPrefix("");
resolver.setSuffix(".ftl");
resolver.setContentType("text/html; charset=UTF-8");
resolver.setOrder(1); // 优先级高于JSP解析器
return resolver;
}
/**
* 配置FreeMarker引擎
*/
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/templates/");
configurer.setDefaultEncoding("UTF-8");
return configurer;
}
/**
* 内容协商视图解析器配置
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.enableContentNegotiation(new MappingJackson2JsonView());
}
}
/**
* 演示不同视图解析方式的控制器
*/
@Controller
public class ViewController {
/**
* 返回JSP视图
*/
@GetMapping("/page/jsp")
public String jspView(Model model) {
model.addAttribute("message", "Hello from JSP!");
return "hello"; // 解析为/WEB-INF/views/hello.jsp
}
/**
* 返回FreeMarker视图
*/
@GetMapping("/page/freemarker")
public String freemarkerView(Model model) {
model.addAttribute("message", "Hello from FreeMarker!");
return "hello"; // 解析为/WEB-INF/templates/hello.ftl
}
/**
* 直接返回ModelAndView
*/
@GetMapping("/page/manual")
public ModelAndView manualView() {
ModelAndView mav = new ModelAndView("custom");
mav.addObject("message", "Hello from custom view!");
return mav;
}
/**
* 返回JSON数据(不使用视图解析)
*/
@GetMapping("/api/data/{id}")
@ResponseBody
public Product jsonData(@PathVariable Long id) {
Product product = new Product();
product.setId(id);
product.setName("Sample Product");
product.setPrice(99.99);
return product; // 直接转换为JSON返回
}
}
六、异常处理机制
在请求处理过程中,可能会发生各种异常,如业务逻辑异常、参数验证异常等。SpringMVC提供了完善的异常处理机制,由DispatcherServlet协调,确保异常被正确处理并返回适当的响应。核心组件是HandlerExceptionResolver接口的实现类,包括DefaultHandlerExceptionResolver(处理标准Spring异常)、ExceptionHandlerExceptionResolver(处理@ExceptionHandler注解)等。当异常发生时,DispatcherServlet会遍历已注册的异常解析器,查找能够处理当前异常的解析器。异常处理可以通过@ExceptionHandler注解、@ControllerAdvice全局异常处理类或实现HandlerExceptionResolver接口来自定义。良好的异常处理设计能够提高系统的健壮性和用户体验。
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.BindException;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Date;
/**
* 全局异常处理器
*/
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
/**
* 处理业务逻辑异常
*/
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse handleBusinessException(BusinessException ex) {
return new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
ex.getMessage(),
new Date()
);
}
/**
* 处理资源未找到异常
*/
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ResponseBody
public ErrorResponse handleResourceNotFoundException(ResourceNotFoundException ex) {
return new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
new Date()
);
}
/**
* 处理参数验证异常
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public Map<String, Object> handleValidationExceptions(ConstraintViolationException ex) {
Map<String, Object> errors = new HashMap<>();
ex.getConstraintViolations().forEach(violation -> {
String fieldName = violation.getPropertyPath().toString();
String errorMessage = violation.getMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
/**
* 处理404错误(需配置throwExceptionIfNoHandlerFound=true)
*/
@Override
protected ResponseEntity<Object> handleNoHandlerFoundException(
NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
ErrorResponse error = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
"Resource not found: " + ex.getRequestURL(),
new Date()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
/**
* 处理表单绑定异常
*/
@Override
protected ResponseEntity<Object> handleBindException(
BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
/**
* 处理所有其他未捕获的异常
*/
@ExceptionHandler(Exception.class)
public ModelAndView handleGenericException(Exception ex) {
ModelAndView modelAndView = new ModelAndView("error/generic");
modelAndView.addObject("errorMessage", ex.getMessage());
modelAndView.addObject("timestamp", new Date());
return modelAndView;
}
}
/**
* 自定义业务异常
*/
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
/**
* 自定义资源未找到异常
*/
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
/**
* 错误响应DTO
*/
public class ErrorResponse {
private int status;
private String message;
private Date timestamp;
public ErrorResponse(int status, String message, Date timestamp) {
this.status = status;
this.message = message;
this.timestamp = timestamp;
}
// Getters and Setters
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Date getTimestamp() { return timestamp; }
public void setTimestamp(Date timestamp) { this.timestamp = timestamp; }
}
总结
SpringMVC的请求处理流程是一个精心设计的管道,由DispatcherServlet作为中央调度器协调各个组件的工作。这一流程始于DispatcherServlet接收HTTP请求,随后通过HandlerMapping查找匹配的Handler,利用HandlerAdapter执行处理器逻辑,最后通过ViewResolver解析视图并渲染响应结果。整个过程高度模块化,各组件职责明确,通过策略模式和组合模式实现了高度的可扩展性和灵活性。了解这一流程不仅有助于开发者编写高质量的代码,还能在遇到问题时快速定位根源。在实际应用中,可以通过自定义HandlerMapping、HandlerAdapter、ViewResolver等组件来扩展框架能力,满足特定业务需求。此外,合理配置拦截器和异常处理器,能够提升应用的安全性和用户体验。掌握SpringMVC的请求处理流程对于Java Web开发者而言是至关重要的基础知识,是构建高性能、可维护Web应用的关键。