SpringMVC请求处理流程深度解析:从DispatcherServlet到Controller的完整链路!

难度:⭐⭐⭐⭐ | 适合人群:想深入理解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 = 交通指挥中心
  • 各种组件 = 交警、红绿灯、路标
  • 请求 = 车辆

职责:

  1. 接收所有HTTP请求
  2. 委托给各个组件处理
  3. 协调整个请求处理流程
  4. 返回响应

🔧 第二问: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,支持:

  1. ✅ URL映射到Controller方法
  2. ✅ 参数解析(@PathVariable、@RequestParam)
  3. ✅ 返回值处理(@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大组件

  1. HandlerMapping - URL映射
  2. HandlerAdapter - Handler适配
  3. ViewResolver - 视图解析
  4. HandlerExceptionResolver - 异常处理
  5. 其他5个组件

请求处理9步骤

  1. 接收请求
  2. 查找Handler
  3. 拦截器preHandle
  4. 调用Handler
  5. 执行业务
  6. 拦截器postHandle
  7. 解析视图
  8. 渲染视图
  9. 拦截器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的核心原理!

如果这篇文章对你有帮助,请:

  • 👍 点赞支持
  • ⭐ 收藏备用
  • 🔄 转发分享
  • 💬 评论交流

感谢阅读,期待下次再见! 👋