Spring Session的使用示例
Session
Http協(xié)議是無(wú)狀態(tài)的,這樣對(duì)于服務(wù)端來(lái)說(shuō),沒(méi)有辦法區(qū)分是新的訪客還是舊的訪客。但是,有些業(yè)務(wù)場(chǎng)景,需要追蹤用戶多個(gè)請(qǐng)求,此時(shí)就需要Session。關(guān)于session的百度百科session
Session:在計(jì)算機(jī)中,尤其是在網(wǎng)絡(luò)應(yīng)用中,稱(chēng)為“會(huì)話控制”。Session對(duì)象存儲(chǔ)特定用戶會(huì)話所需的屬性及配置信息。這樣,當(dāng)用戶在應(yīng)用程序的Web頁(yè)之間跳轉(zhuǎn)時(shí),存儲(chǔ)在Session對(duì)象中的變量將不會(huì)丟失,而是在整個(gè)用戶會(huì)話中一直存在下去。當(dāng)用戶請(qǐng)求來(lái)自應(yīng)用程序的 Web頁(yè)時(shí),如果該用戶還沒(méi)有會(huì)話,則Web服務(wù)器將自動(dòng)創(chuàng)建一個(gè) Session對(duì)象。當(dāng)會(huì)話過(guò)期或被放棄后,服務(wù)器將終止該會(huì)話
核心特點(diǎn):
- 服務(wù)端存儲(chǔ)
- 會(huì)過(guò)期
Session常用解決方案
對(duì)于Session的常用解決方案,可以劃分為三種。
- 負(fù)載均衡方式
借助負(fù)載均衡設(shè)備或者模塊,將指定的Session始終路由到同一臺(tái)機(jī)器即可,如Nginx。
- 副本復(fù)制方式
利用服務(wù)器節(jié)點(diǎn)間的副本復(fù)制方式,保證集群所有節(jié)點(diǎn)擁有的Session數(shù)據(jù)一致。
- 集中存儲(chǔ)方式
引入第三方存儲(chǔ),將Session數(shù)據(jù)集中存儲(chǔ)到外部存儲(chǔ)中,如Redis或者數(shù)據(jù)庫(kù)等。
本文介紹的Spring-Session是采用第三種,集中存儲(chǔ)的方式。
Spring-Session
核心組成模塊
- Spring Session Core
提供Spring Session核心的功能和API
- Spring Session Data Redis
提供基于Redis的SessionRepository以及配置
- Spring Session JDBC
提供基于關(guān)系型數(shù)據(jù)庫(kù)的SessionRepository以及配置
- Spring Session Hazelcast
提供基于Hazelcast的SessionRepository以及配置
測(cè)試代碼
controller提供三個(gè)接口,分別對(duì)應(yīng)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對(duì)應(yīng)頁(yè)面操作
<!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>
基于數(shù)據(jù)庫(kù)的Spring-Session
1.引入maven依賴(lài)
使用MySQL存儲(chǔ),所以引入了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')
如果存在這個(gè)報(bào)錯(cuò),是因?yàn)闆](méi)有引入spring-boot-starter-jdbc,引入即可。
2.配置application.properties文件
主要包含兩部分,數(shù)據(jù)庫(kù)的配置以及Spring Session Jdbc配置。
# 配置數(shù)據(jù)源相關(guān)內(nèi)容 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類(lèi)型選擇jdbc spring.session.store-type=jdbc spring.session.jdbc.initialize-schema=always # 指定表名 #spring.session.jdbc.table-name=SESSIONS # 超時(shí)時(shí)間 spring.session.timeout=180s
3.數(shù)據(jù)庫(kù)存儲(chǔ)解析
默認(rèn)情況下,數(shù)據(jù)庫(kù)中會(huì)創(chuàng)建2張表。SPRING_SESSION和SPRING_SESSION_ATTRIBUTION。
SPRING_SESSION用于存在session自身的一些屬性,如創(chuàng)建時(shí)間、過(guò)期時(shí)間等,詳細(xì)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用于存儲(chǔ)session相關(guān)聯(lián)的屬性,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.測(cè)試執(zhí)行
SPRING_SESSION中的數(shù)據(jù)
SPRING_SESSION_ATTRIBUTION中的數(shù)據(jù)。
基于Redis的Spring-Session
幾乎同樣的步驟
maven依賴(lài)
<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
結(jié)果分析
一次請(qǐng)求后,多了三個(gè)屬性,分析如下。
key | 類(lèi)型 | 用途 | value |
---|---|---|---|
spring:session:sessions:expires:${sessionId} | string | 判斷sesssion是否存在 | 空 |
spring:session:sessions:${sessionId} | hash | session相關(guān)的屬性,包括有效期、創(chuàng)建時(shí)間、具體屬性等 | creationTime/lastAccessedTime/sessionAttr/maxInactiveInterval |
spring:session:expirations:1623656160000 | set | 存儲(chǔ)待過(guò)期的sessionId列表 | key: 過(guò)期的時(shí)間戳;value: 在這個(gè)時(shí)間戳將要過(guò)期的expire key列表。 |
在訪問(wèn)時(shí),先通過(guò)第一個(gè)key,判斷session是否存在以及是否過(guò)期。如果沒(méi)有過(guò)期,可以通過(guò)第二個(gè)key獲取或者更新對(duì)應(yīng)的session詳情。
對(duì)于第三個(gè)key,實(shí)際上Spring-Session-Redis會(huì)有特殊的用途,主要是為了Redis的keySpace-notificationhttps://redis.io/topics/notifications。核心目的是為了確保過(guò)期的session一定要觸發(fā)過(guò)期事件。關(guān)于這方面的解釋?zhuān)梢钥匆幌翿edisIndexedSessionRepository中的注釋。
訂閱Spring-Session的相關(guān)事件
有些時(shí)候,我們比較關(guān)心Session的創(chuàng)建或者銷(xiāo)毀事件,做一些特殊的處理或者記錄。基于Redis的Spring-Session利用Spring Event將該事件發(fā)布出來(lái),我們可以使用EventListener監(jiān)聽(tīng)做處理。
@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); } }
總結(jié)
Spring Session提供了非常便利的,基于關(guān)系型數(shù)據(jù)庫(kù)或者Redis的Session解決方案。
Redis版訪問(wèn)速度快,基于Redis的過(guò)期策略,保證過(guò)期數(shù)據(jù)會(huì)被刪除,同時(shí)支持事件訂閱。
數(shù)據(jù)庫(kù)版直接基于數(shù)據(jù)庫(kù),無(wú)需單獨(dú)引入其他存儲(chǔ)。但是訪問(wèn)速度相對(duì)較慢,過(guò)期數(shù)據(jù)需要依賴(lài)應(yīng)用程序自身進(jìn)行刪除。同時(shí)沒(méi)有提供事件訂閱能力。
以上就是Spring Session的使用示例的詳細(xì)內(nèi)容,更多關(guān)于Spring Session的使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 淺談Spring Session工作原理
- SpringBoot+SpringSession+Redis實(shí)現(xiàn)session共享及唯一登錄示例
- SpringCloud Feign轉(zhuǎn)發(fā)請(qǐng)求頭(防止session失效)的解決方案
- springboot中的springSession的存儲(chǔ)和獲取實(shí)現(xiàn)
- 多個(gè)SpringBoot項(xiàng)目采用redis實(shí)現(xiàn)Session共享功能
- SpringBoot 如何實(shí)現(xiàn)Session共享
- Springboot中登錄后關(guān)于cookie和session攔截問(wèn)題的案例分析
- Springboot Session共享實(shí)現(xiàn)原理及代碼實(shí)例
- SpringBoot中實(shí)現(xiàn)分布式的Session共享的詳細(xì)教程
- spring-redis-session 自定義 key 和過(guò)期時(shí)間
- SpringBoot2.x 整合Spring-Session實(shí)現(xiàn)Session共享功能
相關(guān)文章
淺析JavaMail發(fā)送郵件后再通過(guò)JavaMail接收格式問(wèn)題
這篇文章主要介紹了JavaMail發(fā)送郵件后再通過(guò)JavaMail接收格式問(wèn)題 ,本文通過(guò)代碼實(shí)例給大家詳細(xì)解說(shuō),需要的朋友可以參考下2019-06-06Java線性結(jié)構(gòu)中的雙向鏈表實(shí)現(xiàn)原理
這篇文章將給大家詳細(xì)講解雙向鏈表的內(nèi)容,尤其是會(huì)通過(guò)代碼來(lái)進(jìn)行鏈表的操作,文中的代碼示例介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2023-07-07SpringBoot+vue實(shí)現(xiàn)登錄圖片驗(yàn)證碼功能
這篇文章主要給大家介紹一下如何SpringBoot+vue實(shí)現(xiàn)登錄圖片驗(yàn)證碼功能,文中有詳細(xì)的代碼示例,具有一定的參考價(jià)值,需要的朋友可以參考下2023-07-07IntelliJ IDEA設(shè)置JVM運(yùn)行參數(shù)的操作方法
這篇文章主要介紹了IntelliJ IDEA設(shè)置JVM運(yùn)行參數(shù)的操作方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-03-03IDEA無(wú)法使用終端terminal問(wèn)題的解決方案
這篇文章主要介紹了IDEA無(wú)法使用終端terminal問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09SpringCLoud搭建Zuul網(wǎng)關(guān)集群過(guò)程解析
這篇文章主要介紹了SpringCLoud搭建Zuul網(wǎng)關(guān)集群過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03使用FeignClient進(jìn)行微服務(wù)交互方式(微服務(wù)接口互相調(diào)用)
這篇文章主要介紹了使用FeignClient進(jìn)行微服務(wù)交互方式(微服務(wù)接口互相調(diào)用),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03java system類(lèi)使用方法示例 獲取系統(tǒng)信息
這篇文章主要介紹了java system類(lèi)使用方法,該類(lèi)中的方法都是靜態(tài)的。不能被實(shí)例化,沒(méi)有對(duì)外提供構(gòu)造函數(shù),該類(lèi)可以獲取系統(tǒng)信息2014-01-01詳解Java類(lèi)動(dòng)態(tài)加載和熱替換
本文主要介紹類(lèi)加載器、自定義類(lèi)加載器及類(lèi)的加載和卸載等內(nèi)容,并舉例介紹了Java類(lèi)的熱替換。2021-05-05