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 示范
创建项目时,通常需要在 IDE(如 IDEA)中配置 Maven 依赖。
(1) pom.xml 配置
<dependencies>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<!-- servlet 依赖版本应与 jdk 和 tomcat 的版本相匹配 -->
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
(2) web.xml 配置
在 main 路径下创建 webapp/Web-INF/web.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>
(3) Servlet 代码示例 这里演示如何继承 HttpServlet 并重写方法以处理不同 HTTP 请求:
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/method")
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)管理,包含加载、初始化、处理请求和销毁四个阶段。每个阶段对应特定的方法调用,开发者可通过重写这些方法实现自定义逻辑。
- 类加载:Web 容器通过类加载器加载 Servlet 类(通常首次请求触发或容器启动时预加载),字节码文件被加载到内存但未实例化。
- 实例化:确认 Servlet 类成功加载后立刻执行,在整个 Web 容器中每种 Servlet 类只会有一个实例化对象。
- 初始化:Web 容器调用刚刚创建好的 Servlet 实例的
init(ServletConfig config)方法,在整个 servlet 实例的生命周期中仅调用一次,主要作用是读取配置和资源加载。若初始化失败,抛出ServletException,Servlet 不会被加入可用队列。 - 处理请求:Web 容器为每个请求创建线程,调用
service(ServletRequest req, ServletResponse res)方法。service() 方法根据 HTTP 请求类型(get/post)调用 doGet() 或 doPost()。 - 销毁:Web 容器调用 destroy() 方法,Servlet 实例被标记为垃圾回收。
2. Spring Boot
Servlet 是 Java EE 规范中处理 Web 请求的核心组件,但随着应用复杂度提升,直接使用 Servlet 显得笨重。Spring 框架通过一系列抽象和扩展,简化了企业级应用开发。
我们可以用一个形象的比喻来理解演进过程:建造一座房子。
第一阶段:Servlet 时代 - 自己烧砖砌墙
- 目标:建造一个能遮风挡雨的房子(一个能处理 HTTP 请求的 Web 应用)。
- 工作状态:材料是泥土(Java 语言)和水(JVM)。你需要自己烧制砖块(编写 Servlet 类),只有简单的泥瓦刀(Servlet API)。
- 过程:为每一面墙、每一扇门都亲手烧制一块特定的砖;亲自规划每块砖的位置(在 web.xml 中配置大量的
<servlet>和<servlet-mapping>);在每个 Servlet 的 doGet/doPost 方法中手动解析参数、处理业务、组装 HTML。 - 核心特点:高度灵活但极其繁琐,大量重复性劳动,难以维护,依赖外部容器。
第二阶段:Spring 时代 - 使用预制件和设计图纸
- 目标:用更高效、更标准化的方式建造一个结构更好、更易扩展的房子。
- 核心创新:聘请一位神奇的管家(IoC 容器)。不再亲自'砌砖'(用 new 实例化对象),只需告诉管家你需要什么(用
@Autowired声明依赖)。管家会自动把预制件(Bean)按照图纸(配置)组装好,送到你手上(依赖注入 DI)。 - 过程:一个总大门(DispatcherServlet),所有访客(请求)都先到这里;接待员根据访客需求,呼叫房子里对应的专业房间(@Controller 中的方法)来接待。
- 核心特点:解耦,专业化(AOP 像装修队一样统一安装中央空调),效率提升,但配置复杂。
Spring Boot 1.0.0正式发布于 2014 年 4 月 1 日,标志着该框架的首次稳定版本发布。基于 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(Model-View-Controller)模式最初由挪威计算机科学家 Trygve Reenskaug 于 1978 年在施乐帕克研究中心提出,目的是为 Smalltalk 编程语言设计用户界面。其核心思想是将应用程序的逻辑分为三个独立组件:
- Model:处理数据逻辑和业务规则。
- View:负责数据展示和用户界面。
- Controller:接收用户输入并协调 Model 与 View 的交互。
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
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")
public String receivePerson(@RequestBody Person person) {
return "接收到参数 person:" + person;
}
3.5 RequestParam
这是 Spring MVC 框架中从 HTTP 请求中提取参数/查询字符串的注解,主要用于将请求参数绑定到控制器方法的参数上。
@RequestMapping("/receiveRename")
public String receiveRename(@RequestParam(value = "name", required = false) String userName) {
return "接收到参数 name:" + userName;
}
注意: 需要接收多个同名参数时(如 param=value1¶m=value2),直接绑定到 List 类型需通过该注解明确声明。
在 Spring MVC 中,参数绑定机制对集合类型和数组类型的处理存在差异:
- ArrayList:必须显式添加
@RequestParam注解。默认绑定规则下,Spring 无法自动推断是否需要将多个同名参数合并为集合,需要明确指示。 - String[]:无需
@RequestParam注解即可正确接收。Spring 对数组类型有原生支持,能自动将多个同名请求参数值绑定到数组。 - List:如果直接使用接口类型而非具体实现类,会报错
java.lang.IllegalStateException: No primary or single unique constructor found for interface java.util.List。Spring 虽然支持接口类型参数绑定,但需要满足特定条件:必须配合@RequestParam注解使用,不能直接使用未注解的接口类型参数。
@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;
}
3.6 PathVariable
作用是从 URL 路径中提取变量值并绑定到方法的参数上。
@RequestMapping("/receivePath/{article}/{blog}")
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 请求的处理器,负责处理 HTTP 请求并返回视图。
- ResponseBody:指示方法返回值应直接写入 HTTP 响应体,而非通过视图解析器渲染。
- RestController:是 Spring MVC 中的一个组合注解,它结合了
@Controller和@ResponseBody的功能,标记的类所有方法返回值默认直接作为 HTTP 响应体(JSON/XML 等格式),无需额外视图渲染。
@RequestMapping("/ControllerResponse")
@Controller
public class ControllerResponse {
// 返回视图
@RequestMapping("/HTMLView")
public String HTMLView() {
return "/show.html";
}
// 返回数据
@ResponseBody
@RequestMapping("/HTMLData")
public String HTMLData() {
return "/show.html";
}
}


