JavaWeb 笔记 03 (31 - 45)
Maven, 单元测试, Web 基础相关 (31 - 45)
提前声明:本文章仅作个人学习记录,不涉及商业用途。
内容记录于学习途中, 没有按分类进行总结, 可能存在错误或不完整之处。
第一章 Maven
一、概述
1. 构建生命周期 / 阶段
| 生命周期 | 描述 |
|---|---|
| clean | 清理项目 |
| compile | 编译项目 |
| test | 测试项目 |
| package | 打包项目 |
2. 项目对象模型 (POM)
<groupId>com.fallw</groupId>
<artifactId>maven-project01</artifactId>
<!-- 快照版本 -->
<version>1.0-SNAPSHOT</version>
<properties>
<!-- 源代码 JDK 版本 -->
<maven.compiler.source>21</maven.compiler.source>
<!-- 字节码 JVM 版本 -->
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
3. 项目依赖管理 (Dependency)
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.14.0</version>
</dependency>
依赖查找顺序:本地仓库 \(\leftrightarrow\) 远程仓库(私服)\(\leftrightarrow\) 中央仓库
二、Maven 配置
1. settings.xml
<!-- 56 行 本地仓库路径 -->
<localRepository>D:\Environment\apache-maven-3.9.14\repository</localRepository>
<!-- 162 行 私服地址 -->
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
<!-- 229 行 默认 JDK 版本 -->
<profile>
<id>jdk-21</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>21</jdk>
</activation>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<maven.compiler.compilerVersion>21</maven.compiler.compilerVersion>
</properties>
</profile>
2. IntelliJ IDEA 配置
全局配置
- Build Tools \(\rightarrow\) Maven \(\rightarrow\) Maven home directory, User settings file, Local repository
- Build Tools \(\rightarrow\) Maven \(\rightarrow\) Runner \(\rightarrow\) JRE
- Compiler \(\rightarrow\) Java Compiler \(\rightarrow\) Project bytecode version
三、依赖管理
1. 依赖配置
<!-- 依赖配置 -->
<dependencies>
<dependency>...</dependency>
</dependencies>
<!-- 排除配置 -->
<exclusions>
<exclusion>...</exclusion>
</exclusions>
2. 生命周期
Maven 中有三套相互独立的生命周期:
- clean:清理项目,删除目标目录
- default:默认生命周期,包含 compile、test、package 等阶段
- site:生成项目站点文档
| 生命周期 | 命令 | 具体功能 |
|---|---|---|
| clean | mvn clean |
删除项目目录下的 target 目录 |
| compile | mvn compile |
编译项目源代码,生成 .class 文件 |
| test | mvn test |
执行 test 下的测试类 |
| package | mvn package |
将项目打包成一个 jar 包安装到本项目的 target 下 |
| install | mvn install |
将项目安装到本地仓库 |
在同一套生命周期中,运行后面的阶段时,前面的阶段都会运行。
第二章 单元测试
一、测试概述
测试方式
| 测试方式 | 目的 |
|---|---|
| 白盒测试 | 验证代码与逻辑的正确性 |
| 黑盒测试 | 验证软件的功能与兼容性 |
| 灰盒测试 | 同时考虑白盒和黑盒测试 |
测试步骤
| 测试步骤 | 目的 | 测试方式 |
|---|---|---|
| 单元测试 | 检验项目代码的基本单位的正确性 | 白盒测试 |
| 集成测试 | 检验项目之间的协作是否符合预期 | 灰盒测试 |
| 系统测试 | 验证软件系统的正确性与性能 | 黑盒测试 |
| 验收测试 | 验证软件系统是否与客户需求一致 | 黑盒测试 |
二、JUnit
优点:
- 测试代码与源代码分开,便于维护。
- 一个测试方法执行失败,不会影响其他测试方法。
- 自动生成测试报告,红色失败,绿色成功。
1. pom.xml 引入 JUnit
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
2. 测试类
// 单元测试类命名规范:XxxTest。
public class GetAgeTest {
@Test
// JUnit 测试方法必须声明为 public void。
public void testGetAge(){
Integer age = new UserService().getAge("110002200505091218");
System.out.println(age);
}
}
三、断言
1. Assertions 类
| 断言方法 | 描述 |
|---|---|
Assertions.assertEquals(Object exp,Object act, String msg) |
断言两个值是否相等 |
Assertions.assertNotEquals(Object unexp,Object act, String msg) |
断言两个值是否不相等 |
Assertions.assertTrue(boolean exp, String msg) |
断言 boolean 值为 true |
Assertions.assertFalse(boolean exp, String msg) |
断言 boolean 值为 false |
Assertions.assertNull(Object exp, String msg) |
断言对象为 null |
Assertions.assertNotNull(Object exp, String msg) |
断言对象不为 null |
Assertions.assertThrows(Class expType, Executable exp, String msg) |
断言抛出指定异常 |
2. 常见注解
| 注解 | 描述 | 备注 |
|---|---|---|
@Test |
标记测试方法 | |
@ParameterizedTest |
标记参数化测试方法 | 无需使用 @Test |
@ValueSource |
为参数化测试方法提供测试参数 | 与 @ParameterizedTest 一起使用 |
@DisplayName |
为测试方法的显示名称 | 默认为类名、方法名 |
@BeforeEach |
在每个测试方法之前执行 | 初始化资源 |
@AfterEach |
在每个测试方法之后执行 | 释放资源 |
@BeforeAll |
在所有测试方法之前执行 | static 初始化资源,只执行一次 |
@AfterAll |
在所有测试方法之后执行 | static 释放资源,只执行一次 |
3. 依赖范围
<scope>test</scope>
| 依赖范围 | main |
test |
package |
范例 |
|---|---|---|---|---|
compile (default) |
Y | Y | Y | log4j |
test |
- | Y | - | junit |
provided |
Y | Y | - | servlet-api |
runtime |
- | Y | Y | jdbc driver |
4. 常见问题
IDEA 配置文件目录:C:\Users\fallw\AppData\Roaming\JetBrains
Maven 删除不完整依赖:del /s *.lastUpdated
使用 SpringBoot 4 时发生依赖冲突:安装最新版 IDEA(2025.3)。
第三章 Web 基础
一、SpringBoot Web
1. SpringBoot Web
修改起步依赖:Generate... \(\rightarrow\) Edit Starters
阿里云脚手架:https://start.aliyun.com/
<!-- SpringBoot 4.0.5 -->
<!-- Web 开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
查询依赖版本:spring-boot-starter-parent \(\rightarrow\) spring-boot-dependencies \(\rightarrow\) ${lombok.version}
| 功能 | 说明 |
|---|---|
| 内嵌服务器 | 集成 Tomcat,运行 main 启动 Web 项目 |
| Spring MVC | 提供 @RestController 等注解处理 HTTP 请求 |
| RESTFul 支持 | 自动配置 JSON 处理,把 Java 对象转为 JSON |
| 参数绑定 | 将前端传递的参数映射到 Java 方法参数上 |
2. 常用注解
| 领域 | 注解 | 位置 | 写法与常用参数 |
|---|---|---|---|
| 核心控制器与映射 | @RestController |
类 | @RestController |
@RequestMapping |
类 | @RequestMapping("/api/orders") |
|
| 方法 | @RequestMapping(value = "/list", method = RequestMethod.GET) |
||
@GetMapping |
方法 | @GetMapping("/{id}") |
|
@PostMapping |
方法 | @PostMapping("/save") |
|
@PutMapping |
方法 | @PutMapping("/update") |
|
@DeleteMapping |
方法 | @DeleteMapping("/{id}") |
|
| 参数接收 | @PathVariable |
方法参数 | @PathVariable("id") Long id |
@RequestParam |
方法参数 | @RequestParam(name="page", defaultValue="1") Integer p |
|
@RequestBody |
方法参数 | @RequestBody UserDTO dto (接收JSON) |
|
@RequestHeader |
方法参数 | @RequestHeader("Authorization") String token |
|
| 响应与异常处理 | @ResponseStatus |
方法 | @ResponseStatus(HttpStatus.CREATED) |
@RestControllerAdvice |
类 | @RestControllerAdvice (全局异常拦截) |
|
@ExceptionHandler |
方法 | @ExceptionHandler(Exception.class) |
|
| 跨域配置 | @CrossOrigin |
类 / 方法 | @CrossOrigin(origins = "*") |
二、HTTP (Hyper Text Transfer Protocol)
1. 特点
- 基于 TCP 协议
- 基于请求响应模型
- 无状态:多次请求不能共享数据、速度快
2. 请求协议
测试样例
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(@RequestParam(value = "name", defaultValue = "world") String name) {
return "hello, " + name;
}
}
请求行
### HTTP Request Analysis
# @Method: GET
# @Path: /hello
# @Query-String: name=fallw
# @Protocol: HTTP/1.1
GET /hello?name=fallw HTTP/1.1
# [Request-Line] 定义请求方法、请求资源路径及协议版本
请求头
Host: 127.0.0.1:8080
# [Host] 指定目标服务器的域名或 IP 地址及端口号(HTTP/1.1 必须字段)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
# [Content-Negotiation] 客户端可处理的媒体类型(MIME)及其权重优先级(q-factor)
Accept-Encoding: gzip, deflate, br, zstd
# [Content-Encoding] 客户端支持的内容压缩/编码算法列表
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
# [I18N] 客户端首选的自然语言及地区偏好
Connection: keep-alive
# [Connection-Management] 显式要求建立持久连接以复用 TCP 通道
Cookie: session-4a3fb776=eyJjc3Jm...; session-4a3fb776.sig=TytA...
# [State-Management] 客户端存储的键值对凭证,用于维持服务器端的会话状态
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Edg/146.0.0.0
# [Client-Identification] 包含操作系统、浏览器内核及版本的软件代理标识
Upgrade-Insecure-Requests: 1
# [Security] 指示客户端偏好加密连接,并可处理服务器的 HTTPS 重定向
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
# [Fetch-Metadata] 浏览器自动生成的请求上下文信息,用于服务端进行安全审计与 CSRF 防御
sec-ch-ua: "Chromium";v="146", "Not-A.Brand";v="24", "Microsoft Edge";v="146"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
# [Client-Hints] 现代浏览器提供的结构化用户代理信息,旨在减少指纹追踪并提高解析效率
请求体
// POST 请求,存放请求参数
{ "name": "fallw" }
请求数据格式
| 字段 | 描述 |
|---|---|
| Host | 请求的主机名 |
| User-Agent | 浏览器版本,Chrome 的标识类似 Mozail/5.0 ... Chrome/146,IE 的标识类似 Mozail/5.0(Windows NT ...) like Gecko |
| Accept | 浏览器可接受资源类型, 如 text/*, */* 表示所有 |
| Accept-Language | 浏览器可接受语言,如 zh-CN |
| Accept-Encoding | 浏览器可支持的压缩类型,例如 gzip, deflate |
| Content-Type | 请求主体的请求类型 application/json |
| Content-Length | 请求主体的大小(单位:字节) |
请求元数据
| 分类 | 请求头 (Key) | 取值 (Value) | 技术定义与安全逻辑 |
|---|---|---|---|
| Fetch Metadata (请求来源元数据) |
Sec-Fetch-Site | same-origin |
请求发起方与目标资源完全同源(协议、域名、端口一致)。信任度最高。 |
same-site |
请求发起方与目标资源属于同一站点(二级域名相同)。常用于子域间跳转。 | ||
cross-site |
请求由第三方站点发起。此类请求存在 CSRF 风险,服务端需校验 Token。 | ||
none |
用户直接触发请求(如地址栏输入、书签点击、文件拖拽)。视为用户主动意图。 | ||
| Fetch Metadata (请求模式) |
Sec-Fetch-Mode | navigate |
用于 HTML 文档的导航/跳转请求(点击链接、表单提交)。 |
cors |
跨域资源共享请求(由 fetch 或 XMLHttpRequest 发起)。 |
||
no-cors |
非跨域模式,通常用于加载图片、脚本等静态资源。 | ||
websocket |
专门用于 WebSocket 协议的握手请求。 | ||
| Fetch Metadata (目标类型) |
Sec-Fetch-Dest | document |
请求的目标是完整的 HTML 文档(页面渲染)。 |
image / script / style |
请求的目标是特定的静态资源文件。 | ||
empty |
请求目标不涉及渲染(如 API 接口调用或预加载请求)。 | ||
| 交互标识 | Sec-Fetch-User | ?1 / ?0 |
标识请求是否由用户激活(如点击、按键)触发。?1 表示用户激活。 |
| Client Hints (客户端提示) |
sec-ch-ua | "Brand";v="Ver" |
结构化的浏览器品牌与主要版本号,用于替代冗长的 User-Agent。 |
| sec-ch-ua-mobile | ?0 / ?1 |
布尔型标识。?1 代表移动端设备,?0 代表非移动端。 |
|
| sec-ch-ua-platform | "Windows" 等 |
操作系统名称。相较于 UA 字符串,格式更加固定,便于后端解析。 |
3. 请求获取 HttpSerletRequest
| 分类 | 方法 | 示例返回结果 |
|---|---|---|
| 请求基本信息 | getMethod() |
GET |
getScheme() |
http |
|
getProtocol() |
HTTP/1.1 |
|
| 路径相关 | getRequestURI() |
/hello |
getRequestURL().toString() |
http://localhost:8080/hello |
|
getContextPath() |
(根路径通常为空) | |
| 参数相关 | getQueryString() |
name=fallw |
getParameter("name") |
fallw |
|
| 请求头相关 | getHeader("User-Agent") |
Mozilla/5.0... |
getCookies() |
Cookie[] (需遍历获取值) |
|
| 网络属性 | getRemoteAddr() |
127.0.0.1 |
getLocalPort() |
8080 |
4. 响应协议
响应行
HTTP/1.1 200
# [Status Line] 响应状态行:包含协议版本(HTTP/1.1)和状态码(200 OK,表示请求成功)
响应头
Content-Type: text/html;charset=UTF-8
# [Content-Type] 指定响应实体的媒体类型(HTML 文档)及字符编码(UTF-8),告知浏览器如何渲染内容
Content-Length: 12
# [Content-Length] 指示响应正文的字节长度(此处 "hello, fallw" 刚好 12 字节)
Date: Sat, 04 Apr 2026 09:35:38 GMT
# [Date] 服务器生成响应的具体 GMT 时间
Keep-Alive: timeout=60
# [Keep-Alive] 配合 keep-alive 连接使用,告知客户端该 TCP 连接可以保持空闲的时间(秒)
Connection: keep-alive
# [Connection] 决定当前事务完成后是否关闭网络连接(keep-alive 表示保持持久连接,以复用 TCP 链路)
响应体
hello, fallw
# [Response Body] 响应正文:服务器实际返回给客户端的数据内容
响应状态码
| 状态码 | 描述 |
|---|---|
1xx |
响应中 - 临时状态码 |
2xx |
成功 - 响应成功 |
3xx |
重定向 - 临时重定向或永久重定向 |
4xx |
客户端错误 - 错误请求 |
5xx |
服务器错误 - 服务器内部错误 |
常用状态码
| 状态码 | 英文描述 | 解释 |
|---|---|---|
200 |
OK |
客户端请求成功,即处理成功,这是我们最想看到的状态码 |
302 |
Found |
指示所请求的资源已移动到由Location响应头给定的 URL,浏览器会自动重新访问到这个页面 |
304 |
Not Modified |
告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向 |
307 |
Temporary Redirect |
临时重定向,要求浏览器原封不动地(保持原请求方法和数据)向 Location 发送请求 |
400 |
Bad Request |
客户端请求有语法错误,不能被服务器所理解 |
403 |
Forbidden |
服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源 |
404 |
Not Found |
请求资源不存在,一般是URL输入有误,或者网站资源被删除了 |
405 |
Method Not Allowed |
请求方式有误,比如应该用GET请求方式的资源,用了POST |
428 |
Precondition Required |
服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头 |
429 |
Too Many Requests |
指示用户在给定时间内发送了太多请求(“限速”),配合 Retry-After(多长时间后可以请求)响应头一起使用 |
431 |
Request Header Fields Too Large |
请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。 |
500 |
Internal Server Error |
服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧 |
503 |
Service Unavailable |
服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好 |
307 临时重定向
HTTP/1.1 307 Temporary Redirect
# [Status Line] 临时重定向:告知客户端资源临时移动,但必须保留原始请求方法
Location: /new-temp-endpoint
# [Location] 指定目标新地址(浏览器会自动跳转至此)
Non-Authoritative Reason:
# 307 与 302 的区别:
# 302:允许浏览器将 POST 变更为 GET(历史遗留问题,行为不可控)
# 307:强制严谨,如果是 POST 请求,重定向后必须依然是 POST
响应数据格式
| 字段 | 描述 |
|---|---|
Content-Type |
该响应内容的类型,如 text/html, application/json |
Content-Length |
该响应内容的长度(字节数) |
Content-Encoding |
该响应的压缩算法,如 gzip |
Cache-Control |
指定缓存控制策略,如 max-age=300 |
Set-Cookie |
设置 Cookie |
5. 响应获取
基于 Web 服务提供的 HttpServletResponse 获取响应
@RequestMapping("/response")
public void response(HttpServletResponse response) throws IOException {
response.setStatus(401); // 响应状态码
response.setHeader("name", "fallw"); // 响应体
response.getWriter().write("response success"); // 响应体
}
| 分类 | 方法 | 示例代码或结果 |
|---|---|---|
| 状态码相关 | setStatus(int sc) |
response.setStatus(200) (设置正常成功状态) |
sendError(int sc, String msg) |
response.sendError(404, "Resource Not Found") |
|
| 响应头相关 | setHeader(String name, String value) |
response.setHeader("Cache-Control", "no-cache") |
addCookie(Cookie cookie) |
Set-Cookie: user=fallw; Path=/ (在浏览器存入 Cookie) |
|
setContentType(String type) |
text/html;charset=UTF-8 (告知浏览器解析格式) |
|
| 正文内容相关 | getWriter() |
PrintWriter (用于输出文本数据,如 HTML/JSON) |
getOutputStream() |
ServletOutputStream (用于输出二进制数据,如图片) |
|
| 重定向与转发 | sendRedirect(String location) |
302 / 307 (客户端自动跳转至新 URL) |
encodeURL(String url) |
/hello;jsessionid=1A2B3C... (用于 URL 重写追踪会话) |
|
| 网络属性/配置 | setCharacterEncoding(String charset) |
UTF-8 (设置响应体的字符编码) |
setContentLength(int len) |
Content-Length: 1024 (手动指定响应体大小) |
基于 Spring 中提供的 ResponseEntity 获取响应
@RequestMapping("/response-entity")
public ResponseEntity<String> responseEntity() {
return ResponseEntity
.status(401) // 响应状态码
.header("name", "fallw") // 响应头
.body("response-entity success"); // 响应体
}
浙公网安备 33010602011771号