Spring Session的使用示例
session
http协议是无状态的,这样对于服务端来说,没有办法区分是新的访客还是旧的访客。但是,有些业务场景,需要追踪用户多个请求,此时就需要session。关于session的百度百科session
session:在计算机中,尤其是在网络应用中,称为“会话控制”。session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的web页之间跳转时,存储在session对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 web页时,如果该用户还没有会话,则web服务器将自动创建一个 session对象。当会话过期或被放弃后,服务器将终止该会话
核心特点:
- 服务端存储
- 会过期
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
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_attribution中的数据。
基于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的使用的资料请关注其它相关文章!