日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

从@EnableRedisHttpSession谈谈Spring Session实现原理

發布時間:2025/3/15 javascript 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从@EnableRedisHttpSession谈谈Spring Session实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、EnableRedisHttpSession使用

  • 添加依賴
  • <!-- spring session的依賴 --> <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId> </dependency>
  • 添加注解 @EnableRedisHttpSession
  • /*** session托管到redis*/ @Configuration @EnableRedisHttpSession(maxInactiveIntervalInSeconds= 3600*24, redisFlushMode = RedisFlushMode.ON_SAVE, redisNamespace = "aurora-web") public class RedisSessionConfig {}

    maxInactiveIntervalInSeconds: 設置 Session 失效時間,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 屬性不再生效。

  • 經過上面的配置后,Session調用就會自動去Redis存取。另外,想要達到Session共享的目的,只需要在其他的系統上做同樣的配置即可。開啟spring session的功能就是這么簡單,根據定律:使用越簡單,源碼
  • 二、@EnableRedisHttpSession源碼

    @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({RedisHttpSessionConfiguration.class}) @Configuration public @interface EnableRedisHttpSession {//Session默認過期時間,單位秒,默認1800秒int maxInactiveIntervalInSeconds() default 1800;//配置key的namespace,默認的是spring:session,如果不同的應用共用一個redis,應該為應用配置不同的namespace,這樣才能區分這個Session是來自哪個應用的String redisNamespace() default "spring:session";//配置刷新Redis中Session方式,默認是ON_SAVE模式,只有當Response提交后才會將Session提交到Redis,也可以配置成IMMEDIATE模式,即所有對Session的更改會立即更新到RedisRedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;//清理過期Session的定時任務String cleanupCron() default "0 * * * * *"; }
  • 其中,@Import({RedisHttpSessionConfiguration.class}) 這個注解的主要作用是注冊一個SessionRepositoryFilter,這個Filter會攔截到所有的請求,對Session進行操作,具體的操作細節會在后面講解,這邊主要了解這個注解的作用是注冊SessionRepositoryFilter就行了。注入SessionRepositoryFilter的代碼在RedisHttpSessionConfiguration這個類中。
  • @Configuration @EnableScheduling public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware, SchedulingConfigurer {...... }
  • RedisHttpSessionConfiguration 繼承了 SpringHttpSessionConfiguration,SpringHttpSessionConfiguration 中注冊了 SessionRepositoryFilter。見下面代碼。
  • @Configuration public class SpringHttpSessionConfiguration implements ApplicationContextAware {...@Beanpublic <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;}... }
  • 我們發現注冊 SessionRepositoryFilter 時需要一個 SessionRepository 參數,這個參數是在 RedisHttpSessionConfiguration 中被注入進入的。
  • @Configuration @EnableScheduling public class RedisHttpSessionConfiguration extends SpringHttpSessionConfigurationimplements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware,SchedulingConfigurer {@Beanpublic RedisOperationsSessionRepository sessionRepository() {RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();RedisOperationsSessionRepository sessionRepository = new RedisOperationsSessionRepository(redisTemplate);sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);if (this.defaultRedisSerializer != null) {sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);}sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);if (StringUtils.hasText(this.redisNamespace)) {sessionRepository.setRedisKeyNamespace(this.redisNamespace);}sessionRepository.setRedisFlushMode(this.redisFlushMode);int database = resolveDatabase();sessionRepository.setDatabase(database);return sessionRepository;} }

    上面主要講的就是 Spring-Session 會自動注冊一個 SessionRepositoryFilter ,這個過濾器會攔截所有的請求。下面就具體看下這個過濾器對攔截下來的請求做了哪些操作。SessionRepositoryFilter 攔截到請求后,會先將 request 和 response 對象轉換成 Spring 內部的包裝類 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 對象。SessionRepositoryRequestWrapper 類重寫了原生的getSession方法。

    @Override public HttpSessionWrapper getSession(boolean create) {//通過request的getAttribue方法查找CURRENT_SESSION屬性,有直接返回HttpSessionWrapper currentSession = getCurrentSession();if (currentSession != null) {return currentSession;}//查找客戶端中一個叫SESSION的cookie,通過sessionRepository對象根據SESSIONID去Redis中查找SessionS requestedSession = getRequestedSession();if (requestedSession != null) {if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {requestedSession.setLastAccessedTime(Instant.now());this.requestedSessionIdValid = true;currentSession = new HttpSessionWrapper(requestedSession, getServletContext());currentSession.setNew(false);//將Session設置到request屬性中setCurrentSession(currentSession);//返回Sessionreturn currentSession;}}else {// This is an invalid session id. No need to ask again if// request.getSession is invoked for the duration of this requestif (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");}setAttribute(INVALID_SESSION_ID_ATTR, "true");}//不創建Session就直接返回nullif (!create) {return null;}if (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "+ SESSION_LOGGER_NAME,new RuntimeException("For debugging purposes only (not an error)"));}//通過sessionRepository創建RedisSession這個對象,可以看下這個類的源代碼,如果//@EnableRedisHttpSession這個注解中的redisFlushMode模式配置為IMMEDIATE模式,會立即將創建的RedisSession同步到Redis中去。默認是不會立即同步的。S session = SessionRepositoryFilter.this.sessionRepository.createSession();session.setLastAccessedTime(Instant.now());currentSession = new HttpSessionWrapper(session, getServletContext());setCurrentSession(currentSession);return currentSession; }

    這里關于springSession 的redis存儲模式,可以參考文章

    當調用 SessionRepositoryRequestWrapper 對象的getSession方法拿 Session 的時候,會先從當前請求的屬性中查找CURRENT_SESSION屬性,如果能拿到直接返回,這樣操作能減少Redis操作,提升性能。到現在為止我們發現如果redisFlushMode配置為 ON_SAVE 模式的話,Session 信息還沒被保存到 Redis 中,那么這個同步操作到底是在哪里執行的呢?仔細看代碼,我們`發現 SessionRepositoryFilter 的doFilterInternal方法最后有一個 finally 代碼塊,這個代碼塊的功能就是將 Session同步到 Redis。

    @Override protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, this.servletContext);SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);try {filterChain.doFilter(wrappedRequest, wrappedResponse);}finally {//將Session同步到Redis,同時這個方法還會將當前的SESSIONID寫到cookie中去,同時還會發布一//SESSION創建事件到隊列里面去wrappedRequest.commitSession();} }

    三、簡單總結

  • 主要的核心類有
    • @EnableRedisHttpSession:開啟 Session 共享功能;
    • RedisHttpSessionConfiguration:配置類,一般不需要我們自己配置,主要功能是配置
    • SessionRepositoryFilter 和 RedisOperationsSessionRepository 這兩個Bean;
    • SessionRepositoryFilter:攔截器,Spring-Session 框架的核心;
    • RedisOperationsSessionRepository:可以認為是一個 Redis 操作的客戶端,有在 Redis 中進行增刪改查Session 的功能;
    • SessionRepositoryRequestWrapper:Request的包裝類,主要是重寫了getSession方法
    • SessionRepositoryResponseWrapper:Response的包裝類。
  • 原理簡要總結:
  • 當請求進來的時候,SessionRepositoryFilter 會先攔截到請求,將 request 和 response 對象轉換成 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper。后續當第一次調用 request 的getSession方法時,會調用到 SessionRepositoryRequestWrapper的getSession方法。這個方法是被重寫過的,邏輯是先從 request的屬性中查找,如果找不到;再查找一個key值是"SESSION"的 Cookie,通過這個 Cookie 拿到 SessionId 去Redis 中查找,如果查不到,就直接創建一個RedisSession 對象,同步到 Redis 中。

    說的簡單點就是:攔截請求,將之前在服務器內存中進行 Session 創建銷毀的動作,改成在 Redis 中創建。

    參考文章
    參考文章
    我見過的最詳細的文章

    總結

    以上是生活随笔為你收集整理的从@EnableRedisHttpSession谈谈Spring Session实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。