1. 总控台登场:DispatcherServlet的运作原理

如果把Spring MVC比作剧场表演,DispatcherServlet就是那个掌控全场的总导演。它驻守在Web容器入口,所有HTTP请求都要通过它的调度才能找到对应演员(Controller)。

在典型的Spring MVC项目中,web.xml配置着这个核心调度员:

<!-- 配置Spring MVC请求总控 -->
<servlet>
    <servlet-name>springMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-mvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- 映射所有以.do结尾的请求 -->
<servlet-mapping>
    <servlet-name>springMVC</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

启动时依次执行:

  1. 读取初始化参数定位配置文件
  2. 创建WebApplicationContext(父子容器隔离业务层)
  3. 加载HandlerMapping、ViewResolver等基础设施
  4. 注册默认策略实现(当用户未配置时启用)

2. 八步走完请求流程:深度剖析处理链路

当用户请求/user/list.do时:

// 模拟核心流程的伪代码
public void doDispatch(HttpServletRequest request, HttpServletResponse response) {
    // Step1:通过HandlerMapping路由匹配
    HandlerExecutionChain mappedHandler = getHandler(request);
    
    // Step2:适配到具体处理方法(支持多种Handler)
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    // Step3:执行拦截器preHandle()
    if (!mappedHandler.applyPreHandle(request, response)) return;
    
    // Step4:实际调用Controller方法
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
    
    // Step5:应用默认视图名(如果方法返回字符串)
    applyDefaultViewName(request, mv);
    
    // Step6:执行拦截器postHandle()
    mappedHandler.applyPostHandle(request, response, mv);
    
    // Step7:视图解析和渲染
    processDispatchResult(request, response, mv, dispatchException);
    
    // Step8:完成后的收尾处理
    mappedHandler.triggerAfterCompletion(request, response, null);
}

真实项目中的用户列表控制器示例:

@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    // 用户列表查询
    @GetMapping("/list.do")
    public String listUsers(Model model, 
                           @RequestParam(defaultValue = "1") int pageNo) {
        // 查询数据库获取分页数据
        Page<User> userPage = userService.getUsersByPage(pageNo, 10);
        
        // 将数据存入模型
        model.addAttribute("userList", userPage.getContent());
        model.addAttribute("totalPages", userPage.getTotalPages());
        
        // 返回视图逻辑名
        return "user/list";
    }
    
    // 用户注册表单处理
    @PostMapping("/register.do")
    public ModelAndView registerUser(@Valid UserForm form, BindingResult result) {
        ModelAndView mav = new ModelAndView();
        
        if (result.hasErrors()) {
            mav.setViewName("user/register");
            mav.addObject("errors", result.getAllErrors());
            return mav;
        }
        
        userService.createUser(form);
        mav.setViewName("redirect:/user/success.do");
        return mav;
    }
}

3. 黄金搭档:核心组件深度协同

3.1 路由管家 HandlerMapping

提供四种实现策略:

  • BeanNameUrlHandlerMapping(基于Bean名称)
  • RequestMappingHandlerMapping(注解驱动)
  • RouterFunctionMapping(函数式编程)
  • SimpleUrlHandlerMapping(XML配置)

配置示例:

<!-- 显式配置处理器映射策略 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
    <property name="order" value="0"/>
</bean>

3.2 视图魔术师 ViewResolver

常见视图解析方案:

# 内部资源视图解析器
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# 启用多个视图解析器链
<bean class="org.springframework.web.servlet.view.XmlViewResolver"/>
<bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"/>

4. 应用场景全景扫描

4.1 典型适用场景

  • RESTful API开发(配合@RestController)
  • 传统表单驱动Web应用
  • 前后端分离项目中的路由控制
  • 微服务网关的请求转发

4.2 黄金搭档技术选型

| 技术组合         | 适用场景                   |
|------------------|--------------------------|
| Thymeleaf模板引擎 | 传统JSP替代方案           | 
| Jackson JSON库   | RESTful API数据序列化     |
| Hibernate Validator | 数据校验             |
| Spring Security  | 安全认证控制              |

5. 优劣辩证看:技术选型指南

优势亮点

  • 模块解耦:各组件职责单一明确
  • 扩展灵活:支持自定义处理器和视图
  • 生态强大:与Spring全家桶无缝集成
  • 注解驱动:简化配置提升开发效率

潜在挑战

  • 学习曲线:需理解MVC设计模式
  • 配置复杂:多个配置文件的协调
  • 性能损耗:相较于直接Servlet开发略有损耗

6. 避坑指南:经验总结

  1. 路由陷阱
<!-- 错误的URL映射模式 -->
<url-pattern>/</url-pattern> <!-- 可能导致静态资源被拦截 -->

<!-- 推荐配置方案 -->
<url-pattern>/api/*</url-pattern>
<url-pattern>*.do</url-pattern>
  1. 并发安全
// 错误示范:在Controller中定义实例变量
@Controller
public class UnsafeController {
    private int counter = 0; // 存在线程安全问题
    
    @GetMapping("/count")
    @ResponseBody
    public String count() {
        return "Current count: " + (++counter);
    }
}
  1. 调试技巧: 启用详细日志配置:
logging.level.org.springframework.web.servlet=DEBUG
logging.level.org.springframework.web=TRACE

7. 未来演进方向

随着Spring Boot的普及,配置方式趋向自动化:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoggingInterceptor());
    }
}