杰瑞科技汇

Java session如何正确使用与销毁?

什么是 Session?

Session(会话)是一种在 Web 服务器端记录和跟踪用户状态的机制。

Java session如何正确使用与销毁?-图1
(图片来源网络,侵删)

想象一下你去超市购物:

  • HTTP 协议:就像你每次去超市,都是一次独立的访问,收银员(服务器)不记得你上次买了什么,你告诉收银员“我要买A商品”,这次交易就结束了,下次来,收银员不认识你。
  • Session 机制:超市给你发了一张会员卡(Session ID),你每次去,都出示这张卡,收银机(服务器)通过卡号就能查到你的购物车(Session 数据),知道你上次没买完的商品,继续为你服务。

核心概念:

  1. 状态管理:HTTP 是无状态的,但 Session 让服务器能够“用户。
  2. 工作原理
    • 用户首次访问服务器时,服务器会创建一个独特的 Session ID(通常是一个长字符串,如 JSESSIONID=1234567890ABCDEFFEDCBA9876543210)。
    • 服务器将这个 Session ID 和与该用户相关的数据(如登录信息、购物车内容)存储在服务器内存或数据库中。
    • 服务器通过 Cookie 将这个 Session ID 发送给客户端的浏览器,并让浏览器保存。
    • 当用户再次发送请求时,浏览器会自动携带这个 Cookie(包含 Session ID)。
    • 服务器收到请求后,读取 Session ID,在服务器端找到对应的 Session 数据,从而识别用户并获取其状态。

Session 的基本用法(在 Servlet 中)

在 Java Web 开发中,最传统的处理 Session 的地方是 Servlet,我们以 Servlet API 为例来讲解核心操作。

1 获取 Session 对象

HttpServletservice 方法(或 doGet, doPost 等)中,可以通过 request 对象获取 HttpSession 对象。

Java session如何正确使用与销毁?-图2
(图片来源网络,侵删)
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
// 在 doGet 或 doPost 方法中
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
    // 获取 HttpSession 对象
    // 参数 true 的含义是:
    // - 如果当前请求没有 Session,则创建一个新的 Session 并返回。
    // - 如果有,则返回现有的 Session。
    // 如果传入 false,则没有 Session 时会返回 null。
    HttpSession session = request.getSession();
    // 如果只想获取已存在的 Session,不希望创建新的
    // HttpSession session = request.getSession(false);
}

2 向 Session 中存取数据

HttpSession 对象像一个 Map,可以存储键值对,它支持多种数据类型。

// 在 doGet 或 doPost 方法中
HttpSession session = request.getSession();
// 1. 向 Session 中存数据 (setAttribute)
// 键是 String 类型,值是 Object 类型
session.setAttribute("username", "张三");
session.setAttribute("userLoginTime", new Date());
session.setAttribute("shoppingCart", new ArrayList<String>());
// 2. 从 Session 中取数据 (getAttribute)
// getAttribute 返回的是 Object 类型,需要强制类型转换
String username = (String) session.getAttribute("username");
Date loginTime = (Date) session.getAttribute("userLoginTime");
if (username != null) {
    System.out.println("欢迎回来, " + username);
    System.out.println("您的登录时间是: " + loginTime);
} else {
    System.out.println("您还未登录");
}
// 3. 删除 Session 中的指定数据 (removeAttribute)
session.removeAttribute("username");

3 Session 的生命周期管理

3.1 Session 的创建

  • 当调用 request.getSession()request.getSession(true) 时,如果当前请求没有关联的 Session,服务器就会创建一个新的 Session。

3.2 Session 的销毁

Session 会在以下几种情况下被销毁:

  1. 调用 invalidate() 方法:这是最直接的方式,会立即销毁整个 Session,并清除所有数据。
    // 用户点击“退出登录”按钮时调用
    session.invalidate();
  2. Session 超时:每个 Session 都有一个默认的超时时间(通常是 30 分钟),如果用户在指定时间内没有发送任何请求,服务器会自动销毁该 Session。
    • 配置超时时间
      • web.xml 中配置(全局)
        <session-config>
            <!-- 设置超时时间为 15 分钟,单位为分钟 -->
            <session-timeout>15</session-timeout>
        </session-config>
      • 在代码中动态设置(针对当前 Session)
        // 设置 Session 超时时间为 10 分钟
        session.setMaxInactiveInterval(10 * 60); // 单位是秒
  3. 服务器关闭或重启:所有 Session 数据都会丢失。

3.4 获取 Session ID 和创建时间

HttpSession session = request.getSession();
// 获取 Session 的唯一标识符
String sessionId = session.getId();
System.out.println("当前 Session ID: " + sessionId);
// 获取 Session 的创建时间(从 1970 年 1 月 1 日开始的毫秒数)
long creationTime = session.getCreationTime();
System.out.println("Session 创建时间: " + new Date(creationTime));
// 获取 Session 最后一次被访问的时间
long lastAccessedTime = session.getLastAccessedTime();
System.out.println("Session 最后访问时间: " + new Date(lastAccessedTime));

在现代框架中的用法(Spring MVC)

在现代的 Java Web 框架如 Spring MVC 中,对 Session 的操作进行了封装,使用起来更加方便。

1 在 Controller 中注入 HttpSession

Spring MVC 会自动将 HttpSession 对象注入到 Controller 方法的参数中。

