深入理解 DispatcherServlet 与请求处理机制
Spring MVC 的核心是 DispatcherServlet(前端控制器),它负责协调各个组件完成请求处理。
用户通过浏览器发送 HTTP 请求到服务器,请求被 DispatcherServlet 接收。
# 示例请求
GET http://localhost:8080/users/123
DispatcherServlet 作为前端控制器,拦截所有符合配置的请求。
// DispatcherServlet 配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
// DispatcherServlet 默认拦截 "/"
}
DispatcherServlet 调用 HandlerMapping,根据请求 URL 查找对应的 Handler(Controller)。
// HandlerMapping 查找过程
HandlerExecutionChain chain = handlerMapping.getHandler(request);
// chain 包含:Handler + 拦截器链
// 常用的 HandlerMapping 实现:
// 1. RequestMappingHandlerMapping(处理 @RequestMapping)
// 2. BeanNameUrlHandlerMapping(根据 Bean 名称匹配)
// 3. SimpleUrlHandlerMapping(简单 URL 映射)
DispatcherServlet 通过 HandlerAdapter 适配器执行 Handler。
// HandlerAdapter 适配过程
HandlerAdapter adapter = getHandlerAdapter(handler);
ModelAndView mv = adapter.handle(request, response, handler);
// 常用的 HandlerAdapter 实现:
// 1. RequestMappingHandlerAdapter(处理 @RequestMapping 方法)
// 2. HttpRequestHandlerAdapter(处理 HttpRequestHandler)
// 3. SimpleControllerHandlerAdapter(处理 Controller 接口)
在执行 Handler 之前,执行拦截器链的 preHandle 方法。
// 拦截器执行
for (HandlerInterceptor interceptor : interceptors) {
if (!interceptor.preHandle(request, response, handler)) {
return; // 如果返回 false,中断请求
}
}
Controller 方法执行业务逻辑,返回 ModelAndView 或数据。
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public ModelAndView getUser(@PathVariable Long id) {
User user = userService.findById(id);
ModelAndView mv = new ModelAndView("user/detail");
mv.addObject("user", user);
return mv;
}
// 或者返回 JSON
@GetMapping("/{id}")
@ResponseBody
public User getUserJson(@PathVariable Long id) {
return userService.findById(id);
}
}
Handler 执行完成后,执行拦截器链的 postHandle 方法。
// 逆序执行 postHandle
for (int i = interceptors.length - 1; i >= 0; i--) {
interceptors[i].postHandle(request, response, handler, mv);
}
如果返回 ModelAndView,ViewResolver 根据视图名称解析出具体的 View 对象。
// ViewResolver 解析过程
View view = viewResolver.resolveViewName(viewName, locale);
// 常用的 ViewResolver 实现:
// 1. InternalResourceViewResolver(JSP 视图)
// 2. ThymeleafViewResolver(Thymeleaf 模板)
// 3. FreeMarkerViewResolver(FreeMarker 模板)
// 配置示例
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
View 渲染模型数据,生成 HTML 响应返回给客户端。
// 视图渲染
view.render(mv.getModelMap(), request, response);
// 执行拦截器的 afterCompletion 方法
for (int i = interceptors.length - 1; i >= 0; i--) {
interceptors[i].afterCompletion(request, response, handler, ex);
}
1. 用户发送请求
↓
2. DispatcherServlet 接收请求
↓
3. HandlerMapping 查找 Handler
├─ RequestMappingHandlerMapping
├─ BeanNameUrlHandlerMapping
└─ SimpleUrlHandlerMapping
↓
4. 返回 HandlerExecutionChain(Handler + 拦截器)
↓
5. HandlerAdapter 适配 Handler
├─ RequestMappingHandlerAdapter
├─ HttpRequestHandlerAdapter
└─ SimpleControllerHandlerAdapter
↓
6. 执行拦截器 preHandle()
↓
7. 执行 Handler(Controller 方法)
↓
8. 执行拦截器 postHandle()
↓
9. ViewResolver 解析视图
├─ InternalResourceViewResolver
├─ ThymeleafViewResolver
└─ FreeMarkerViewResolver
↓
10. View 渲染视图
↓
11. 执行拦截器 afterCompletion()
↓
12. 返回响应给客户端
// DispatcherServlet 核心方法(简化版)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
try {
// 1. 查找 Handler
mappedHandler = getHandler(request);
if (mappedHandler == null) {
noHandlerFound(request, response);
return;
}
// 2. 获取 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 3. 执行拦截器 preHandle
if (!mappedHandler.applyPreHandle(request, response)) {
return;
}
// 4. 执行 Handler
mv = ha.handle(request, response, mappedHandler.getHandler());
// 5. 执行拦截器 postHandle
mappedHandler.applyPostHandle(request, response, mv);
// 6. 处理结果(视图渲染)
processDispatchResult(request, response, mappedHandler, mv, null);
} catch (Exception ex) {
// 异常处理
processHandlerException(request, response, mappedHandler, ex);
} finally {
// 7. 执行拦截器 afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
}
作用:根据请求 URL 查找对应的 Handler(Controller)。
// 常用实现
RequestMappingHandlerMapping // 处理 @RequestMapping 注解
BeanNameUrlHandlerMapping // 根据 Bean 名称匹配 URL
SimpleUrlHandlerMapping // 简单 URL 映射
// 使用示例
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
作用:适配不同类型的 Handler,执行具体的处理方法。
// 常用实现
RequestMappingHandlerAdapter // 处理 @RequestMapping 方法
HttpRequestHandlerAdapter // 处理 HttpRequestHandler
SimpleControllerHandlerAdapter // 处理 Controller 接口
// 适配器模式:统一调用接口
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler);
作用:处理 Handler 执行过程中抛出的异常。
// 常用实现
ExceptionHandlerExceptionResolver // 处理 @ExceptionHandler
ResponseStatusExceptionResolver // 处理 @ResponseStatus
DefaultHandlerExceptionResolver // 处理 Spring MVC 标准异常
// 使用示例
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception e) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("message", e.getMessage());
return mv;
}
}
作用:根据视图名称解析出具体的 View 对象。
// 常用实现
InternalResourceViewResolver // JSP 视图
ThymeleafViewResolver // Thymeleaf 模板
FreeMarkerViewResolver // FreeMarker 模板
// 配置示例
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
作用:当 Handler 没有返回视图名称时,根据请求 URL 自动生成视图名称。
// 默认实现:DefaultRequestToViewNameTranslator
// 请求 /users/list → 视图名称 users/list
@GetMapping("/users/list")
public void listUsers(Model model) {
// 没有返回视图名称,自动使用 "users/list"
model.addAttribute("users", userService.findAll());
}
作用:解析客户端的 Locale(语言环境),支持国际化。
// 常用实现
AcceptHeaderLocaleResolver // 根据 HTTP 请求头
SessionLocaleResolver // 存储在 Session
CookieLocaleResolver // 存储在 Cookie
// 配置示例
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
作用:解析应用的主题,支持动态切换主题。
// 常用实现
FixedThemeResolver // 固定主题
SessionThemeResolver // Session 存储
CookieThemeResolver // Cookie 存储
// 配置示例
@Bean
public ThemeResolver themeResolver() {
SessionThemeResolver resolver = new SessionThemeResolver();
resolver.setDefaultThemeName("default");
return resolver;
}
作用:处理文件上传请求。
// 常用实现
CommonsMultipartResolver // 基于 Commons FileUpload
StandardServletMultipartResolver // 基于 Servlet 3.0
// 配置示例
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10485760); // 10MB
resolver.setDefaultEncoding("UTF-8");
return resolver;
}
// 使用示例
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
file.transferTo(new File("/uploads/" + file.getOriginalFilename()));
return "success";
}
作用:管理重定向时的临时数据(FlashMap)。
// 默认实现:SessionFlashMapManager
// 将数据存储在 Session 中,重定向后自动删除
@PostMapping("/save")
public String save(User user, RedirectAttributes attributes) {
userService.save(user);
// 添加 Flash 属性(重定向后可用)
attributes.addFlashAttribute("message", "保存成功");
return "redirect:/users/list";
}
@GetMapping("/list")
public String list(Model model) {
// 可以获取 Flash 属性
String message = (String) model.getAttribute("message");
return "users/list";
}
| 组件 | 作用 | 是否必需 |
|---|---|---|
| HandlerMapping | 查找 Handler | ✅ 必需 |
| HandlerAdapter | 执行 Handler | ✅ 必需 |
| HandlerExceptionResolver | 异常处理 | ✅ 必需 |
| ViewResolver | 解析视图 | ⚠️ 视图渲染需要 |
| RequestToViewNameTranslator | 视图名称转换 | ❌ 可选 |
| LocaleResolver | 国际化 | ❌ 可选 |
| ThemeResolver | 主题切换 | ❌ 可选 |
| MultipartResolver | 文件上传 | ⚠️ 文件上传需要 |
| FlashMapManager | 重定向数据 | ❌ 可选 |
DispatcherServlet 继承自 HttpServlet,在 Web 容器启动时初始化。
// 继承关系
DispatcherServlet
→ FrameworkServlet
→ HttpServletBean
→ HttpServlet
// 初始化入口
public void init(ServletConfig config) throws ServletException {
// 1. 设置 ServletConfig
this.config = config;
// 2. 初始化 Bean 属性
initBeanWrapper();
// 3. 初始化 Servlet Bean
initServletBean();
}
// FrameworkServlet.initServletBean()
protected final void initServletBean() throws ServletException {
// 创建或刷新 WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 初始化 FrameworkServlet
initFrameworkServlet();
}
// 创建 WebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// 1. 获取根容器(由 ContextLoaderListener 创建)
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
// 2. 创建子容器
WebApplicationContext wac = createWebApplicationContext(rootContext);
// 3. 刷新容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
// DispatcherServlet.onRefresh()
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
// 初始化所有策略组件
protected void initStrategies(ApplicationContext context) {
// 1. 初始化文件上传解析器
initMultipartResolver(context);
// 2. 初始化国际化解析器
initLocaleResolver(context);
// 3. 初始化主题解析器
initThemeResolver(context);
// 4. 初始化 HandlerMapping
initHandlerMappings(context);
// 5. 初始化 HandlerAdapter
initHandlerAdapters(context);
// 6. 初始化异常处理器
initHandlerExceptionResolvers(context);
// 7. 初始化视图名称转换器
initRequestToViewNameTranslator(context);
// 8. 初始化视图解析器
initViewResolvers(context);
// 9. 初始化 FlashMap 管理器
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 从容器中查找所有 HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
// 只查找名为 "handlerMapping" 的 Bean
HandlerMapping hm = context.getBean("handlerMapping", HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
// 如果没有找到,使用默认策略
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
}
Web 容器启动
↓
Servlet.init()
↓
HttpServletBean.init()
↓
FrameworkServlet.initServletBean()
↓
创建 WebApplicationContext
├─ 获取根容器(Root WebApplicationContext)
├─ 创建子容器(Servlet WebApplicationContext)
└─ 刷新容器
↓
DispatcherServlet.onRefresh()
↓
initStrategies() - 初始化九大组件
├─ initMultipartResolver()
├─ initLocaleResolver()
├─ initThemeResolver()
├─ initHandlerMappings()
├─ initHandlerAdapters()
├─ initHandlerExceptionResolvers()
├─ initRequestToViewNameTranslator()
├─ initViewResolvers()
└─ initFlashMapManager()
↓
初始化完成,等待请求
// 1. 传统 web.xml 配置
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
// 2. Spring Boot 自动配置
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// Spring Boot 自动注册 DispatcherServlet
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
处理 @RequestMapping 注解的 Handler。
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id) {
return "user";
}
}
根据 Bean 名称匹配 URL。
@Component("/hello")
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
return new ModelAndView("hello");
}
}
通过配置文件映射 URL 到 Handler。
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/welcome">welcomeController</prop>
</props>
</property>
</bean>
作用:适配不同类型的 Handler,统一调用方式。
Spring MVC 支持多种 Handler 类型:
// 1. RequestMappingHandlerAdapter
// 处理 @RequestMapping 注解的方法
@GetMapping("/user")
public String getUser() { }
// 2. HttpRequestHandlerAdapter
// 处理 HttpRequestHandler
public class MyHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request,
HttpServletResponse response) {
// 处理请求
}
}
// 3. SimpleControllerHandlerAdapter
// 处理 Controller 接口
public class MyController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
return new ModelAndView("view");
}
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// Controller 返回 "user"
// 实际路径:/WEB-INF/views/user.jsp
@Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
return resolver;
}
@Bean
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setSuffix(".ftl");
return resolver;
}
根据请求的 Content-Type 选择合适的 ViewResolver。
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
if (id < 0) {
throw new IllegalArgumentException("ID 不能为负数");
}
return "user";
}
@ExceptionHandler(IllegalArgumentException.class)
public ModelAndView handleIllegalArgument(IllegalArgumentException ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("message", ex.getMessage());
return mav;
}
}
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView handleException(Exception ex) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("message", ex.getMessage());
return mav;
}
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleNullPointer() {
return "null-pointer-error";
}
}
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "用户不存在")
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
@Bean
public SimpleMappingExceptionResolver exceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("NullPointerException", "error/null");
mappings.setProperty("Exception", "error/generic");
resolver.setExceptionMappings(mappings);
return resolver;
}
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10485760); // 10MB
resolver.setMaxInMemorySize(1048576); // 1MB
resolver.setDefaultEncoding("UTF-8");
return resolver;
}
// Spring Boot 配置
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
@Controller
public class FileUploadController {
// 单文件上传
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "error";
}
try {
String filename = file.getOriginalFilename();
String path = "/uploads/" + filename;
file.transferTo(new File(path));
return "success";
} catch (IOException e) {
return "error";
}
}
// 多文件上传
@PostMapping("/batch-upload")
public String batchUpload(@RequestParam("files") MultipartFile[] files) {
for (MultipartFile file : files) {
if (!file.isEmpty()) {
// 保存文件
}
}
return "success";
}
}
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">上传</button>
</form>
返回视图名称,需要配合 ViewResolver 解析视图。
@Controller
public class UserController {
@GetMapping("/user")
public String getUser(Model model) {
model.addAttribute("name", "张三");
return "user"; // 返回视图名称
}
// 如果要返回 JSON,需要加 @ResponseBody
@GetMapping("/api/user")
@ResponseBody
public User getUserJson() {
return new User("张三", 25);
}
}
相当于 @Controller + @ResponseBody,直接返回数据(JSON/XML)。
@RestController
public class UserRestController {
@GetMapping("/api/user")
public User getUser() {
return new User("张三", 25); // 自动转换为 JSON
}
@GetMapping("/api/users")
public List<User> getUsers() {
return Arrays.asList(
new User("张三", 25),
new User("李四", 30)
);
}
}
// 等价于
@Controller
@ResponseBody
public class UserRestController { }
@RestController
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
@GetMapping("/api/user")
public User getUser() {
return new User();
}
// 方法级别配置
@CrossOrigin(
origins = {"http://localhost:3000", "http://example.com"},
methods = {RequestMethod.GET, RequestMethod.POST},
maxAge = 3600
)
@PostMapping("/api/user")
public User createUser(@RequestBody User user) {
return user;
}
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://localhost:3000");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
┌─────────────────────────────────┐
│ Root ApplicationContext │ 父容器
│ (Spring 容器) │
│ - Service │
│ - Repository │
│ - DataSource │
└─────────────────────────────────┘
↑
│ 父子关系
│
┌─────────────────────────────────┐
│ WebApplicationContext │ 子容器
│ (Spring MVC 容器) │
│ - Controller │
│ - HandlerMapping │
│ - ViewResolver │
└─────────────────────────────────┘
<!-- web.xml -->
<!-- 1. 配置 Spring 容器(父容器)-->
<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>
<!-- 2. 配置 Spring MVC 容器(子容器)-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
</servlet>
<!-- applicationContext.xml(父容器)-->
<context:component-scan base-package="com.example">
<!-- 排除 Controller -->
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- spring-mvc.xml(子容器)-->
<context:component-scan base-package="com.example.controller">
<!-- 只扫描 Controller -->
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
@RestController
@RequestMapping("/api/users")
public class UserRestController {
// GET /api/users - 查询所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/1 - 查询单个用户
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
// POST /api/users - 创建用户
@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
// PUT /api/users/1 - 更新用户
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
return userService.update(user);
}
// DELETE /api/users/1 - 删除用户
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
// PATCH /api/users/1 - 部分更新
@PatchMapping("/{id}")
public User patchUser(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
return userService.patch(id, updates);
}
}
@Data
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(200);
response.setMessage("success");
response.setData(data);
return response;
}
}
@GetMapping("/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ApiResponse.success(user);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // 201
public User createUser(@RequestBody User user) {
return userService.save(user);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT) // 204
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
# messages_zh_CN.properties
welcome=欢迎
user.name=用户名
user.age=年龄
# messages_en_US.properties
welcome=Welcome
user.name=Username
user.age=Age
// 方式1:基于 Session
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
// 方式2:基于 Cookie
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
resolver.setCookieName("language");
return resolver;
}
// 方式3:基于请求头
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return resolver;
}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang"); // 参数名
registry.addInterceptor(interceptor);
}
}
// 访问:http://localhost:8080/user?lang=en_US
@Controller
public class UserController {
@Autowired
private MessageSource messageSource;
@GetMapping("/welcome")
public String welcome(Model model, Locale locale) {
String message = messageSource.getMessage("welcome", null, locale);
model.addAttribute("message", message);
return "welcome";
}
}
// JSP 中使用
<spring:message code="welcome" />
@Controller
public class UserController {
// 方式1:返回字符串
@GetMapping("/user")
public String getUser() {
return "forward:/user/detail"; // 转发到 /user/detail
}
// 方式2:使用 RequestDispatcher
@GetMapping("/user2")
public void getUser2(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.getRequestDispatcher("/user/detail").forward(request, response);
}
}
@Controller
public class UserController {
// 方式1:返回字符串
@PostMapping("/user")
public String createUser() {
// 保存用户
return "redirect:/user/list"; // 重定向到 /user/list
}
// 方式2:使用 RedirectAttributes 传递参数
@PostMapping("/user2")
public String createUser2(RedirectAttributes attributes) {
attributes.addAttribute("id", 1); // URL 参数
attributes.addFlashAttribute("message", "创建成功"); // Flash 属性
return "redirect:/user/detail";
}
// 方式3:重定向到外部 URL
@GetMapping("/external")
public String redirectExternal() {
return "redirect:https://www.example.com";
}
}
| 特性 | 转发(Forward) | 重定向(Redirect) |
|---|---|---|
| 请求次数 | 1次 | 2次 |
| URL 变化 | 不变 | 改变 |
| 数据共享 | Request 域共享 | 不共享 |
| 跨域 | 不能 | 可以 |
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 检查是否文件上传请求
processedRequest = checkMultipart(request);
// 2. 根据请求获取 Handler(HandlerMapping)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 3. 根据 Handler 获取 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 4. 执行拦截器的 preHandle 方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 5. 执行 Handler(Controller 方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 6. 设置默认视图名
applyDefaultViewName(processedRequest, mv);
// 7. 执行拦截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
// 8. 处理结果(渲染视图或处理异常)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) {
// 9. 如果有异常,处理异常
if (exception != null) {
mv = processHandlerException(request, response, handler, exception);
}
// 10. 渲染视图
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
}
// 11. 执行拦截器的 afterCompletion 方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
// 1. getHandler - 获取 Handler
protected HandlerExecutionChain getHandler(HttpServletRequest request) {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
// 2. getHandlerAdapter - 获取适配器
protected HandlerAdapter getHandlerAdapter(Object handler) {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler");
}
// 3. render - 渲染视图
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 通过 ViewResolver 解析视图
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
} else {
view = mv.getView();
}
// 渲染视图
view.render(mv.getModelInternal(), request, response);
}
doDispatch 执行流程:
1. checkMultipart() → 检查文件上传
2. getHandler() → 获取 Handler
3. getHandlerAdapter() → 获取 HandlerAdapter
4. applyPreHandle() → 拦截器前置处理
5. ha.handle() → 执行 Controller 方法
6. applyPostHandle() → 拦截器后置处理
7. processDispatchResult() → 处理结果
├─ processHandlerException() → 处理异常
├─ render() → 渲染视图
└─ triggerAfterCompletion() → 拦截器完成处理