欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring Session的使用示例

程序员文章站 2022-03-10 20:47:56
目录sessionsession常用解决方案spring-session基于数据库的spring-session基于redis的spring-sessionsessionhttp协议是无状态的,这样对...

session

http协议是无状态的,这样对于服务端来说,没有办法区分是新的访客还是旧的访客。但是,有些业务场景,需要追踪用户多个请求,此时就需要session。关于session的百度百科session

session:在计算机中,尤其是在网络应用中,称为“会话控制”。session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的web页之间跳转时,存储在session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 web页时,如果该用户还没有会话,则web服务器将自动创建一个 session对象。当会话过期或被放弃后,服务器将终止该会话

核心特点:

  1. 服务端存储
  2. 会过期

session常用解决方案

对于session的常用解决方案,可以划分为三种。

  • 负载均衡方式

借助负载均衡设备或者模块,将指定的session始终路由到同一台机器即可,如nginx。

  • 副本复制方式

利用服务器节点间的副本复制方式,保证集群所有节点拥有的session数据一致。

  • 集中存储方式

引入第三方存储,将session数据集中存储到外部存储中,如redis或者数据库等。

本文介绍的spring-session是采用第三种,集中存储的方式。

spring-session

核心组成模块

  • spring session core

提供spring session核心的功能和api

  • spring session data redis

提供基于redis的sessionrepository以及配置

  • spring session jdbc

提供基于关系型数据库的sessionrepository以及配置

  • spring session hazelcast

提供基于hazelcast的sessionrepository以及配置

测试代码

controller提供三个接口,分别对应session的获取、保存和清理

@getmapping("/")
public string process(model model, httpsession session) {
  @suppresswarnings("unchecked")
  list<string> messages = (list<string>) session.getattribute("springsession");

  if (messages == null) {
    messages = new arraylist<>();
  }
  model.addattribute("sessionmessages", messages);

  return "sessiontest";
}

@postmapping("/persistsession")
public string persistmessage(@requestparam("msg") string msg, httpservletrequest request) {
  @suppresswarnings("unchecked")
  list<string> messages = (list<string>) request.getsession().getattribute("springsession");
  if (messages == null) {
    messages = new arraylist<>();
    request.getsession().setattribute("springsession", messages);
  }
  messages.add(msg);
  request.getsession().setattribute("springsession", messages);
  return "redirect:/";
}

@postmapping("/destroysession")
public string destroysession(httpservletrequest request) {
  request.getsession().invalidate();
  return "redirect:/";
}

sessiontest.html对应页面操作

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <title>spring boot session example</title>
</head>
<body>
<div>
    <form th:action="@{/persistsession}" method="post">
        <textarea name="msg" cols="40" rows="2"></textarea>
        <br> <input type="submit" value="保存" />
    </form>
</div>
<div>
    <h2>session列表</h2>
    <ul th:each="message : ${sessionmessages}">
        <li th:text="${message}">message</li>
    </ul>
</div>

<div>
    <form th:action="@{/destroysession}" method="post">
        <input type="submit" value="清空" />
    </form>
</div>
</body>
</html>

Spring Session的使用示例

基于数据库的spring-session

1.引入maven依赖

使用mysql存储,所以引入了mysql。

涉及到springboot jdbc的配置,引入了spring boot jdbc starter。

<dependency>
  <groupid>org.springframework.session</groupid>
  <artifactid>spring-session-core</artifactid>
  <version>2.5.0</version>
</dependency>

<dependency>
  <groupid>org.springframework.session</groupid>
  <artifactid>spring-session-jdbc</artifactid>
  <version>2.5.0</version>
</dependency>

<dependency>
  <groupid>mysql</groupid>
  <artifactid>mysql-connector-java</artifactid>
  <version>8.0.18</version>
</dependency>

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-jdbc</artifactid>
</dependency>

注意:

no session repository could be auto-configured, check your configuration (session store type is 'jdbc')

如果存在这个报错,是因为没有引入spring-boot-starter-jdbc,引入即可。

2.配置application.properties文件

主要包含两部分,数据库的配置以及spring session jdbc配置。

