Skip to content

会话


基本使用

获取 Session

java
@RestController
public class SessionController {

    @GetMapping("/set")
    public String setSession(HttpServletRequest request) {
        HttpSession session = request.getSession();    // 类比 session_start()
        session.setAttribute("user_id", 123);           // 类比 $_SESSION['user_id'] = 123
        return "set";
    }

    @GetMapping("/get")
    public String getSession(HttpServletRequest request) {
        HttpSession session = request.getSession(false); // 不存在的 session 返回 null
        if (session != null) {
            Integer userId = (Integer) session.getAttribute("user_id");
            //                 ⬆ 需要强制类型转换(Java 静态类型)
            return "user_id: " + userId;
        }
        return "no session";
    }

    @GetMapping("/clear")
    public String clearSession(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();  // 类比 session_destroy()
        }
        return "cleared";
    }
}

通过 @SessionAttribute 注入

java
@RestController
public class CartController {

    @GetMapping("/cart")
    public String cart(@SessionAttribute(name = "cart", required = false) List<Item> cart) {
        // 直接从 Session 中取 cart 属性,不存在则返回 null
        if (cart == null) {
            return "cart is empty";
        }
        return "cart has " + cart.size() + " items";
    }
}

⚠️ Session 存储配置

默认 Session 存储在 Tomcat 内存中。

yaml
# application.yml
server:
  servlet:
    session:
      timeout: 30m           # 超时时间,类比 SESSION_LIFETIME
      cookie:
        name: MYAPP_SESSION  # 类比 session_name()
        http-only: true
        secure: false

生产环境注意:默认的 Tomcat 内存 Session 有两个大问题:

  1. 应用重启后 Session 丢失(类比 PHP-FPM 重启后内存 Session 丢失)
  2. 多实例部署时 Session 不同步(需要粘性会话或外部存储)

解决方案:用 Redis 存储 Session(需添加 spring-session-data-redis 依赖):

yaml
spring:
  session:
    store-type: redis  # 类比 Laravel SESSION_DRIVER=redis

加完依赖和配置后,代码完全不需要改动,Spring Session 自动替换底层存储。


⚠️ 重要差异

1. PHP 中 session_start() 之后可以读写任意数据,Java 的 Session 也类似,但取值时要强转类型

php
// PHP:随便放,随便取
$_SESSION['count'] = 0;
$_SESSION['user'] = User::find(1);
$count = $_SESSION['count'];
java
// Java:放时是 Object,取时要强转
session.setAttribute("count", 0);
session.setAttribute("user", userService.findById(1));
int count = (int) session.getAttribute("count");          // 强转
User user = (User) session.getAttribute("user");

这是静态类型导致的结果——getAttribute() 的返回类型是 Object,编译器不知道具体是什么类型,需要开发者告诉它。

2. 并发安全问题

php
// PHP:每个请求独立进程,不用担心
$_SESSION['count'] = ($_SESSION['count'] ?? 0) + 1;
java
// Java:多线程共享 session,存在并发问题
Integer count = (Integer) session.getAttribute("count");
if (count == null) count = 0;
session.setAttribute("count", count + 1);  // 并发情况会丢数据

如果在 Session 中做累加这类操作,需要加同步锁:

java
synchronized (session.getId().intern()) {
    Integer count = (Integer) session.getAttribute("count");
    session.setAttribute("count", (count == null ? 0 : count) + 1);
}

更推荐的做法是避免在 Session 中存可变数据,把状态放到数据库或 Redis 中。

3. 没有全局的 session() 辅助函数

PHP 有 session() / Session::get(),Java 没有类似的静态方法。Session 只能通过方法参数注入获取:

java
public String foo(HttpSession session) { ... }

或通过 @SessionAttribute 注解。

4. Session ID 的获取方式

java
String sessionId = request.getSession().getId();
// 类比 PHP 的 session_id()

面向 PHP 开发者的 Spring Boot 文档