在Java Web开发领域,SpringMVC作为主流的MVC框架,其核心价值在于实现请求处理与视图渲染的解耦,让开发更具规范性和可维护性。想要熟练运用SpringMVC,首先要理清它的请求执行全流程——从用户发起请求到最终获得响应,每一步都有明确的组件分工,而前端控制器则是整个流程的“中枢神经”。本文将一步步拆解SpringMVC的标准执行流程,结合实战代码示例,让每一个环节的作用都清晰可见。
一、核心组件先了解
在拆解流程前,先明确参与流程的核心组件及其核心职责,这是理解整个流程的基础,无需额外拓展,聚焦核心功能即可:
- DispatcherServlet:前端控制器,SpringMVC的入口,统一接收所有请求,协调其他所有组件完成请求处理和响应。
- HandlerMapping:处理器映射器,根据请求的URL和配置,找到对应的处理器(Controller方法),并返回包含处理器和拦截器的执行链。
- HandlerAdapter:处理器适配器,适配不同类型的处理器,负责解析请求参数、调用具体的处理器方法。
- Controller:处理器,核心业务逻辑的载体,接收请求、处理业务,最终返回处理结果和视图信息。
- ViewResolver:视图解析器,将Controller返回的逻辑视图名,解析为实际的视图对象(如JSP、HTML)。
- View:视图,负责将处理结果的数据渲染为最终的页面,响应给用户。
二、SpringMVC执行流程全拆解(10步走)
下面结合实战代码,一步步拆解SpringMVC从请求发起至响应结束的完整流程,每一步都对应实际开发中的具体场景,让流程更易理解。
步骤1:用户发送HTTP请求至DispatcherServlet
用户通过浏览器、Postman等客户端,发送HTTP请求(如GET请求 /student/query?id=1),该请求会首先被Web容器(如Tomcat)拦截,然后转发给SpringMVC的前端控制器DispatcherServlet。
DispatcherServlet需要在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_4_0.xsd"
version="4.0">
<!-- 配置SpringMVC前端控制器DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置SpringMVC配置文件路径 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 启动时加载DispatcherServlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置请求映射,所有请求都交给DispatcherServlet处理 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
步骤2:DispatcherServlet调用HandlerMapping查找处理器
DispatcherServlet本身不直接处理请求,而是接收请求后,委托HandlerMapping根据请求的URL,查找对应的处理器(即Controller中的具体方法)。
实际开发中,我们常用注解式开发,通过@RequestMapping注解指定请求路径与处理器方法的映射,此时使用的HandlerMapping实现类是RequestMappingHandlerMapping(SpringMVC默认加载)。我们只需在SpringMVC配置文件中开启注解驱动即可,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描Controller所在的包,让Spring管理 -->
<context:component-scan base-package="com.example.springmvc.controller"/>
<!-- 开启SpringMVC注解驱动,自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter -->
<mvc:annotation-driven/>
<!-- 配置视图解析器ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置视图前缀,指定视图文件所在目录 -->
<property name="prefix" value="/WEB-INF/views/"/>
<!-- 配置视图后缀,指定视图文件格式 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
步骤3:HandlerMapping返回处理器执行链
HandlerMapping根据请求URL匹配到对应的处理器方法后,不会只返回该方法,而是返回一个处理器执行链(HandlerExecutionChain),该执行链包含匹配的处理器(Controller方法)和对应的拦截器(如果有),然后将执行链返回给DispatcherServlet。
这里我们编写一个简单的拦截器示例,演示拦截器如何加入执行链,拦截器代码如下:
package com.example.springmvc.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 自定义拦截器,实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
// 请求处理前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器:请求处理前执行,验证请求合法性");
// 返回true表示放行,false表示拦截
return true;
}
}
在SpringMVC配置文件中配置拦截器,使其生效:
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor><!-- 配置拦截的请求路径,/**表示拦截所有请求 -->
<mvc:mapping path="/**"/>
<!-- 配置不拦截的请求路径(可选) -->
<mvc:exclude-mapping path="/static/**"/>
<!-- 关联自定义拦截器 -->
<bean class="com.example.springmvc.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
步骤4:DispatcherServlet调用HandlerAdapter适配处理器
DispatcherServlet接收处理器执行链后,发现无法直接调用处理器方法(因为处理器的形式多样,如注解式、XML配置式),此时需要通过HandlerAdapter进行适配。
结合前面的注解驱动配置,SpringMVC会自动加载RequestMappingHandlerAdapter,该适配器专门适配带有@RequestMapping注解的处理器方法,负责解析请求参数、将参数绑定到处理器方法的形参上。
步骤5:HandlerAdapter调用具体处理器(Controller)
HandlerAdapter完成适配后,会调用具体的处理器方法(Controller中的方法),执行核心业务逻辑。这里我们编写一个简单的Controller,接收请求参数、处理业务,并返回ModelAndView对象,代码如下:
package com.example.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
// 标识该类为Controller处理器
@Controller
@RequestMapping("/student")
public class StudentController {
// 匹配请求路径 /student/query,接收id参数
@RequestMapping("/query")
public ModelAndView queryStudent(@RequestParam("id") Integer studentId) {
// 模拟业务逻辑:根据id查询学生信息(实际开发中会调用Service层)
String studentName = "张三";
Integer age = 20;
// 创建ModelAndView对象,封装视图和数据
ModelAndView modelAndView = new ModelAndView();
// 存入数据到Model(键值对形式,前端可通过EL表达式获取)
modelAndView.addObject("studentId", studentId);
modelAndView.addObject("studentName", studentName);
modelAndView.addObject("age", age);
// 设置逻辑视图名(视图解析器会根据前缀和后缀解析为实际视图)
modelAndView.setViewName("studentInfo");
return modelAndView;
}
}
步骤6:Controller执行并返回ModelAndView
Controller方法执行完成后,会返回一个ModelAndView对象,该对象包含两部分核心信息:Model(处理结果的数据,如查询到的学生信息)和View(逻辑视图名,如上面的“studentInfo”,并非实际的视图文件)。
需要注意的是,Model中的数据会被存入request域中,供前端视图渲染时使用;逻辑视图名则用于后续视图解析器的解析。
步骤7:HandlerAdapter将ModelAndView返回给DispatcherServlet
HandlerAdapter调用完Controller方法后,会将返回的ModelAndView对象,重新传递回DispatcherServlet,由DispatcherServlet继续后续的视图解析和渲染流程。
步骤8:DispatcherServlet委托ViewResolver解析视图
DispatcherServlet接收ModelAndView后,会委托ViewResolver(视图解析器),将逻辑视图名解析为实际的视图对象。结合前面的SpringMVC配置,我们使用的ViewResolver实现类是InternalResourceViewResolver,它会根据配置的前缀(/WEB-INF/views/)和后缀(.jsp),将逻辑视图名“studentInfo”解析为实际的视图文件路径:/WEB-INF/views/studentInfo.jsp。
步骤9:ViewResolver返回具体View给DispatcherServlet
ViewResolver完成视图解析后,会生成一个具体的View对象(这里是JSP视图对象),并将该View对象返回给DispatcherServlet。
我们编写对应的JSP视图文件(studentInfo.jsp),用于渲染Model中的数据,代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>学生信息</title>
</head>
<body>
<h3>学生信息查询结果</h3>
<div>
<p>学生ID:${studentId}</p>
<p>学生姓名:${studentName}</p>
<p>学生年龄:${age}</p>
</div>
</body>
</html>
步骤10:DispatcherServlet渲染视图并响应用户
DispatcherServlet接收View对象后,会调用View对象的render()方法,将Model中的数据填充到视图中(如JSP页面中的EL表达式{studentName},会被Model中的对应数据替换),生成最终的HTML页面。
最后,DispatcherServlet将渲染完成的HTML页面,通过HTTP响应返回给用户,用户在浏览器中就能看到完整的学生信息页面,整个执行流程结束。
三、流程核心总结
整个SpringMVC执行流程中,DispatcherServlet始终处于核心协调地位,串联起HandlerMapping、HandlerAdapter、ViewResolver等所有组件,实现了“请求接收-业务处理-视图渲染-响应返回”的完整闭环。
组件之间的分工明确,互不耦合:HandlerMapping负责“找处理器”,HandlerAdapter负责“调处理器”,ViewResolver负责“解析视图”,Controller负责“处理业务”,这种分工协作让SpringMVC具备了良好的灵活性和可维护性,也是其成为主流Java Web框架的核心原因。