1. 前置知识
1.1 Tomcat
**定义:**Tomcat 是一个开源的轻量级 Web(Http) 服务器和 Servlet 容器。它实现了 Java Servlet 等 Java EE 规范的核心功能,常用于部署和运行 Java Web 应用程序。换言之,Tomcat 就是一个严格遵循 Servlet 规范开发出来的、可以独立安装和运行的 Java Web 服务器/Servlet 容器。 核心功能: Servlet 容器:支持 Servlet 的执行,处理 HTTP 请求和响应 Web 服务器:提供静态资源 (如 HTML) 的访问能力,支持基本的 HTTP 服务 目录结构: bin: 存放可执行文件,如 startup.bat conf: 存放配置文件 lib: 存放 Tomcat 运行所需的 jar 文件 logs: 存储日志文件 temp: 存放临时文件,如上传的文件或缓存数据 webapps: 默认 web 应用部署目录 work: 服务器的工作目录,存放运行时生成的临时文件 (编译文件)
1.2 Servlet
1.2.1 定义
Servlet 是 Java 语言编写的、运行在服务器端的程序,它遵循一套标准的 API 规范 (Tomcat 是这套规范的一个具体实现/容器,并提供了让 Servlet 与前端交互的运行时环境)
1.2.2 API 示范
创建项目/配置文件:
- 在 IDEA 中创建 Maven 项目
- 在 pom.xml 文件中添加 servlet 依赖 (置于
<project></project>标签下)
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<!-- servlet 依赖版本应与 jdk 和 tomcat 的版本相匹配 -->
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
- 在 main 路径下创建 webapp/Web-INF/web.xml,在 xml 文件中添加以下内容
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
API 示例:
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
// 设置访问路径 (url)
@WebServlet("/method")
// 继承 HttpServlet 并重写 doGet 和 doPost 方法
public class MethodServlet extends HttpServlet {
// 接收 method=post 的请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("doPost");
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("doPost");
}
// 接收 method=put 的请求
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("doPut");
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("doPut");
}
// 接收 method=delete 的请求
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("doDelete");
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("doDelete");
}
}
1.2.3 生命周期
定义:Servlet 生命周期由 Web 容器 (如 Tomcat) 管理,包含加载、初始化、处理请求和销毁四个阶段。每个阶段对应特定的方法调用,开发者可通过重写这些方法实现自定义逻辑。 **1. 类加载:**Web 容器通过类加载器加载 Servlet 类 (通常首次请求触发或容器启动时预加载,字节码文件被加载到内存但未实例化)。 **1.5 实例化:**确认 Servlet 类成功加载后立刻执行,在整个 Web 容器中每种 Servlet 类 (如 HttpServlet) 只会有一个实例化对象。 **2. 初始化:**Web 容器调用刚刚创建好的 Servlet 实例的
init(ServletConfig config)方法,在整个 servlet 实例的生命周期中仅调用一次,主要作用是读取配置和资源加载。若初始化失败,抛出ServletException,Servlet 不会被加入可用队列。 **3. 处理请求:**Web 容器为每个请求创建线程,调用service(ServletRequest req, ServletResponse res)方法。service() 方法根据 HTTP 请求类型 (get/post) 调用 doGet() 或 doPost()。 **4. 销毁:**Web 容器调用 destroy() 方法,Servlet 实例被标记为垃圾回收。
2. SpringBoot
Servlet 是 Java EE 规范中处理 Web 请求的核心组件,但随着应用复杂度提升,Servlet 的直接使用显得笨重。Spring 框架通过一系列抽象和扩展,简化了企业级应用开发。
我们可以用一个非常形象的比喻来贯穿始终:建造一座房子。 第一阶段:Servlet 时代 - 自己烧砖砌墙 **目标:**建造一个能遮风挡雨的房子 (一个能处理 HTTP 请求的 Web 应用) 你的工作状态: **材料:**你有最基础的原材料——泥土 (Java 语言) 和水 (JVM)。你需要自己烧制砖块 (编写 Servlet 类) **工具:**只有简单的泥瓦刀 (Servlet API) 过程:
- 你为每一面墙、每一扇门都亲手烧制一块特定的砖 (编写 LoginServlet, UserServlet, OrderServlet)
- 你亲自规划每块砖的位置 (在 web.xml 中配置大量的
<servlet>和<servlet-mapping>)- 你亲自搅拌水泥,一块一块地砌墙 (在每个 Servlet 的 doGet/doPost 方法中手动解析参数、处理业务、组装 HTML)
核心特点: **高度灵活:**你可以造出任何形状的砖 **极其繁琐:**大量重复性劳动 (每个 Servlet 都有获取参数、关闭连接等样板代码) **难以维护:**砖块之间紧密耦合 (对象依赖硬编码),想换一扇窗 (修改功能) 可能会牵动整面墙 **依赖外部:**房子建在别人的地上 (需要将 war 包部署到外部的 Tomcat 服务器)
**总结:**Servlet 提供了 Web 开发的基础能力,但开发效率极低,代码冗余且难以维护。
第二阶段:Spring 时代 - 使用预制件和设计图纸 **目标:**用更高效、更标准化的方式建造一个结构更好、更易扩展的房子 你的工作状态: **材料:**你不再烧砖,而是使用工厂提供的标准化预制件 (Spring Bean,如
@Controller,@Service) **核心创新:**你聘请了一位神奇的管家 (IoC 容器)。你不再亲自'砌砖'(用 new 实例化对象),只需告诉管家你需要什么 (用@Autowired声明依赖)。管家会自动把预制件 (Bean) 按照图纸 (配置) 组装好,送到你手上 (依赖注入 DI)。 过程:
- 一个总大门 (DispatcherServlet):房子只有一个入口,所有访客 (请求) 都先到这里
- 管家调度:总大门处的接待员 (DispatcherServlet) 根据访客需求,呼叫房子里对应的专业房间 (
@Controller中的方法) 来接待- 开发者只需专注于房间内的专业服务 (业务逻辑),而不用关心访客是怎么进来的
核心特点: **解耦:**预制件之间是松耦合的,易于更换和测试 **专业化:**AOP(面向切面编程) 可以像'装修队'一样,非侵入式地为所有房间统一安装中央空调(日志、安全、事务) **效率提升:**避免了大量重复劳动,结构清晰 **配置复杂:**绘制详细的'组装图纸'(配置 Spring) 本身成了一项复杂的工作
**总结:**Spring 框架通过 IoC/DI 和 AOP 等理念,解决了代码耦合和重复劳动问题,但引入了显著的配置复杂度。
Spring Boot 1.0.0正式发布于 2014 年 4 月 1 日,标志着该框架的首次稳定版本发布。SpringBoot 基于 Spring Framework 4 进行设计,显著减少了开发者的配置工作量,彻底消除了 Spring 的配置地狱。 **约定大于配置:**约定了默认配置 **Start 机制:**是一种依赖管理机制,每个 Starter 包含特定功能所需的依赖库和自动配置类,开发者只需引入对应 Starter 即可快速启用功能模块 **嵌入式容器:**内置了 Tomcat 等嵌入式容器,无需部署 war 文件到外部容器,直接运行即可启动应用
3. Spring Web MVC
3.1 概述
**官方描述:**Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,并从一开始就在 Spring 框架中。正式名称'Spring Web MVC',来自其源模块的名称 (spring-webmvc),但它通常被称为'Spring MVC'。
**MVC 的起源与发展:**MVC (Model-View-Controller) 模式最初由挪威计算机科学家 Trygve Reenskaug 于 1978 年在施乐帕克研究中心 (Xerox PARC) 提出,目的是为 Smalltalk 编程语言设计用户界面。其核心思想是将应用程序的逻辑分为三个独立组件: **Model:**处理数据逻辑和业务规则 **View:**负责数据展示和用户界面 **Controller:**接收用户输入并协调 Model 与 View 的交互
**Spring MVC 与 MVC 的关系:**Spring MVC 是 MVC 模式在 Spring 框架中的具体化,同时扩展了传统 MVC 的功能以适应现代 Web 开发需求。
3.2 必需工具
**Postman:**主要用于 API 的开发和测试。它提供了一个用户友好的界面,支持发送 HTTP 请求、管理请求历史、自动化测试以及团队协作。 **Fiddler:**是一个网络调试代理工具,主要用于监控和分析 HTTP/HTTPS 流量。它可以捕获设备与服务器之间的所有请求和响应,支持修改请求、重放请求以及性能分析。
3.3 RequestMapping
**作用:**是 Spring MVC 中最核心、最基础的注解之一,用于将 HTTP 请求映射到具体的方法上。 **注解级别:**类 + 方法 **作为类注解:**可以为整个类提供一个统一的 url 前缀 (可有可无) **作为方法注解:**指定该方法负责处理哪个 url 的请求 (强制要求)
@RequestMapping("/HelloController")
// @RestController 声明该类是一个 Spring MVC 控制器
@RestController
public class HelloController {
// 0. 不接收参数
// 作为 方法注解 时,@RequestMapping(value = "/hello", method = RequestMethod.GET) 等同于 @GetMapping(value = "/hello")
// 设置返回的响应是 json 格式,produces = "application/json"
@RequestMapping(value = "/hello", method = RequestMethod.GET, produces = "application/json")
public String hello() {
return "{\"Hello\" : World}";
}
// 1. 一个参数
@RequestMapping("/receiveAge1")
// 不传参或者传递的参数名不匹配时默认为 null
public String receiveAge1(Integer age) {
return "接收到参数 age:" + age;
}
@RequestMapping("/receiveAge2")
// 不传参或者传递的参数名不匹配时尝试设置为 null,但 int 无法被设置为 null,所以抛出 IllegalStateException
public String receiveAge2(int age) {
return "接收到参数 age:" + age;
}
// 2. 接收数组
@RequestMapping("/receiveArray")
public String receiveArray(String[] array) {
return "接收到参数 array:" + Arrays.toString(array);
}
// 3. 接收对象,需要保证传递的参数名称和数量与 Java 对象保持一致
@RequestMapping("/receivePerson")
public String receivePerson(Person person) {
return "接收到参数 person:" + person;
}
}
3.4 RequestBody
**作用:**将 HTTP 请求体中的 json 数据绑定到 Java 对象 (方法注解)。 **注解级别:**方法
@RequestMapping("/receivePerson")
// @RequestBody 接收 JSON 格式的数据
public String receivePerson(@RequestBody Person person) {
return "接收到参数 person:" + person;
}
3.5 RequestParam
作用:是 Spring MVC 框架中从 HTTP 请求中提取参数/查询字符串的注解,主要用于将请求参数绑定到控制器方法的参数上。 **注解级别:**方法
@RequestMapping("/receiveRename")
// @RequestParam 将 url 中 key=name 的查询字符串绑定到控制器的 userName 参数上
// required = false 设置该参数为非必传 (默认为 true,必传)
public String receiveRename(@RequestParam(value = "name", required = false) String userName) {
return "接收到参数 name:" + userName;
}
注意:需要接收多个同名参数时 (如 param=value1¶m=value2),直接绑定到 List 类型需通过该注解明确声明。 (1) 在 Spring MVC 中,参数绑定机制对集合类型和数组类型的处理存在差异。 (2) 使用 ArrayList 作为方法参数时,必须显式添加
@RequestParam注解,原因如下: **默认绑定规则:**Spring 默认将单个请求参数的值绑定到简单类型 (如 String、int) 或单个对象。对于集合类型,框架无法自动推断是否需要将多个同名参数合并为集合。 需要明确指示:@RequestParam注解会告知 Spring 将同名请求参数的值收集到一个集合中。 (3) 数组 (如 String[]) 无需@RequestParam注解即可正确接收,原因如下: **内置支持:**Spring 对数组类型有原生支持,能自动将多个同名请求参数值绑定到数组。这是框架的默认行为,无需额外配置。
@RequestMapping("/receiveList1")
public String receiveList1(ArrayList<String> list) {
// 返回的 list 为空
return "接收到参数 list:" + list;
}
@RequestMapping("/receiveList2")
public String receiveList2(@RequestParam(required = false) ArrayList<String> list) {
// 正确返回
return "接收到参数 list:" + list;
}
@RequestMapping("/receiveList3")
public String receiveList3(List<String> list) {
// 报错
return "接收到参数 list:" + list;
}
**后端报错:**java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.List。receiveList3 方法使用 List 接口类型而非具体实现类。Spring 虽然支持接口类型参数绑定,但需要满足特定条件:必须配合
@RequestParam注解使用,不能直接使用未注解的接口类型参数。 **报错根本原因:**Spring 尝试实例化 List 接口失败 (接口不可实例化)。
3.6 PathVariable
作用:用于从 URL 路径中提取变量值并绑定到方法的参数上。 **注解级别:**方法
@RequestMapping("/receivePath/{article}/{blog}")
// required = false 设置该参数为非必传 (默认为 true,必传)
public String receivePath(@PathVariable(value = "article", required = false) Integer title, @PathVariable(value = "blog", required = false) String content) {
return "接收到参数 article:" + title + " blog:" + content;
}
3.7 RequestPart
**作用:**用于处理 HTTP 请求中的 multipart/form-data 类型数据,通常用于文件上传或同时上传文件和其他表单字段的场景。 **注解级别:**方法
@RequestMapping("/receiveFile")
public String receiveFile(@RequestPart(value = "file", required = false) MultipartFile imgFile, @RequestParam(value = "userName", required = false) String name) {
// 返回原始的文件名
return "用户:" + name + ",接收到文件:" + imgFile.getOriginalFilename();
}
3.8 Controller&ResponseBody&RestController
Controller **作用:**是 Spring MVC 中的核心注解,用于标记一个类作为 Web 请求的处理器 (声明一个类是一个 Spring MVC 控制器),负责处理 HTTP 请求并返回视图。 **注解级别:**类
ResponseBody **作用:**指示方法返回值应直接写入 HTTP 响应体,而非通过视图解析器渲染。 **注解级别:**类 + 方法
@RequestMapping("/ControllerResponse")
@Controller
public class ControllerResponse {
// 返回视图
@RequestMapping("/HTMLView")
public String HTMLView() {
return "/show.html";
}
// 返回数据
@ResponseBody
@RequestMapping("/HTMLData")
public String HTMLData() {
return "/show.html";
}
}
RestController **作用:**是 Spring MVC 中的一个组合注解,它结合了
@Controller和@ResponseBody的功能,标记的类所有方法返回值默认直接作为 HTTP 响应体 (JSON/XML 等格式),无需额外视图渲染。 **注解级别:**类


