javascript
SpringSession实战项目(基于SpringBoot项目)【附源码】
Python實戰社群
Java實戰社群
長按識別下方二維碼,按需求添加
掃碼關注添加客服
進Python社群▲
掃碼關注添加客服
進Java社群▲
作者丨懷瑾握瑜
來源鏈接:
https://www.cnblogs.com/lxyit/p/9720159.html
spring-boot 整合 spring-session 的自動配置可謂是開箱即用,極其簡潔和方便。這篇文章即介紹 spring-boot 整合 spring-session,這里只介紹基于 RedisSession 的實戰。
考慮到 RedisSession 模塊與 spring-session v2.0.6 版本的差異很小,且能夠與 spring-boot v2.0.0 兼容,所以實戰篇是基于 spring-boot v2.0.0 基礎上配置 spring-session。
配置 spring-session
引入 spring-session 的 pom 配置,由于 spring-boot 包含 spring-session 的 starter 模塊,所以 pom 中依賴:
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId> </dependency>編寫 spring boot 啟動類 SessionExampleApplication
/***?啟動類**?@author?huaijin*/ @SpringBootApplication public?class?SessionExampleApplication?{public?static?void?main(String[]?args)?{SpringApplication.run(SessionExampleApplication.class,?args);} }配置 application.yml
spring:session:redis:flush-mode:?on_savenamespace:?session.examplecleanup-cron:?0?*?*?*?*?*store-type:?redistimeout:?1800redis:host:?localhostport:?6379jedis:pool:max-active:?100max-wait:?10max-idle:?10min-idle:?10database:?0編寫 controller
編寫登錄控制器,登錄時創建 session,并將當前登錄用戶存儲 sesion 中。登出時,使 session 失效。
/***?登錄控制器**?@author?huaijin*/ @RestController public?class?LoginController?{private?static?final?String?CURRENT_USER?=?"currentUser";/***?登錄**?@param?loginVo?登錄信息**?@author?huaijin*/@PostMapping("/login.do")public?String?login(@RequestBody?LoginVo?loginVo,?HttpServletRequest?request)?{UserVo?userVo?=?UserVo.builder().userName(loginVo.getUserName()).userPassword(loginVo.getUserPassword()).build();HttpSession?session?=?request.getSession();session.setAttribute(CURRENT_USER,?userVo);System.out.println("create?session,?sessionId?is:"?+?session.getId());return?"ok";}/***?登出**?@author?huaijin*/@PostMapping("/logout.do")public?String?logout(HttpServletRequest?request)?{HttpSession?session?=?request.getSession(false);session.invalidate();return?"ok";} }編寫查詢控制器,在登錄創建 session 后,使用將 sessionId 置于 cookie 中訪問。如果沒有 session 將返回錯誤。
/***?查詢**?@author?huaijin*/ @RestController @RequestMapping("/session") public?class?QuerySessionController?{@GetMapping("/query.do")public?String?querySessionId(HttpServletRequest?request)?{HttpSession?session?=?request.getSession(false);if?(session?==?null)?{return?"error";}System.out.println("current's?user?is:"?+?session.getId()?+??"in?session");return?"ok";} }編寫 Session 刪除事件監聽器
Session 刪除事件監聽器用于監聽登出時使 session 失效的事件源。
/***?session事件監聽器**?@author?huaijin*/ @Component public?class?SessionEventListener?implements?ApplicationListener<SessionDeletedEvent>?{private?static?final?String?CURRENT_USER?=?"currentUser";@Overridepublic?void?onApplicationEvent(SessionDeletedEvent?event)?{Session?session?=?event.getSession();UserVo?userVo?=?session.getAttribute(CURRENT_USER);System.out.println("invalid?session's?user:"?+?userVo.toString());} }驗證測試
編寫 spring-boot 測試類,測試 controller,驗證 spring-session 是否生效。
/***?測試Spring-Session:*?1.登錄時創建session*?2.使用sessionId能正常訪問*?3.session過期銷毀,能夠監聽銷毀事件**?@author?huaijin*/ @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public?class?SpringSessionTest?{@Autowiredprivate?MockMvc?mockMvc;@Testpublic?void?testLogin()?throws?Exception?{LoginVo?loginVo?=?new?LoginVo();loginVo.setUserName("admin");loginVo.setUserPassword("admin@123");String?content?=?JSON.toJSONString(loginVo);//?mock登錄ResultActions?actions?=?this.mockMvc.perform(post("/login.do").content(content).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string("ok"));String?sessionId?=?actions.andReturn().getResponse().getCookie("SESSION").getValue();//?使用登錄的sessionId?mock查詢this.mockMvc.perform(get("/session/query.do").cookie(new?Cookie("SESSION",?sessionId))).andExpect(status().isOk()).andExpect(content().string("ok"));//?mock登出this.mockMvc.perform(post("/logout.do").cookie(new?Cookie("SESSION",?sessionId))).andExpect(status().isOk()).andExpect(content().string("ok"));} }測試類執行結果:
create?session,?sessionId?is:429cb0d3-698a-475a-b3f1-09422acf2e9c current's?user?is:429cb0d3-698a-475a-b3f1-09422acf2e9cin?session invalid?session's?user:UserVo{userName='admin',?userPassword='admin@123'登錄時創建 Session,存儲當前登錄用戶。然后在以登錄響應返回的 SessionId 查詢用戶。最后再登出使 Session 過期。
spring-boot 整合 spring-session 自動配置原理
前兩篇文章介紹 spring-session 原理時,總結 spring-session 的核心模塊。這節中探索 spring-boot 中自動配置如何初始化 spring-session 的各個核心模塊。
spring-boot-autoconfigure 模塊中包含了 spinrg-session 的自動配置。包 org.springframework.boot.autoconfigure.session 中包含了 spring-session 的所有自動配置項。
其中 RedisSession 的核心配置項是 RedisHttpSessionConfiguration 類。
@Configuration @ConditionalOnClass({?RedisTemplate.class,?RedisOperationsSessionRepository.class?}) @ConditionalOnMissingBean(SessionRepository.class) @ConditionalOnBean(RedisConnectionFactory.class) @Conditional(ServletSessionCondition.class) @EnableConfigurationProperties(RedisSessionProperties.class) class?RedisSessionConfiguration?{@Configurationpublic?static?class?SpringBootRedisHttpSessionConfigurationextends?RedisHttpSessionConfiguration?{//?加載application.yml或者application.properties中自定義的配置項://?命名空間:用于作為session redis key的一部分// flushmode:session寫入redis的模式//?定時任務時間:即訪問redis過期鍵的定時任務的cron表達式@Autowiredpublic?void?customize(SessionProperties?sessionProperties,RedisSessionProperties?redisSessionProperties)?{Duration?timeout?=?sessionProperties.getTimeout();if?(timeout?!=?null)?{setMaxInactiveIntervalInSeconds((int)?timeout.getSeconds());}setRedisNamespace(redisSessionProperties.getNamespace());setRedisFlushMode(redisSessionProperties.getFlushMode());setCleanupCron(redisSessionProperties.getCleanupCron());}}}RedisSessionConfiguration 配置類中嵌套 SpringBootRedisHttpSessionConfiguration 繼承了 RedisHttpSessionConfiguration 配置類。首先看下該配置類持有的成員。
@Configuration @EnableScheduling public?class?RedisHttpSessionConfiguration?extends?SpringHttpSessionConfigurationimplements?BeanClassLoaderAware,?EmbeddedValueResolverAware,?ImportAware,SchedulingConfigurer?{//?默認的cron表達式,application.yml可以自定義配置static?final?String?DEFAULT_CLEANUP_CRON?=?"0?*?*?*?*?*";//?session的有效最大時間間隔,?application.yml可以自定義配置private?Integer?maxInactiveIntervalInSeconds?=?MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;//?session在redis中的命名空間,主要為了區分session,application.yml可以自定義配置private?String?redisNamespace?=?RedisOperationsSessionRepository.DEFAULT_NAMESPACE;//?session寫入Redis的模式,application.yml可以自定義配置private?RedisFlushMode?redisFlushMode?=?RedisFlushMode.ON_SAVE;//?訪問過期Session集合的定時任務的定時時間,默認是每整分運行任務private?String?cleanupCron?=?DEFAULT_CLEANUP_CRON;private?ConfigureRedisAction?configureRedisAction?=?new?ConfigureNotifyKeyspaceEventsAction();//?spring-data-redis的redis連接工廠private?RedisConnectionFactory?redisConnectionFactory;//?spring-data-redis的RedisSerializer,用于序列化session中存儲的attributesprivate?RedisSerializer<Object>?defaultRedisSerializer;//?session時間發布者,默認注入的是AppliationContext實例private?ApplicationEventPublisher?applicationEventPublisher;//?訪問過期session鍵的定時任務的調度器private?Executor?redisTaskExecutor;private?Executor?redisSubscriptionExecutor;private?ClassLoader?classLoader;private?StringValueResolver?embeddedValueResolver; }該配置類中初始化了 RedisSession 的最為核心模塊之一 RedisOperationsSessionRepository。
@Bean public?RedisOperationsSessionRepository?sessionRepository()?{//?創建RedisOperationsSessionRepositoryRedisTemplate<Object,?Object>?redisTemplate?=?createRedisTemplate();RedisOperationsSessionRepository?sessionRepository?=?new?RedisOperationsSessionRepository(redisTemplate);//?設置Session Event發布者。如果對此迷惑,傳送門:https://www.cnblogs.com/lxyit/p/9719542.htmlsessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);if?(this.defaultRedisSerializer?!=?null)?{sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);}//?設置默認的Session最大有效期間隔sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);//?設置命名空間if?(StringUtils.hasText(this.redisNamespace))?{sessionRepository.setRedisKeyNamespace(this.redisNamespace);}//?設置寫redis的模式sessionRepository.setRedisFlushMode(this.redisFlushMode);return?sessionRepository; }同時也初始化了 Session 事件監聽器 MessageListener 模塊
@Bean public?RedisMessageListenerContainer?redisMessageListenerContainer()?{//?創建MessageListener容器,這屬于spring-data-redis范疇,略過RedisMessageListenerContainer?container?=?new?RedisMessageListenerContainer();container.setConnectionFactory(this.redisConnectionFactory);if?(this.redisTaskExecutor?!=?null)?{container.setTaskExecutor(this.redisTaskExecutor);}if?(this.redisSubscriptionExecutor?!=?null)?{container.setSubscriptionExecutor(this.redisSubscriptionExecutor);}//?模式訂閱redis的__keyevent@*:expired和__keyevent@*:del通道,//?獲取redis的鍵過期和刪除事件通知container.addMessageListener(sessionRepository(),Arrays.asList(new?PatternTopic("__keyevent@*:del"),new?PatternTopic("__keyevent@*:expired")));//?模式訂閱redis的${namespace}:event:created:*通道,當該向該通道發布消息,//?則MessageListener消費消息并處理container.addMessageListener(sessionRepository(),Collections.singletonList(new?PatternTopic(sessionRepository().getSessionCreatedChannelPrefix()?+?"*")));return?container; }上篇文章中介紹到的 spring-session event 事件原理,spring-session 在啟動時監聽 Redis 的 channel,使用 Redis 的鍵空間通知處理 Session 的刪除和過期事件和使用 Pub/Sub 模式處理 Session 創建事件。
關于 RedisSession 的存儲管理部分已經初始化,但是 spring-session 的另一個基礎設施模塊 SessionRepositoryFilter 是在 RedisHttpSessionConfiguration 父類 SpringHttpSessionConfiguration 中初始化。
@Bean public?<S?extends?Session>?SessionRepositoryFilter<??extends?Session>?springSessionRepositoryFilter(SessionRepository<S>?sessionRepository)?{SessionRepositoryFilter<S>?sessionRepositoryFilter?=?new?SessionRepositoryFilter<>(sessionRepository);sessionRepositoryFilter.setServletContext(this.servletContext);sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);return?sessionRepositoryFilter; }spring-boot 整合 spring-session 配置的層次:
RedisSessionConfiguration|_?_?SpringBootRedisHttpSessionConfiguration|_?_?RedisHttpSessionConfiguration|_?_?SpringHttpSessionConfiguration回顧思考 spring-boot 自動配置 spring-session,非常合理。
SpringHttpSessionConfiguration 是 spring-session 本身的配置類,與 spring-boot 無關,畢竟 spring-session 也可以整合單純的 spring 項目,只需要使用該 spring-session 的配置類即可。
RedisHttpSessionConfiguration 用于配置 spring-session 的 Redission,畢竟 spring-session 還支持其他的各種 session:Map/JDBC/MogonDB 等,將其從 SpringHttpSessionConfiguration 隔離開來,遵循開閉原則和接口隔離原則。但是其必須依賴基礎的 SpringHttpSessionConfiguration,所以使用了繼承。RedisHttpSessionConfiguration 是 spring-session 和 spring-data-redis 整合配置,需要依賴 spring-data-redis。
SpringBootRedisHttpSessionConfiguration 才是 spring-boot 中關鍵配置
RedisSessionConfiguration 主要用于處理自定義配置,將 application.yml 或者 application.properties 的配置載入。
Tips:
配置類也有相當強的設計模式。遵循開閉原則:對修改關閉,對擴展開放。遵循接口隔離原則:變化的就要單獨分離,使用不同的接口隔離。SpringHttpSessionConfiguration 和 RedisHttpSessionConfiguration 的設計深深體現這兩大原則。
參考
Spring Session參考文:https://spring.io/projects/spring-session#samples
本文例子源碼
https://github.com/lixyou/spring-boot-example/tree/master/session-example
程序員專欄?掃碼關注填加客服?長按識別下方二維碼進群近期精彩內容推薦:??
?外包程序員入職螞蟻金服被質疑,網友:人生污點
?11?月全國程序員平均工資出爐
?棄用 Notepad++,還有 5 款更牛逼的選擇!
?福利!手把手教你Python爬取女神套圖
在看點這里好文分享給更多人↓↓
總結
以上是生活随笔為你收集整理的SpringSession实战项目(基于SpringBoot项目)【附源码】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单工厂模式-工厂模式-抽象工厂模式类图
- 下一篇: Spring官方文档(中文版!!!)