Java session如何正确使用与销毁?-图3
(图片来源网络,侵删)
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class UserController {
    @GetMapping("/login")
    public String loginPage() {
        return "login"; // 返回登录页面的视图名
    }
    @PostMapping("/doLogin")
    public String doLogin(@RequestParam String username, HttpSession session) {
        // 1. 将用户名存入 Session
        session.setAttribute("username", username);
        System.out.println("用户 " + username + " 已登录,Session ID: " + session.getId());
        // 2. 重定向到主页,防止表单重复提交
        return "redirect:/home";
    }
    @GetMapping("/home")
    public String homePage(HttpSession session) {
        // 3. 从 Session 中获取用户名
        String username = (String) session.getAttribute("username");
        if (username == null) {
            // Session 中没有用户名,说明未登录,重定向到登录页
            return "redirect:/login";
        }
        // 将用户名传递给视图
        // 在 Thymeleaf 等模板引擎中,可以直接通过 ${session.username} 访问
        return "home";
    }
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        // 4. 销毁 Session
        session.invalidate();
        return "redirect:/login";
    }
}

2 使用 @SessionAttributes 注解

这是一个更高级的用法,可以让你直接将 Model 中的数据自动存入 Session,而无需手动调用 session.setAttribute

场景:当你希望在多个请求之间共享 Model 中的某个数据时。

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.ModelAttribute;
@Controller
// 将名为 "user" 的模型属性存入 Session
@SessionAttributes("user")
public class ProfileController {
    @GetMapping("/profile")
    public String showProfile(Model model) {
        // 假设从数据库获取用户信息
        User user = new User("李四", "lisi@example.com");
        // 将 user 对象添加到 Model 中
        // 因为有 @SessionAttributes("user") 注解,这个 user 对象也会被存入 Session
        model.addAttribute("user", user);
        return "profile";
    }
    @GetMapping("/editProfile")
    public String editProfile(@ModelAttribute("user") User user) {
        // 这个方法可以直接从 Session 中获取到 "user" 对象
        // 无需再从数据库查询
        System.out.println("正在编辑用户信息: " + user.getName());
        return "edit_profile";
    }
    @PostMapping("/saveProfile")
    public String saveProfile(@ModelAttribute("user") User user) {
        // 保存修改后的用户信息...
        System.out.println("保存用户信息: " + user.getName());
        // 可以选择从 Session 中移除该属性
        // 但通常不这样做,因为用户可能继续访问其他页面需要这些信息
        // 如果想移除,可以在 Controller 方法中通过 Model 的 addAttribute 覆盖为 null
        // 或者使用 SessionStatus 对象
        return "redirect:/profile";
    }
}

Session 的优缺点与最佳实践

1 优点

  • 安全性高:敏感数据存储在服务器端,客户端只持有一个无意义的 Session ID,不容易被篡改。
  • 存储容量大:Session 可以存储比 Cookie 更多的数据,因为 Cookie 有大小限制(通常为 4KB)。
  • 灵活性强:可以存储任何 Java 对象(只要实现了 Serializable 接口)。

2 缺点

  • 占用服务器资源:每个活跃的 Session 都会占用服务器的内存,如果用户量巨大,会给服务器带来压力。
  • 扩展性问题:在分布式或集群环境下,Session 存储在单个服务器的内存中,就会出现“Session 粘滞”(Sticky Session)问题,即用户必须每次都请求到同一台服务器,否则会丢失 Session,解决方案是使用 Session 集群,如将 Session 存入 Redis、Memcached 等共享缓存中。
  • 依赖 Cookie:默认情况下,Session 依赖 Cookie 传递 ID,如果用户禁用了 Cookie,Session 可能会失效(可以通过 URL 重写来解决,但用户体验差)。

3 最佳实践

  1. 只存储必要的数据:不要将大对象或大量数据存入 Session,Session 应用于存储用户的身份状态(如 userId, username),而不是业务数据(如整个商品列表)。
  2. 及时清理数据:当用户退出登录或不再需要某些数据时,使用 removeAttribute() 及时清理,避免数据冗余。
  3. 合理设置超时时间:根据业务需求设置合理的 session-timeout,对于安全性要求高的系统,可以设置较短的超时时间。
  4. 考虑序列化:如果需要将 Session 对象持久化到硬盘或在网络中传输(如分布式环境),确保存入 Session 的 Java 类实现了 java.io.Serializable 接口。
  5. 注意线程安全:在同一个 Session 中,多个请求可能并发执行(尤其是在异步场景下),Session 中的数据被多个线程同时修改,可能会导致数据不一致,应避免将可变状态存储在 Session 中,或者进行适当的同步控制。
特性 说明
核心作用 在无状态的 HTTP 协议上实现有状态的用户会话管理。
关键组件 HttpSession (服务器端), JSESSIONID (客户端 Cookie)。
基本操作 request.getSession(), setAttribute(), getAttribute(), removeAttribute(), invalidate()
生命周期 创建于首次访问,销毁于 invalidate()、超时或服务器重启。
框架集成 Spring MVC 中可直接注入 HttpSession,或使用 @SessionAttributes
适用场景 用户登录状态、购物车、权限验证等需要在多个页面间共享的用户数据。
替代方案 对于需要客户端存储少量数据且不敏感的场景,可以考虑使用 Cookie,对于大型分布式系统,JWT (JSON Web Token) 是一种更现代的无状态解决方案。

理解 Session 的原理和用法是 Java Web 开发的基础,它为你构建更复杂、更人性化的 Web 应用打下了坚实的基础。

分享:
扫描分享到社交APP
上一篇
下一篇