SpringSession實戰(zhàn)項目(基于SpringBoot項目)【附源碼】
spring-boot 整合 spring-session 的自動配置可謂是開箱即用,極其簡潔和方便。這篇文章即介紹 spring-boot 整合 spring-session,這里只介紹基于 RedisSession 的實戰(zhàn)。
考慮到 RedisSession 模塊與 spring-session v2.0.6 版本的差異很小,且能夠與 spring-boot v2.0.0 兼容,所以實戰(zhàn)篇是基于 spring-boot v2.0.0 基礎(chǔ)上配置 spring-session。
配置 spring-session
引入 spring-session 的 pom 配置,由于 spring-boot 包含 spring-session 的 starter 模塊,所以 pom 中依賴:
?org.springframework.session
?spring-session-data-redis
編寫 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_save
??????namespace:?session.example
??????cleanup-cron:?0?*?*?*?*?*
????store-type:?redis
????timeout:?1800
??redis:
????host:?localhost
????port:?6379
????jedis:
??????pool:
????????max-active:?100
????????max-wait:?10
????????max-idle:?10
????????min-idle:?10
????database:?0
編寫 controller
編寫登錄控制器,登錄時創(chuàng)建 session,并將當(dāng)前登錄用戶存儲 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";
????}
}
編寫查詢控制器,在登錄創(chuàng)建 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 刪除事件監(jiān)聽器
Session 刪除事件監(jiān)聽器用于監(jiān)聽登出時使 session 失效的事件源。
/**
?*?session事件監(jiān)聽器
?*
?*?@author?huaijin
?*/
@Component
public?class?SessionEventListener?implements?ApplicationListener?{
????private?static?final?String?CURRENT_USER?=?"currentUser";
????@Override
????public?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.登錄時創(chuàng)建session
?*?2.使用sessionId能正常訪問
?*?3.session過期銷毀,能夠監(jiān)聽銷毀事件
?*
?*?@author?huaijin
?*/
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public?class?SpringSessionTest?{
????@Autowired
????private?MockMvc?mockMvc;
????@Test
????public?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"));
????}
}
測試類執(zhí)行結(jié)果:
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'
登錄時創(chuàng)建 Session,存儲當(dāng)前登錄用戶。然后在以登錄響應(yīng)返回的 SessionId 查詢用戶。最后再登出使 Session 過期。
spring-boot 整合 spring-session 自動配置原理
前兩篇文章介紹 spring-session 原理時,總結(jié) spring-session 的核心模塊。這節(jié)中探索 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?{
?@Configuration
?public?static?class?SpringBootRedisHttpSessionConfiguration
???extends?RedisHttpSessionConfiguration?{
??//?加載application.yml或者application.properties中自定義的配置項:
??//?命名空間:用于作為session redis key的一部分
??// flushmode:session寫入redis的模式
??//?定時任務(wù)時間:即訪問redis過期鍵的定時任務(wù)的cron表達(dá)式
??@Autowired
??public?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?SpringHttpSessionConfiguration
??implements?BeanClassLoaderAware,?EmbeddedValueResolverAware,?ImportAware,
??SchedulingConfigurer?{
?//?默認(rèn)的cron表達(dá)式,application.yml可以自定義配置
?static?final?String?DEFAULT_CLEANUP_CRON?=?"0?*?*?*?*?*";
?//?session的有效最大時間間隔,?application.yml可以自定義配置
?private?Integer?maxInactiveIntervalInSeconds?=?MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS;
?//?session在redis中的命名空間,主要為了區(qū)分session,application.yml可以自定義配置
?private?String?redisNamespace?=?RedisOperationsSessionRepository.DEFAULT_NAMESPACE;
?//?session寫入Redis的模式,application.yml可以自定義配置
?private?RedisFlushMode?redisFlushMode?=?RedisFlushMode.ON_SAVE;
?//?訪問過期Session集合的定時任務(wù)的定時時間,默認(rèn)是每整分運(yùn)行任務(wù)
?private?String?cleanupCron?=?DEFAULT_CLEANUP_CRON;
?private?ConfigureRedisAction?configureRedisAction?=?new?ConfigureNotifyKeyspaceEventsAction();
?//?spring-data-redis的redis連接工廠
?private?RedisConnectionFactory?redisConnectionFactory;
?//?spring-data-redis的RedisSerializer,用于序列化session中存儲的attributes
?private?RedisSerializer該配置類中初始化了 RedisSession 的最為核心模塊之一 RedisOperationsSessionRepository。
@Bean
public?RedisOperationsSessionRepository?sessionRepository()?{
?//?創(chuàng)建RedisOperationsSessionRepository
?RedisTemplate同時也初始化了 Session 事件監(jiān)聽器 MessageListener 模塊
@Bean
public?RedisMessageListenerContainer?redisMessageListenerContainer()?{
?//?創(chuàng)建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:*通道,當(dāng)該向該通道發(fā)布消息,
?//?則MessageListener消費消息并處理
?container.addMessageListener(sessionRepository(),
???Collections.singletonList(new?PatternTopic(
?????sessionRepository().getSessionCreatedChannelPrefix()?+?"*")));
?return?container;
}
上篇文章中介紹到的 spring-session event 事件原理,spring-session 在啟動時監(jiān)聽 Redis 的 channel,使用 Redis 的鍵空間通知處理 Session 的刪除和過期事件和使用 Pub/Sub 模式處理 Session 創(chuàng)建事件。
關(guān)于 RedisSession 的存儲管理部分已經(jīng)初始化,但是 spring-session 的另一個基礎(chǔ)設(shè)施模塊 SessionRepositoryFilter 是在 RedisHttpSessionConfiguration 父類 SpringHttpSessionConfiguration 中初始化。
@Bean
public??SessionRepositoryFilter?extends?Session>?springSessionRepositoryFilter(
??SessionRepository?sessionRepository)?{
?SessionRepositoryFilter?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 無關(guān),畢竟 spring-session 也可以整合單純的 spring 項目,只需要使用該 spring-session 的配置類即可。 RedisHttpSessionConfiguration 用于配置 spring-session 的 Redission,畢竟 spring-session 還支持其他的各種 session:Map/JDBC/MogonDB 等,將其從 SpringHttpSessionConfiguration 隔離開來,遵循開閉原則和接口隔離原則。但是其必須依賴基礎(chǔ)的 SpringHttpSessionConfiguration,所以使用了繼承。RedisHttpSessionConfiguration 是 spring-session 和 spring-data-redis 整合配置,需要依賴 spring-data-redis。 SpringBootRedisHttpSessionConfiguration 才是 spring-boot 中關(guān)鍵配置 RedisSessionConfiguration 主要用于處理自定義配置,將 application.yml 或者 application.properties 的配置載入。
“Tips:
”
配置類也有相當(dāng)強(qiáng)的設(shè)計模式。遵循開閉原則:對修改關(guān)閉,對擴(kuò)展開放。遵循接口隔離原則:變化的就要單獨分離,使用不同的接口隔離。SpringHttpSessionConfiguration 和 RedisHttpSessionConfiguration 的設(shè)計深深體現(xiàn)這兩大原則。
參考
Spring Session參考文:https://spring.io/projects/spring-session#samples
本文例子源碼
https://github.com/lixyou/spring-boot-example/tree/master/session-example
作者:懷瑾握瑜
來源鏈接:
https://www.cnblogs.com/lxyit/p/9720159.html
最近熬夜給大家準(zhǔn)備了515套Java代碼,有一些是業(yè)務(wù)類的小項目,比如Java博客項目,也有腳手架、也有平時用一些的工具類、21套小程序代碼,也有一些游戲類的項目。
掃以下二維碼并回復(fù)“828”即可獲取
或者在本公眾號對話框回復(fù)【828】馬上獲取

