难度:⭐⭐⭐⭐ | 适合人群:想深入理解SpringMVC核心原理的开发者
💥 开场:一次404的"灵异事件"
时间: 周三下午3点
地点: 办公室
事件: 紧急Bug修复
测试妹子: "你的接口怎么404了?我明明看到Controller写了啊!"
我: "不可能啊,我看看..." 😰
代码明明写了:
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
访问: http://localhost:8080/api/user/1
结果: 404 Not Found 😱
我: "WTF?明明写了啊!"
排查半小时后...
原因: 启动类和Controller不在同一个包下,ComponentScan没扫描到!
// 启动类在 com.example.app
@SpringBootApplication
public class Application {
}
// Controller在 com.example.web (没被扫描)
@RestController
public class UserController {
}
测试妹子: "你这都能错?SpringMVC怎么工作的你知道吗?"
我: "这..." 😓(语塞)
第二天,请教架构师老李:
我: "李哥,SpringMVC到底是怎么把请求路由到Controller方法的?"
老李: "这就要从DispatcherServlet说起了,它是SpringMVC的核心,负责请求的分发。整个流程有9个步骤,涉及9大组件..."
我: "这么复杂?" 🤔
老李: "来,我给你画个图,你就明白了..."
🎯 第一问:SpringMVC是什么?
MVC模式回顾
什么是MVC?
MVC = Model + View + Controller
Model(模型) - 数据和业务逻辑
View(视图) - 页面展示
Controller(控制器) - 请求处理和调度
传统MVC流程:
用户请求
↓
Controller(接收请求)
↓
调用 Model(处理业务)
↓
返回 View(展示结果)
↓
响应用户
SpringMVC定位
SpringMVC是什么?
SpringMVC是Spring框架提供的Web模块,基于MVC设计模式,用于构建Web应用。
核心特点:
- ✅ 基于Servlet API
- ✅ 前端控制器模式(DispatcherServlet)
- ✅ 松耦合、可扩展
- ✅ 与Spring IoC无缝集成
SpringMVC架构图
[浏览器/客户端]
↓
HTTP请求
↓
┌──────────────────────────┐
│ DispatcherServlet │ ← 前端控制器(核心)
│ (请求分发器) │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ HandlerMapping │ ← 处理器映射器
│ (找到对应的Handler) │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ HandlerAdapter │ ← 处理器适配器
│ (执行Handler) │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ Controller │ ← 控制器(你写的)
│ (处理业务逻辑) │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ ViewResolver │ ← 视图解析器
│ (解析视图名称) │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ View │ ← 视图(JSP/Thymeleaf)
│ (渲染页面) │
└──────────────────────────┘
↓
HTTP响应
↓
[浏览器/客户端]
DispatcherServlet的定位
DispatcherServlet是什么?
DispatcherServlet是SpringMVC的前端控制器,是整个流程的指挥中心。
类比:
- DispatcherServlet = 交通指挥中心
- 各种组件 = 交警、红绿灯、路标
- 请求 = 车辆
职责:
- 接收所有HTTP请求
- 委托给各个组件处理
- 协调整个请求处理流程
- 返回响应
🔧 第二问:DispatcherServlet的9大组件
组件概览
public class DispatcherServlet extends FrameworkServlet {
/** 1. 处理器映射器 */
private List<HandlerMapping> handlerMappings;
/** 2. 处理器适配器 */
private List<HandlerAdapter> handlerAdapters;
/** 3. 处理器异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** 4. 视图解析器 */
private List<ViewResolver> viewResolvers;
/** 5. 请求到视图名翻译器 */
private RequestToViewNameTranslator viewNameTranslator;
/** 6. 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 7. 本地化解析器 */
private LocaleResolver localeResolver;
/** 8. 主题解析器 */
private ThemeResolver themeResolver;
/** 9. FlashMap管理器 */
private FlashMapManager flashMapManager;
}
组件1:HandlerMapping(处理器映射器)
作用: 根据请求URL找到对应的Handler(Controller方法)
常见实现:
// 1. RequestMappingHandlerMapping(最常用)
// 处理 @RequestMapping 注解
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) { }
// 2. BeanNameUrlHandlerMapping
// 根据Bean名称映射URL
@Component("/user/list")
public class UserListController { }
// 3. SimpleUrlHandlerMapping
// 通过配置文件映射URL
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/user/**">userController</prop>
</props>
</property>
</bean>
工作原理:
请求:GET /api/user/123
↓
HandlerMapping遍历所有映射规则
↓
匹配到:UserController.getUser() 方法
↓
返回HandlerExecutionChain(Handler + 拦截器)
组件2:HandlerAdapter(处理器适配器)
作用: 适配不同类型的Handler,执行具体的处理方法
为什么需要适配器?
// SpringMVC支持多种Handler类型
// 类型1:@RequestMapping注解的方法
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) { }
// 类型2:实现Controller接口
public class UserController implements Controller {
public ModelAndView handleRequest(...) { }
}
// 类型3:实现HttpRequestHandler接口
public class UserHandler implements HttpRequestHandler {
public void handleRequest(...) { }
}
不同Handler调用方式不同,需要适配器统一处理!
常见实现:
// 1. RequestMappingHandlerAdapter(最常用)
// 处理 @RequestMapping 注解的方法
// 2. SimpleControllerHandlerAdapter
// 处理实现Controller接口的类
// 3. HttpRequestHandlerAdapter
// 处理实现HttpRequestHandler接口的类
组件3:ViewResolver(视图解析器)
作用: 将逻辑视图名解析为具体的View对象
示例:
@GetMapping("/user/list")
public String listUsers(Model model) {
List<User> users = userService.findAll();
model.addAttribute("users", users);
return "user/list"; // 逻辑视图名
}
ViewResolver工作:
逻辑视图名:"user/list"
↓
ViewResolver解析
↓
实际路径:"/WEB-INF/views/user/list.jsp"
↓
返回View对象
常见实现:
// 1. InternalResourceViewResolver(JSP)
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
// 2. ThymeleafViewResolver(Thymeleaf)
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
return resolver;
}
// 3. FreeMarkerViewResolver(FreeMarker)
组件4:HandlerExceptionResolver(异常解析器)
作用: 处理Handler执行过程中抛出的异常
常见实现:
// 1. ExceptionHandlerExceptionResolver
// 处理 @ExceptionHandler 注解
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity.status(404).body(new ErrorResponse(ex.getMessage()));
}
// 2. ResponseStatusExceptionResolver
// 处理 @ResponseStatus 注解
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException { }
// 3. DefaultHandlerExceptionResolver
// 处理Spring MVC标准异常
组件5-9:其他组件
5. MultipartResolver(文件上传解析器)
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(10485760); // 10MB
return resolver;
}
6. LocaleResolver(国际化解析器)
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver resolver = new SessionLocaleResolver();
resolver.setDefaultLocale(Locale.CHINA);
return resolver;
}
7. ThemeResolver(主题解析器)
@Bean
public ThemeResolver themeResolver() {
SessionThemeResolver resolver = new SessionThemeResolver();
resolver.setDefaultThemeName("default");
return resolver;
}
8. RequestToViewNameTranslator(视图名翻译器)
// 当Controller方法返回null时,自动根据URL生成视图名
// 请求:/user/list → 视图名:user/list
9. FlashMapManager(重定向数据管理器)
// 用于redirect时传递数据
redirectAttributes.addFlashAttribute("message", "保存成功");
return "redirect:/user/list";
🔍 第三问:请求处理完整流程(9步骤)
流程概览
步骤1:DispatcherServlet接收请求
↓
步骤2:HandlerMapping查找Handler
↓
步骤3:执行拦截器的preHandle方法
↓
步骤4:HandlerAdapter调用Handler
↓
步骤5:Handler执行业务逻辑
↓
步骤6:执行拦截器的postHandle方法
↓
步骤7:ViewResolver解析视图
↓
步骤8:渲染视图
↓
步骤9:执行拦截器的afterCompletion方法
步骤1:DispatcherServlet接收请求
入口方法:
public class DispatcherServlet extends FrameworkServlet {
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) {
// 1. 设置request属性
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
// 2. 保存FlashMap
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, inputFlashMap);
}
// 3. 分发请求(核心)
doDispatch(request, response);
}
}
步骤2:HandlerMapping查找Handler
核心方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 1. 检查是否文件上传请求
processedRequest = checkMultipart(request);
// 2. 获取Handler(核心!)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没找到Handler,返回404
noHandlerFound(processedRequest, response);
return;
}
// ... 后续处理
}
catch (Exception ex) {
dispatchException = ex;
}
}
finally {
// 清理资源
}
}
getHandler()方法:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历所有HandlerMapping
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler; // 找到就返回
}
}
}
return null;
}
HandlerExecutionChain结构:
public class HandlerExecutionChain {
private final Object handler; // Controller方法
private HandlerInterceptor[] interceptors; // 拦截器数组
private List<HandlerInterceptor> interceptorList; // 拦截器列表
}
步骤3:执行拦截器的preHandle方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ... 获取Handler
// 3. 执行拦截器的前置方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return; // 如果返回false,中断请求
}
// ... 后续处理
}
applyPreHandle()实现:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 顺序执行所有拦截器
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
// 调用preHandle
if (!interceptor.preHandle(request, response, this.handler)) {
// 如果返回false,触发afterCompletion回调
triggerAfterCompletion(request, response, null);
return false; // 中断请求
}
this.interceptorIndex = i; // 记录执行到哪个拦截器
}
}
return true;
}
步骤4:HandlerAdapter调用Handler
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ... 执行拦截器preHandle
// 4. 获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 5. 调用Handler(执行Controller方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ... 后续处理
}
getHandlerAdapter()方法:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历所有HandlerAdapter
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter; // 找到支持的适配器
}
}
}
throw new ServletException("No adapter for handler [" + handler + "]");
}
步骤5:Handler执行业务逻辑
RequestMappingHandlerAdapter的handle方法:
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 调用Controller方法
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
invokeHandlerMethod()核心流程:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 1. 创建数据绑定工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 2. 创建模型工厂
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 3. 创建方法调用器
ServletInvocableHandlerMethod invocableMethod =
createInvocableHandlerMethod(handlerMethod);
// 4. 设置参数解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 5. 设置返回值处理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 6. 设置数据绑定工厂
invocableMethod.setDataBinderFactory(binderFactory);
// 7. 创建ModelAndViewContainer
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 8. 调用Controller方法(核心!)
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 9. 获取ModelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
invokeAndHandle()方法:
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 1. 调用Controller方法,获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 2. 设置响应状态
setResponseStatus(webRequest);
// 3. 处理返回值
if (returnValue == null) {
// 返回值为null的处理
if (isRequestNotModified(webRequest) || getResponseStatus() != null ||
mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
// 4. 使用返回值处理器处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
throw ex;
}
}
invokeForRequest()方法:
public Object invokeForRequest(ServletWebRequest request,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 1. 解析方法参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
// 2. 反射调用Controller方法
return doInvoke(args);
}
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 反射调用
return getBridgedMethod().invoke(getBean(), args);
}
catch (Exception ex) {
throw ex;
}
}
步骤6:执行拦截器的postHandle方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ... Handler执行完成,得到ModelAndView
// 6. 执行拦截器的后置方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
// ... 后续处理
}
applyPostHandle()实现:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response,
ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 倒序执行所有拦截器
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
步骤7:ViewResolver解析视图
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ... 执行拦截器postHandle
// 7. 处理分发结果(渲染视图)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
processDispatchResult()方法:
private void processDispatchResult(HttpServletRequest request,
HttpServletResponse response, HandlerExecutionChain mappedHandler,
ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
// 1. 处理异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 2. 渲染视图
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// 3. 执行拦截器的afterCompletion方法
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
步骤8:渲染视图
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 1. 解析视图名,获取View对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" +
mv.getViewName() + "'");
}
}
else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains " +
"a view name nor a View object");
}
}
// 2. 渲染视图
view.render(mv.getModelInternal(), request, response);
}
resolveViewName()方法:
protected View resolveViewName(String viewName, Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
// 遍历所有ViewResolver
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
步骤9:执行拦截器的afterCompletion方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
Exception ex) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
// 倒序执行(只执行preHandle返回true的拦截器)
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
💻 第四问:手写简易版DispatcherServlet
目标
实现一个最简单的DispatcherServlet,支持:
- ✅ URL映射到Controller方法
- ✅ 参数解析(@PathVariable、@RequestParam)
- ✅ 返回值处理(@ResponseBody)
实现代码
1. 自定义注解
package com.example.mvc;
import java.lang.annotation.*;
/**
* Controller注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value() default "";
}
/**
* RequestMapping注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
/**
* PathVariable注解
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PathVariable {
String value();
}
/**
* ResponseBody注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ResponseBody {
}
2. Handler封装类
package com.example.mvc;
import java.lang.reflect.Method;
/**
* Handler封装类
*/
public class HandlerMethod {
private Object bean; // Controller实例
private Method method; // Controller方法
private String[] paramNames; // 参数名称
public HandlerMethod(Object bean, Method method, String[] paramNames) {
this.bean = bean;
this.method = method;
this.paramNames = paramNames;
}
// Getter方法
public Object getBean() {
return bean;
}
public Method getMethod() {
return method;
}
public String[] getParamNames() {
return paramNames;
}
}
3. 简易DispatcherServlet
package com.example.mvc;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 简易版DispatcherServlet
*/
public class SimpleDispatcherServlet extends HttpServlet {
// 存储URL和Handler的映射关系
private List<HandlerMapping> handlerMappings = new ArrayList<>();
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("===== SimpleDispatcherServlet初始化 =====");
// 1. 扫描Controller
String basePackage = config.getInitParameter("basePackage");
if (basePackage == null) {
basePackage = "com.example";
}
scanPackage(basePackage);
System.out.println("扫描到 " + handlerMappings.size() + " 个Handler");
for (HandlerMapping mapping : handlerMappings) {
System.out.println(" " + mapping.getPattern() + " -> " +
mapping.getHandlerMethod().getMethod().getName());
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doDispatch(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doDispatch(req, resp);
}
/**
* 核心分发方法
*/
private void doDispatch(HttpServletRequest request, HttpServletResponse response)
throws IOException {
try {
// 1. 获取请求URL
String url = request.getRequestURI();
String contextPath = request.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
System.out.println("\n===== 处理请求:" + url + " =====");
// 2. 查找Handler
HandlerMapping handlerMapping = getHandler(url);
if (handlerMapping == null) {
response.setStatus(404);
response.getWriter().write("404 Not Found: " + url);
return;
}
HandlerMethod handlerMethod = handlerMapping.getHandlerMethod();
System.out.println("找到Handler:" + handlerMethod.getMethod().getName());
// 3. 解析参数
Object[] args = parseMethodArguments(request, handlerMapping, url);
// 4. 反射调用Controller方法
Object result = handlerMethod.getMethod().invoke(
handlerMethod.getBean(), args);
System.out.println("方法返回值:" + result);
// 5. 处理返回值
if (handlerMethod.getMethod().isAnnotationPresent(ResponseBody.class)) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(result.toString());
} else {
response.getWriter().write(result.toString());
}
} catch (Exception e) {
e.printStackTrace();
response.setStatus(500);
response.getWriter().write("500 Internal Server Error: " + e.getMessage());
}
}
/**
* 查找Handler
*/
private HandlerMapping getHandler(String url) {
for (HandlerMapping mapping : handlerMappings) {
Matcher matcher = mapping.getPattern().matcher(url);
if (matcher.matches()) {
return mapping;
}
}
return null;
}
/**
* 解析方法参数
*/
private Object[] parseMethodArguments(HttpServletRequest request,
HandlerMapping handlerMapping, String url) {
HandlerMethod handlerMethod = handlerMapping.getHandlerMethod();
Method method = handlerMethod.getMethod();
Parameter[] parameters = method.getParameters();
Object[] args = new Object[parameters.length];
// 提取路径变量
Matcher matcher = handlerMapping.getPattern().matcher(url);
matcher.matches();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
// 处理@PathVariable
if (parameter.isAnnotationPresent(PathVariable.class)) {
PathVariable pathVariable = parameter.getAnnotation(PathVariable.class);
String paramName = pathVariable.value();
// 从URL中提取值
try {
String value = matcher.group(paramName);
args[i] = convert(value, parameter.getType());
System.out.println(" 参数[" + paramName + "] = " + value);
} catch (Exception e) {
args[i] = null;
}
}
// 可以继续处理@RequestParam等
}
return args;
}
/**
* 类型转换
*/
private Object convert(String value, Class<?> type) {
if (type == String.class) {
return value;
} else if (type == Integer.class || type == int.class) {
return Integer.parseInt(value);
} else if (type == Long.class || type == long.class) {
return Long.parseLong(value);
}
return value;
}
/**
* 扫描包
*/
private void scanPackage(String basePackage) {
try {
String packagePath = basePackage.replace(".", "/");
URL url = this.getClass().getClassLoader().getResource(packagePath);
if (url != null) {
File dir = new File(url.getFile());
scanClasses(dir, basePackage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 扫描类
*/
private void scanClasses(File dir, String packageName) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
scanClasses(file, packageName + "." + file.getName());
} else if (file.getName().endsWith(".class")) {
String className = packageName + "." +
file.getName().replace(".class", "");
registerHandler(className);
}
}
}
}
}
/**
* 注册Handler
*/
private void registerHandler(String className) {
try {
Class<?> clazz = Class.forName(className);
// 检查是否有@Controller注解
if (!clazz.isAnnotationPresent(Controller.class)) {
return;
}
Object instance = clazz.newInstance();
// 获取类级别的@RequestMapping
String baseUrl = "";
if (clazz.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
baseUrl = requestMapping.value();
}
// 遍历所有方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping =
method.getAnnotation(RequestMapping.class);
String url = baseUrl + requestMapping.value();
// 构建正则表达式(支持{id}这种路径变量)
String regex = url.replaceAll("\\{(\\w+)\\}", "(?<$1>\\\\w+)");
Pattern pattern = Pattern.compile(regex);
// 获取参数名称
Parameter[] parameters = method.getParameters();
String[] paramNames = new String[parameters.length];
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].isAnnotationPresent(PathVariable.class)) {
PathVariable pathVariable =
parameters[i].getAnnotation(PathVariable.class);
paramNames[i] = pathVariable.value();
}
}
HandlerMethod handlerMethod = new HandlerMethod(instance, method, paramNames);
handlerMappings.add(new HandlerMapping(pattern, handlerMethod));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* HandlerMapping内部类
*/
private static class HandlerMapping {
private Pattern pattern;
private HandlerMethod handlerMethod;
public HandlerMapping(Pattern pattern, HandlerMethod handlerMethod) {
this.pattern = pattern;
this.handlerMethod = handlerMethod;
}
public Pattern getPattern() {
return pattern;
}
public HandlerMethod getHandlerMethod() {
return handlerMethod;
}
}
}
4. 测试Controller
package com.example.controller;
import com.example.mvc.*;
/**
* 测试Controller
*/
@Controller
@RequestMapping("/api")
public class UserController {
@RequestMapping("/user/{id}")
@ResponseBody
public String getUser(@PathVariable("id") Long id) {
return "{\"id\":" + id + ",\"name\":\"User-" + id + "\"}";
}
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "Hello, Simple MVC!";
}
}
5. web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<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_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>simpleDispatcherServlet</servlet-name>
<servlet-class>com.example.mvc.SimpleDispatcherServlet</servlet-class>
<init-param>
<param-name>basePackage</param-name>
<param-value>com.example</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>simpleDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
6. 测试效果
启动服务器,访问:
http://localhost:8080/api/hello
输出:Hello, Simple MVC!
http://localhost:8080/api/user/123
输出:{"id":123,"name":"User-123"}
控制台输出:
===== SimpleDispatcherServlet初始化 =====
扫描到 2 个Handler
/api/hello -> hello
/api/user/(?<id>\w+) -> getUser
===== 处理请求:/api/user/123 =====
找到Handler:getUser
参数[id] = 123
方法返回值:{"id":123,"name":"User-123"}
成功! ✨
🎯 第五问:实战案例 - RESTful API
完整的User CRUD
package com.example.controller;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户Controller
*/
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
/**
* 获取所有用户
*/
@GetMapping
public List<User> listUsers() {
return userService.findAll();
}
/**
* 根据ID获取用户
*/
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
/**
* 创建用户
*/
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.save(user);
return ResponseEntity.status(201).body(created);
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id,
@RequestBody User user) {
user.setId(id);
User updated = userService.update(user);
if (updated == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(updated);
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean deleted = userService.delete(id);
if (!deleted) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
/**
* 搜索用户
*/
@GetMapping("/search")
public List<User> searchUsers(@RequestParam String keyword) {
return userService.search(keyword);
}
}
拦截器示例
package com.example.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 日志拦截器
*/
public class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
System.out.println(">>> 请求开始:" + request.getMethod() + " " +
request.getRequestURI());
return true; // 返回true继续执行,false中断请求
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println(">>> Handler执行完成");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println(">>> 请求完成,耗时:" + (endTime - startTime) + "ms\n");
}
}
配置拦截器:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/api/**") // 拦截所有/api/**路径
.excludePathPatterns("/api/public/**"); // 排除公开接口
}
}
异常处理
package com.example.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理用户未找到异常
*/
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
ErrorResponse error = new ErrorResponse(404, ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
/**
* 处理参数校验异常
*/
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException ex) {
ErrorResponse error = new ErrorResponse(400, ex.getMessage());
return ResponseEntity.badRequest().body(error);
}
/**
* 处理所有其他异常
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception ex) {
ex.printStackTrace();
ErrorResponse error = new ErrorResponse(500, "服务器内部错误");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
/**
* 错误响应
*/
class ErrorResponse {
private int code;
private String message;
public ErrorResponse(int code, String message) {
this.code = code;
this.message = message;
}
// Getter和Setter
}
💡 最佳实践与优化建议
1. Controller设计规范
// ✅ 推荐:RESTful风格
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping // GET /api/users 查询列表
@GetMapping("/{id}") // GET /api/users/1 查询单个
@PostMapping // POST /api/users 创建
@PutMapping("/{id}") // PUT /api/users/1 更新
@DeleteMapping("/{id}") // DELETE /api/users/1 删除
}
// ❌ 不推荐:动词式URL
@GetMapping("/getUserById")
@PostMapping("/createUser")
@PostMapping("/updateUser")
2. 参数校验
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
// @Valid触发JSR-303校验
return ResponseEntity.status(201).body(userService.save(user));
}
// User实体
public class User {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 20, message = "用户名长度必须在3-20之间")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 18, message = "年龄必须大于18岁")
private Integer age;
}
3. 统一响应格式
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
public static <T> Result<T> error(int code, String message) {
return new Result<>(code, message, null);
}
}
// 使用
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return Result.success(user);
}
4. 异步请求处理
@GetMapping("/async")
public DeferredResult<String> asyncRequest() {
DeferredResult<String> result = new DeferredResult<>(5000L);
// 异步处理
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(2000);
result.setResult("异步处理完成");
} catch (Exception e) {
result.setErrorResult("处理失败");
}
});
return result;
}
5. 跨域配置
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
};
}
}
📊 知识点总结
SpringMVC核心要点
✅ DispatcherServlet
- 前端控制器,整个流程的核心
- 负责请求分发和协调各组件
✅ 9大组件
- HandlerMapping - URL映射
- HandlerAdapter - Handler适配
- ViewResolver - 视图解析
- HandlerExceptionResolver - 异常处理
- 其他5个组件
✅ 请求处理9步骤
- 接收请求
- 查找Handler
- 拦截器preHandle
- 调用Handler
- 执行业务
- 拦截器postHandle
- 解析视图
- 渲染视图
- 拦截器afterCompletion
✅ 核心源码
- doDispatch() - 请求分发
- getHandler() - 查找Handler
- handle() - 执行Handler
- render() - 渲染视图
记忆口诀
请求到达Dispatcher,
HandlerMapping来查找。
拦截器前置先拦截,
Adapter适配来执行。
业务逻辑Controller,
返回结果ModelView。
拦截器后置再处理,
ViewResolver来解析。
渲染视图返回客户,
最后完成来清理。
🤔 常见问题
Q1: 为什么需要HandlerAdapter?
A: 因为SpringMVC支持多种Handler类型,需要适配器统一处理。
// 类型1:注解式
@GetMapping("/user")
public String getUser() { }
// 类型2:实现接口
public class UserController implements Controller {
public ModelAndView handleRequest(...) { }
}
// 不同类型调用方式不同,HandlerAdapter就是适配器模式的应用
Q2: 拦截器和过滤器的区别?
| 维度 | 拦截器(Interceptor) | 过滤器(Filter) |
|---|---|---|
| 实现 | Spring框架 | Servlet规范 |
| 拦截范围 | Controller方法 | 所有请求 |
| 执行时机 | DispatcherServlet之后 | DispatcherServlet之前 |
| 访问Spring容器 | ✅ 可以 | ❌ 不可以 |
| 使用场景 | 权限校验、日志 | 编码、CORS |
Q3: @Controller和@RestController的区别?
// @Controller:传统MVC,返回视图
@Controller
public class UserController {
@GetMapping("/user")
public String getUser(Model model) {
return "user/detail"; // 返回视图名
}
}
// @RestController:RESTful API,返回数据
@RestController
public class UserApiController {
@GetMapping("/api/user")
public User getUser() {
return new User(); // 直接返回JSON
}
}
// @RestController = @Controller + @ResponseBody
💬 写在最后
从DispatcherServlet到Controller,我们深入剖析了SpringMVC的完整请求处理流程:
- 🔍 理解了9大组件的作用
- 📊 掌握了9步骤处理流程
- 💻 手写了简易版DispatcherServlet
- 🎯 学习了实战最佳实践
这篇万字长文,希望能让你彻底搞懂SpringMVC的核心原理!
如果这篇文章对你有帮助,请:
- 👍 点赞支持
- ⭐ 收藏备用
- 🔄 转发分享
- 💬 评论交流
感谢阅读,期待下次再见! 👋