Spring Session的使用示例
Http協(xié)議是無狀態(tài)的,這樣對于服務端來說,沒有辦法區(qū)分是新的訪客還是舊的訪客。但是,有些業(yè)務場景,需要追蹤用戶多個請求,此時就需要Session。關于session的百度百科session
Session:在計算機中,尤其是在網(wǎng)絡應用中,稱為“會話控制”。Session對象存儲特定用戶會話所需的屬性及配置信息。這樣,當用戶在應用程序的Web頁之間跳轉時,存儲在Session對象中的變量將不會丟失,而是在整個用戶會話中一直存在下去。當用戶請求來自應用程序的 Web頁時,如果該用戶還沒有會話,則Web服務器將自動創(chuàng)建一個 Session對象。當會話過期或被放棄后,服務器將終止該會話
核心特點:
服務端存儲 會過期Session常用解決方案對于Session的常用解決方案,可以劃分為三種。
負載均衡方式借助負載均衡設備或者模塊,將指定的Session始終路由到同一臺機器即可,如Nginx。
副本復制方式利用服務器節(jié)點間的副本復制方式,保證集群所有節(jié)點擁有的Session數(shù)據(jù)一致。
集中存儲方式引入第三方存儲,將Session數(shù)據(jù)集中存儲到外部存儲中,如Redis或者數(shù)據(jù)庫等。
本文介紹的Spring-Session是采用第三種,集中存儲的方式。
Spring-Session核心組成模塊
Spring Session Core提供Spring Session核心的功能和API
Spring Session Data Redis提供基于Redis的SessionRepository以及配置
Spring Session JDBC提供基于關系型數(shù)據(jù)庫的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>
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文件
主要包含兩部分,數(shù)據(jù)庫的配置以及Spring Session Jdbc配置。
# 配置數(shù)據(jù)源相關內(nèi)容spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_learn?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=falsespring.datasource.username=xxxspring.datasource.password=xxxspring.datasource.initialization-mode=always# session類型選擇jdbcspring.session.store-type=jdbcspring.session.jdbc.initialize-schema=always# 指定表名#spring.session.jdbc.table-name=SESSIONS# 超時時間spring.session.timeout=180s
3.數(shù)據(jù)庫存儲解析
默認情況下,數(shù)據(jù)庫中會創(chuàng)建2張表。SPRING_SESSION和SPRING_SESSION_ATTRIBUTION。
SPRING_SESSION用于存在session自身的一些屬性,如創(chuàng)建時間、過期時間等,詳細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相關聯(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.測試執(zhí)行
SPRING_SESSION中的數(shù)據(jù)
SPRING_SESSION_ATTRIBUTION中的數(shù)據(jù)。
幾乎同樣的步驟
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=redisspring.redis.host=127.0.0.1spring.redis.port=6379
結果分析
一次請求后,多了三個屬性,分析如下。
key 類型 用途 value spring:session:sessions:expires:${sessionId} string 判斷sesssion是否存在 空 spring:session:sessions:${sessionId} hash session相關的屬性,包括有效期、創(chuàng)建時間、具體屬性等 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一定要觸發(fā)過期事件。關于這方面的解釋,可以看一下RedisIndexedSessionRepository中的注釋。
訂閱Spring-Session的相關事件
有些時候,我們比較關心Session的創(chuàng)建或者銷毀事件,做一些特殊的處理或者記錄。基于Redis的Spring-Session利用Spring Event將該事件發(fā)布出來,我們可以使用EventListener監(jiān)聽做處理。
@Component@Slf4jpublic 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提供了非常便利的,基于關系型數(shù)據(jù)庫或者Redis的Session解決方案。
Redis版訪問速度快,基于Redis的過期策略,保證過期數(shù)據(jù)會被刪除,同時支持事件訂閱。
數(shù)據(jù)庫版直接基于數(shù)據(jù)庫,無需單獨引入其他存儲。但是訪問速度相對較慢,過期數(shù)據(jù)需要依賴應用程序自身進行刪除。同時沒有提供事件訂閱能力。
以上就是Spring Session的使用示例的詳細內(nèi)容,更多關于Spring Session的使用的資料請關注好吧啦網(wǎng)其它相關文章!
相關文章:
1. python 如何在 Matplotlib 中繪制垂直線2. bootstrap select2 動態(tài)從后臺Ajax動態(tài)獲取數(shù)據(jù)的代碼3. ASP常用日期格式化函數(shù) FormatDate()4. python中@contextmanager實例用法5. html中的form不提交(排除)某些input 原創(chuàng)6. CSS3中Transition屬性詳解以及示例分享7. js select支持手動輸入功能實現(xiàn)代碼8. 如何通過python實現(xiàn)IOU計算代碼實例9. 開發(fā)效率翻倍的Web API使用技巧10. vue使用moment如何將時間戳轉為標準日期時間格式
