Skip to content

VO 视图对象设计模式与 Session 钝化活化

本文探讨 Web 架构开发中的 VO (View Object) 视图对象设计模式,以及高可用服务器环境下 Session 的钝化活化与 URL 重写 Session 追踪技术。

1. VO (View Object) 视图对象设计模式

在分层架构开发中,我们常常需要对不同的数据载体进行职责解耦:

1.1 PO/Entity vs VO 区别

  • PO / Entity (Persistent Object,持久化实体类): 数据库表结构的一对一映射类。每个字段对应数据库表中的某一列,用于 DAO 层的数据库增删改查。
  • VO (Value Object / View Object,视图对象): 专为前端 UI 页面展示而设计的数据传输载体。当前端页面需要展现的数据来自于多张数据库表联查或包含了计算出的派生属性时,PO 实体类无法或不宜直接包含这些临时属性,此时必须定义 VO 对象。

1.2 VO 设计架构图与代码示例

[ 数据库 ] ──(SQL 多表联查)──> [ DAO 层 (PO) ] ──> [ Service 业务层 (封装组合)] ──> [ VO 视图对象 ] ──> [ 前端页面 / API ]
java
// 需求:订单列表页面需要展示【订单ID、下单用户名、商品名称、购买数量、实付总金额】
// 但数据库中分为 t_order, t_user, t_order_item 三张表

public class OrderDetailVO implements Serializable {
    private String orderId;       // 来自 t_order
    private String username;      // 来自 t_user
    private String productName;   // 来自 t_product
    private Integer count;        // 来自 t_order_item
    private BigDecimal totalPrice;// 业务层计算属性

    // getter, setter, 构造方法...
}

2. Session 的钝化与活化 (Passivation & Activation)

2.1 什么是钝化与活化?

  • 钝化 (Passivation): 当 Tomcat 服务器正常关闭、重启或 Session 长时间未活动导致内存不足时,服务器会把内存中尚未过期的 Session 对象序列化 (Serialize) 并保存到本地磁盘(如 tomcat/work/ 目录下的 .ser 文件中)。
  • 活化 (Activation): 当服务器重新启动完成,或再次有客户端请求携带该 JSESSIONID 访问时,服务器将磁盘上的 .ser 文件反序列化 (Deserialize) 重新加载会内存中恢复 Session。

2.2 核心限制:实现 Serializable 接口

为了保证保存在 Session 中的自定义 Java 对象能够在服务器重启后成功恢复(活化),存入 Session 域中的所有自定义类必须实现 java.io.Serializable 接口

java
// ✅ 正确规范:存入 Session 的用户或购物车对象必须实现 Serializable
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String id;
    private String username;
    // ...
}

警告:如果存入 Session 的对象没有实现 Serializable 接口,在 Tomcat 关闭或触发钝化时,后台控制台将抛出 java.io.NotSerializableException 异常,且该对象的 Session 数据将在重启后彻底丢失。


当用户将浏览器的 Cookie 功能完全禁用时,浏览器将无法保存与发送 JSESSIONID Cookie。此时服务器每次调用 getSession() 都将误以为是新用户。

可以使用 URL 重写 (URL Rewriting) 显式在每个链接 URL 的问号前拼接 JSESSIONID 证明身份:

java
// 使用 response.encodeURL 包装访问链接
String originalUrl = "buy.jsp";
String encodedUrl = response.encodeURL(originalUrl);

// 输出结果(若 Cookie 被禁用,自动追加参数):
// buy.jsp;jsessionid=ABCD1234EF5678GH
out.print("<a href='" + encodedUrl + "'>前往结算</a>");

基于 VitePress 构建 | 技术知识库