跳到主要内容Java Web 实战:JSON 处理、单元测试与会话管理 | 极客日志Javajava
Java Web 实战:JSON 处理、单元测试与会话管理
本文涵盖 Java Web 开发中的核心概念,包括 JSON 数据格式规范与序列化反序列化操作。通过 JUnit 的@Test 注解进行单元测试验证代码逻辑。深入解析 Cookie 与 Session 的区别及存储机制,并演示如何使用 Spring MVC 注解(如@RequestBody、@PathVariable)处理 HTTP 请求参数、文件上传及 Header 信息。适合希望巩固 Web 基础与状态管理的开发者参考。
GitMaster16 浏览 Java Web 核心:JSON 基础与请求处理
JSON 概念与语法
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于 JavaScript 的一个子集,但采用了独立语言的文本格式,几乎所有编程语言都有支持 JSON 的库。
核心数据类型
| 类型 | 说明 |
|---|
| 对象(Object) | 无序的键值对集合,用{}包裹;键必须是双引号包裹的字符串,值为任意 JSON 类型 |
| 数组(Array) | 有序的值列表,用[]包裹;值可以是任意 JSON 类型,且允许不同类型混合 |
| 字符串(String) | 必须双引号包裹(不能用单引号),支持转义字符 |
| 数字(Number) | 支持整数、浮点数、负数;不支持 NaN、Infinity;可省略小数点前/后数字 |
| 布尔值(Boolean) | 仅两个固定值:true 或 false |
| 空值(Null) | 仅一个固定值:null,表示'无数据' |
语法规则
- JSON 的结构只能是对象或者数组,不能直接是字符串、数字等单个值;
- 键名必须是双引号,不能是单引号;
- 逗号分隔且无尾逗号:对象的键值对、数组的元素之间用逗号分隔,但是最后一个元素不能加逗号;
- JSON 不支持注释;
- 大小写敏感。
常见使用示例
对象结构
{
"name": "张三",
"age": 30,
"isStudent": false,
"hobbies": ["篮球", "编程"],
"address":
{
"city"
:
"上海"
,
"street"
:
"XX 路"
}
,
"score"
:
null
}
[
{ "id": 1, "name": "苹果", "price": 5.99 },
{ "id": 2, "name": "香蕉", "price": 3.5 },
{ "id": 3, "name": "橙子", "price": 4.2 }
]
单元测试与 JSON 验证
@Test 注解的作用
@Test 是 JUnit 提供的单元测试注解,用于标记'测试方法',让测试框架自动执行该方法以验证代码逻辑。在开发中,我们常用它来测试 JSON 序列化/反序列化是否正常,或者从文件读取数据的功能是否稳定。
public class JsonTest {
@Test
void test1() {
System.out.println(111);
}
@Test
void test2() {
System.out.println(222);
}
}
测试方法可以单个窗口执行,也可以多个窗口同时运行,互不干扰。
@Test 与 main 方法的区别
| 对比 | @Test 标记的测试方法 | main 方法 |
| 作用 | 验证代码功能 | 程序入口,启动并指向核心业务逻辑 |
| 调用 | 由测试框架自动调用 | 由 JVM 自动调用(程序启动时) |
| 定义规范 | 必须是无参数 void | 必须是 public static void main(String[] args) |
| 独立性 | 每个测试方法应独立,不依赖执行顺序 | 代码逻辑顺序执行,依赖前后逻辑 |
| 场景 | 用于开发的测试验证 | 用于程序的实际运行 |
JSON 与 Java 对象互转
在实际开发中,我们经常需要在 JSON 字符串和 Java 对象之间进行转换。这里推荐使用 Jackson 库的 ObjectMapper 类。
实体类定义
首先定义一个简单的用户信息类,确保包含 Getter/Setter 方法。
public class UserInfo {
private String name;
private int age;
public UserInfo() {}
public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "UserInfo{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
对象转 JSON 字符串
使用 writeValueAsString() 方法将对象序列化为 JSON 字符串。
@Test
void test3() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
UserInfo userInfo = new UserInfo("小熊", 3);
String s = objectMapper.writeValueAsString(userInfo);
System.out.println(s);
}
注意:writeValueAsString() 可能会抛出异常,因此方法签名通常需要声明 throws JsonProcessingException。
JSON 字符串转对象
使用 readValue() 方法将 JSON 字符串反序列化为 Java 对象。
@Test
void test4() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String s = "{\"name\":\"小熊\",\"age\":3}";
UserInfo userInfo = objectMapper.readValue(s, UserInfo.class);
System.out.println(userInfo);
}
这里的 UserInfo.class 指定了要转换的目标类类型。
HTTP 请求处理
在 Spring MVC 中,处理前端传来的数据有多种方式,根据数据来源不同选择相应的注解。
接收 JSON 对象 (@RequestBody)
当需要接收前端发送的 JSON 格式的请求体数据时,使用 @RequestBody 注解。它充当桥梁,把前端请求体里面的 JSON 数据自动绑定到后端对象上。
@RequestMapping("/api/user")
public String handleUser(@RequestBody UserInfo user) {
return "收到用户:" + user.getName();
}
如果没有 @RequestBody,Spring 将无法正确解析请求体中的 JSON 内容。
获取 URL 路径参数 (@PathVariable)
对于动态路径中的参数,例如 /user/123/info,需要使用 @PathVariable。
@RequestMapping("/11/{id}/{name}")
public String method10(@PathVariable("id") Integer id, @PathVariable("name") String name) {
return "获取文章 id:" + id + " 获取文章 name:" + name;
}
{变量名} 表示 URL 的动态部分。
@PathVariable("userid") 指定的名称需要与 URL 模版中的 {userid} 一致,虽然有时可以不写名称,但显式指定更清晰。
上传文件 (@RequestPart)
处理文件上传通常涉及 multipart/form-data 类型请求,可以使用 @RequestPart 配合 MultipartFile。
@RequestMapping("/upload")
public String methodUpload(@RequestPart("file") MultipartFile file) throws IOException {
String filename = file.getOriginalFilename();
System.out.println(filename);
File file1 = new File("D:/.temp/" + file.getOriginalFilename());
file.transferTo(file1);
return "文件上传成功";
}
MultipartFile 是 Spring 提供的文件上传处理接口,transferTo 方法负责将流写入磁盘。
Cookie 与 Session 管理
Web 开发中,HTTP 协议本身是无状态的,为了保持用户状态,我们需要借助 Cookie 和 Session。
Cookie 详解
Cookie 是 Web 服务器保存在用户浏览器上的小型文本文件。
- 首次访问网站,浏览器发送请求;
- 服务器生成 Cookie 信息,通过
Set-Cookie 响应头返回;
- 浏览器接收并保存 Cookie;
- 再次访问该网站时,浏览器自动在请求头中携带 Cookie;
- 服务器识别 Cookie,返回对应内容;
- Cookie 过期或被清除后失效。
这就好比去超市办了一张会员卡,第一次存入积分,第二次结账出示卡片即可识别身份。
@RequestMapping("/getCookies")
public String method14(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + ":" + cookie.getValue());
}
}
return "获取 Cookie 成功";
}
@RequestMapping("/getCookieValue")
public String method15(@CookieValue("username") String name) {
return "用户名:" + name;
}
Session 详解
Session 是指客户端与服务器之间为完成特定交互而建立的临时、有状态的连接或数据存储机制,核心作用是在无状态的 HTTP 下保护用户的身份信息和操作上下文。
- 创建 Session:用户首次访问,服务器生成唯一的 SessionId 并在服务端创建存储;
- 传递 SessionId:服务器通过 Cookie 将 SessionId 发送给客户端;
- 后续交互验证:客户端携带 Cookie 中的 SessionId,服务器找到对应的 Session 数据确认身份;
- 销毁 Session:用户退出登录、超时或服务器重启后删除。
这就像在酒店吃饭,服务员为你生成一份专属菜单(Session 数据),凭取餐码(SessionId)就能知道你的订单。
@RequestMapping("/setSession")
public String method16(HttpSession session) {
session.setAttribute("name", "lisi");
session.setAttribute("age", "11");
return "存储 Session 成功";
}
@RequestMapping("/getSession1")
public String method17(HttpSession session) {
if (session == null) {
return "用户未登入";
}
String username = (String) session.getAttribute("name");
String userage = (String) session.getAttribute("age");
return "用户名:" + username + " 年龄:" + userage;
}
@RequestMapping("/getSession2")
public String method18(HttpSession session) {
String username = (String) session.getAttribute("name");
return "用户名:" + username;
}
@RequestMapping("/getSession3")
public String method19(@SessionAttribute("name") String name) {
return "用户名:" + name;
}
Cookie 与 Session 的关系
| Cookie | Session |
| 存储位置 | 浏览器(客户端) | 服务器(服务端) |
| 核心作用 | 传递 SessionId | 存用户会话'信息' |
| 依赖关系 | Session 通常靠 Cookie 传 ID 实现 | Cooki 常作为 Session 的'身份证' |
| 典型场景 | 记录登录状态(传 SessionId) | 存用户购物、登录详细信息 |
Header 是通信双方传递的'额外说明信息',类似于点外卖时的备注。
@RequestMapping("/getHeader1")
public String method20(HttpServletRequest request) {
String agent = request.getHeader("User-Agent");
return "agent:" + agent;
}
@RequestMapping("/getHeader2")
public String method21(@RequestHeader("User-Agent") String agent) {
return "agent:" + agent;
}
通过这种方式,我们可以轻松获取客户端的设备信息、语言偏好等细节。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online