# 配置数据源相关内容
spring.datasource.driver-class-name=com.mysql.cj.jdbc.driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_learn?createdatabaseifnotexist=true&autoreconnect=true&usessl=false
spring.datasource.username=xxx
spring.datasource.password=xxx
spring.datasource.initialization-mode=always

# session类型选择jdbc
spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=always
# 指定表名
#spring.session.jdbc.table-name=sessions
# 超时时间
spring.session.timeout=180s

3.数据库存储解析

默认情况下,数据库中会创建2张表。spring_session和spring_session_attribution。

spring_session用于存在session自身的一些属性,如创建时间、过期时间等,详细schema如下。

create table `spring_session` (
  `primary_id` char(36) not null,
  `session_id` char(36) not null,
  `creation_time` bigint not null,
  `last_access_time` bigint not null,
  `max_inactive_interval` int not null,
  `expiry_time` bigint not null,
  `principal_name` varchar(100) default null,
  primary key (`primary_id`),
  unique key `spring_session_ix1` (`session_id`),
  key `spring_session_ix2` (`expiry_time`),
  key `spring_session_ix3` (`principal_name`)
) engine=innodb default charset=utf8mb4 collate=utf8mb4_0900_ai_ci row_format=dynamic

spring_session_attribution用于存储session相关联的属性,schema如下。

create table `spring_session_attributes` (
  `session_primary_id` char(36) not null,
  `attribute_name` varchar(200) not null,
  `attribute_bytes` blob not null,
  primary key (`session_primary_id`,`attribute_name`),
  constraint `spring_session_attributes_fk` foreign key (`session_primary_id`) references `spring_session` (`primary_id`) on delete cascade
) engine=innodb default charset=utf8mb4 collate=utf8mb4_0900_ai_ci row_format=dynamic

4.测试执行

spring_session中的数据

Spring Session的使用示例

spring_session_attribution中的数据。

Spring Session的使用示例

基于redis的spring-session

几乎同样的步骤

maven依赖

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>

<dependency>
  <groupid>org.springframework.session</groupid>
  <artifactid>spring-session-data-redis</artifactid>
</dependency>

application.properties配置

spring.session.store-type=redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

结果分析

一次请求后,多了三个属性,分析如下。

key 类型 用途 value
spring:session:sessions:expires:${sessionid} string 判断sesssion是否存在
spring:session:sessions:${sessionid} hash session相关的属性,包括有效期、创建时间、具体属性等 creationtime/lastaccessedtime/sessionattr/maxinactiveinterval
spring:session:expirations:1623656160000 set 存储待过期的sessionid列表 key: 过期的时间戳;value: 在这个时间戳将要过期的expire key列表。

在访问时,先通过第一个key,判断session是否存在以及是否过期。如果没有过期,可以通过第二个key获取或者更新对应的session详情。

对于第三个key,实际上spring-session-redis会有特殊的用途,主要是为了redis的keyspace-notificationhttps://redis.io/topics/notifications。核心目的是为了确保过期的session一定要触发过期事件。关于这方面的解释,可以看一下redisindexedsessionrepository中的注释。

订阅spring-session的相关事件

有些时候,我们比较关心session的创建或者销毁事件,做一些特殊的处理或者记录。基于redis的spring-session利用spring event将该事件发布出来,我们可以使用eventlistener监听做处理。

@component
@slf4j
public class annotationdriveneventlistener {

  @eventlistener
  public void handlesessioncreated(sessioncreatedevent sessioncreatedevent) {
    string sessionid = sessioncreatedevent.getsessionid();
    log.info("session id:{} created", sessionid);
  }

  @eventlistener
  public void handlesessiondestroyed(sessiondestroyedevent sessiondestroyedevent) {
    string sessionid = sessiondestroyedevent.getsessionid();
    log.info("session id:{} destroyed", sessionid);
  }
}

总结

spring session提供了非常便利的,基于关系型数据库或者redis的session解决方案。

redis版访问速度快,基于redis的过期策略,保证过期数据会被删除,同时支持事件订阅。

数据库版直接基于数据库,无需单独引入其他存储。但是访问速度相对较慢,过期数据需要依赖应用程序自身进行删除。同时没有提供事件订阅能力。

以上就是spring session的使用示例的详细内容,更多关于spring session的使用的资料请关注其它相关文章!

相关标签: Spring Session