1. 引言
很多初学者在接触 Spring MVC 时,往往只会复制粘贴代码:照着教程写一个@Controller、加一个@RequestMapping,请求就能通,但一旦遇到 404、参数绑定失败、拦截器不生效等问题就无从下手。究其原因,是缺少 Servlet 底层基础,不理解 Spring MVC 的设计由来。
本文的核心目标,就是补齐 Servlet 底层基础,让你理解 Spring MVC 为什么这样设计,避免后续学习只会复制粘贴代码。我们将用三天时间,从 Servlet 核心规范出发,逐步过渡到 MVC 架构模式,最终搭建起 Spring MVC 的基础开发环境,跑通第一个纯注解驱动的入门案例。
2. 学习路线总览
本阶段的学习分为三天,每天都有明确的目标与核心知识点:
- Day1:吃透 Servlet 核心规范 —— 掌握 Servlet 生命周期、Tomcat 容器运行原理,手写原生 Servlet 实现简单请求处理,直观感受传统 Servlet 开发的代码冗余痛点
- Day2:深入理解 MVC 架构模式 —— 理清 Model、View、Controller 三层的职责划分,对比三层架构与 MVC 的协作关系,掌握 MVC 架构解耦、易维护的核心优势
- Day3:搭建 Spring MVC 基础开发环境 —— 导入核心 Maven 依赖,完成第一个纯注解驱动的 Spring MVC 入门案例,验证请求响应全链路通跑
整体路线遵循「先底层、后框架」的原则:先用手写 Servlet 感受传统开发的痛点,再理解 MVC 如何解决这些痛点,最后用 Spring MVC 验证全链路,从而真正理解框架的设计由来。
3. Day1:吃透 Servlet 核心规范
3.1 Servlet 生命周期
Servlet 的生命周期由容器(Tomcat)管理,主要分为三个阶段:
- init:Servlet 被创建后调用一次,用于初始化资源,如加载配置、建立数据库连接
- service:每次请求到来时调用,负责处理请求并生成响应,是核心业务逻辑的入口
- destroy:应用卸载或容器关闭时调用一次,用于释放资源
理解生命周期,是理解容器如何管理组件的基础,也是后续理解 Spring 容器管理 Bean 生命周期的铺垫。
3.2 Tomcat 容器运行原理
Tomcat 作为 Servlet 容器,负责接收 HTTP 请求并分发到对应的 Servlet。其核心流程为:
- 客户端发起 HTTP 请求,Tomcat 的 Connector 监听端口并接收请求
- 请求被封装为
HttpServletRequest与HttpServletResponse对象 - Tomcat 根据 URL 映射找到对应的 Servlet,调用其
service方法 - Servlet 处理完业务后,将结果写入响应对象,Tomcat 将其返回给客户端
理解这一流程,就能明白请求是如何被容器接收并分发的,这也是 Spring MVC 中DispatcherServlet的前身。
3.3 手写原生 Servlet:感受代码冗余痛点
下面手写一个原生 Servlet,实现简单的请求处理:
@WebServlet("/hello")publicclassHelloServletextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{// 手动处理请求参数Stringname=req.getParameter("name");if(name==null){name="World";}// 手动拼接 HTML 响应resp.setContentType("text/html;charset=UTF-8");PrintWriterout=resp.getWriter();out.println("<html><body>");out.println("<h1>Hello, "+name+"!</h1>");out.println("</body></html>");}}这段代码虽然能跑通,但暴露了传统 Servlet 开发的几个痛点:
- 参数绑定繁琐:每次都要手动调用
getParameter并做类型转换 - 响应拼接原始:HTML 需要手动拼接字符串,难以维护
- 逻辑耦合严重:请求处理、业务逻辑、视图渲染全部堆在一个方法里
- 配置分散:URL 映射、初始化参数等散落在注解与配置文件中
正是这些痛点,催生了 MVC 架构模式与 Spring MVC 框架。
4. Day2:深入理解 MVC 架构模式
4.1 Model、View、Controller 三层职责划分
MVC 将应用程序划分为三个核心部分:
- Model(模型):负责业务逻辑与数据访问,封装数据与状态,不关心如何展示
- View(视图):负责展示数据,将 Model 中的数据渲染给用户,如 JSP、Thymeleaf 模板
- Controller(控制器):负责接收请求、调用 Model 处理业务、选择 View 渲染结果,是请求的调度中枢
三层各司其职,通过明确的接口协作,从而降低耦合、提升可维护性。
4.2 三层架构与 MVC 的协作关系
三层架构(表现层、业务层、持久层)与 MVC 并不冲突,而是互补协作:
- Controller 对应表现层:负责接收请求、参数校验、调用业务层
- Service 对应业务层:承载核心业务逻辑,对应 MVC 中的 Model 一部分
- DAO/Repository 对应持久层:负责数据访问,也是 Model 的一部分
在实际项目中,MVC 关注的是「请求如何被接收、处理、渲染」的流程,而三层架构关注的是「业务如何分层、数据如何访问」的结构,两者结合形成完整的应用骨架。
下面用一张图直观展示三层架构与 MVC 的协作关系:
从图中可以看出:Controller 对应表现层,Service 对应业务层,DAO/Repository 对应持久层;而 Model 贯穿业务层与持久层,View 负责最终渲染,两者通过 Controller 串联起完整的请求处理链路。
4.3 MVC 架构解耦、易维护的核心优势
相比传统 Servlet 的「大杂烩」写法,MVC 带来了显著优势:
- 解耦:视图与业务逻辑分离,修改页面样式不影响业务代码
- 易维护:每层职责单一,定位问题只需关注对应层
- 可测试:Model 与 Controller 可以脱离 View 独立单元测试
- 可复用:同一 Model 可以被多个 View 复用,同一 Controller 可以调用不同 Service
理解了这些优势,就能明白 Spring MVC 为什么采用 DispatcherServlet 作为统一入口,并通过注解驱动来简化开发。
5. Day3:搭建 Spring MVC 基础开发环境
5.1 导入核心 Maven 依赖
创建一个 Maven 工程,在pom.xml中导入 Spring MVC 核心依赖:
<dependencies><!-- Spring MVC 核心 --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.30</version></dependency><!-- Servlet API(Tomcat 提供,编译期需要) --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope></dependency></dependencies>5.2 完成第一个纯注解驱动的 Spring MVC 入门案例
首先编写一个 Controller,使用纯注解声明请求映射:
@ControllerpublicclassHelloController{@RequestMapping("/hello")@ResponseBodypublicStringhello(@RequestParam(value="name",defaultValue="World")Stringname){return"Hello, "+name+"!";}}接着编写配置类,启用注解驱动并配置组件扫描:
@Configuration@EnableWebMvc@ComponentScan("com.example.controller")publicclassWebConfigimplementsWebMvcConfigurer{// 可在此配置视图解析器、静态资源等}最后在web.xml或通过AbstractAnnotationConfigDispatcherServletInitializer注册 DispatcherServlet:
publicclassWebInitializerextendsAbstractAnnotationConfigDispatcherServletInitializer{@OverrideprotectedClass<?>[]getRootConfigClasses(){returnnewClass<?>[0];}@OverrideprotectedClass<?>[]getServletConfigClasses(){returnnewClass<?>[]{WebConfig.class};}@OverrideprotectedString[]getServletMappings(){returnnewString[]{"/"};}}5.3 验证请求响应全链路通跑
启动 Tomcat 后,访问http://localhost:8080/hello?name=Spring,应返回Hello, Spring!。此时对比 Day1 的手写 Servlet,你会发现:
- 参数绑定由
@RequestParam自动完成,无需手动getParameter - 响应由
@ResponseBody自动序列化,无需手动拼接 HTML - 请求分发由 DispatcherServlet 统一调度,无需手动维护 Servlet 映射
这正是 Spring MVC 在 Servlet 基础上做的封装与升华——理解了 Servlet,才能真正理解 Spring MVC 的设计由来。
6. 总结
通过三天的学习,我们从手写 Servlet 感受传统开发的痛点,到理解 MVC 架构如何解耦,再到用 Spring MVC 跑通全链路,完成了从底层到框架的认知升级。核心收获是:Spring MVC 不是凭空出现的框架,而是对 Servlet 的合理封装与演进。只有吃透 Servlet 底层,才能在后续学习中知其然更知其所以然,避免只会复制粘贴代码。