欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

spring-session簡介及實(shí)現(xiàn)原理源碼分析

 更新時(shí)間:2017年11月30日 09:36:53   作者:孤客_  
這篇文章主要介紹了spring-session簡介及實(shí)現(xiàn)原理源碼分析,具有一定參考價(jià)值,需要的朋友可以了解下。

一:spring-session介紹

1.簡介

session一直都是我們做集群時(shí)需要解決的一個(gè)難題,過去我們可以從serlvet容器上解決,比如開源servlet容器-tomcat提供的tomcat-redis-session-manager、memcached-session-manager。

或者通過nginx之類的負(fù)載均衡做ip_hash,路由到特定的服務(wù)器上..

但是這兩種辦法都存在弊端。

spring-session是spring旗下的一個(gè)項(xiàng)目,把servlet容器實(shí)現(xiàn)的httpSession替換為spring-session,專注于解決 session管理問題。可簡單快速且無縫的集成到我們的應(yīng)用中。

2.支持功能

1)輕易把session存儲(chǔ)到第三方存儲(chǔ)容器,框架提供了redis、jvm的map、mongo、gemfire、hazelcast、jdbc等多種存儲(chǔ)session的容器的方式。

2)同一個(gè)瀏覽器同一個(gè)網(wǎng)站,支持多個(gè)session問題。

3)RestfulAPI,不依賴于cookie??赏ㄟ^header來傳遞jessionID

4)WebSocket和spring-session結(jié)合,同步生命周期管理。

3.集成方式

集成方式非常簡單,直接看官網(wǎng)的samplesandguide。http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/

主要分為以下幾個(gè)集成步驟:

1)引入依賴jar包

2)注解方式或者xml方式配置特定存儲(chǔ)容器的存儲(chǔ)方式,如redis的xml配置方式

<context:annotation-config/>  
/** 初始化一切spring-session準(zhǔn)備,且把springSessionFilter放入IOC     **/
<beanclass="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>  
/** 這是存儲(chǔ)容器的鏈接池 **/ 
<beanclass="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>

3)xml方式配置 web.xml ,配置 springSessionFilter到 filter chain中

<filter>
     <filter-name>springSessionRepositoryFilter</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
   </filter> 
   <filter-mapping>
     <filter-name>springSessionRepositoryFilter</filter-name>
     <url-pattern>/*</url-pattern>
     <dispatcher>REQUEST</dispatcher><dispatcher>ERROR</dispatcher> 
   </filter-mapping>

二:spring-session框架內(nèi)部剖析

1.框架高層抽象結(jié)構(gòu)圖

2.spring-session重寫servlet request 及 redis實(shí)現(xiàn)存儲(chǔ)相關(guān)問題

spring-session無縫替換應(yīng)用服務(wù)器的request大概原理是:
1.自定義個(gè)Filter,實(shí)現(xiàn)doFilter方法
2.繼承 HttpServletRequestWrapper 、HttpServletResponseWrapper 類,重寫getSession等相關(guān)方法(在這些方法里調(diào)用相關(guān)的 session存儲(chǔ)容器操作類)。
3.在 第一步的doFilter中,new 第二步 自定義的request和response的類。并把它們分別傳遞 到 過濾器鏈
4.把該filter配置到 過濾器鏈的第一個(gè)位置上

/** 這個(gè)類是spring-session的1.30源碼,也是實(shí)現(xiàn)上面第一到第三步的關(guān)鍵類 **/
public class SessionRepositoryFilter<S extends ExpiringSession>
    extends OncePerRequestFilter {

  /** session存儲(chǔ)容器接口,redis、mongoDB、genfire等數(shù)據(jù)庫都是實(shí)現(xiàn)該接口 **/
  private final SessionRepository<S> sessionRepository;

  private ServletContext servletContext;
  /** 
   sessionID的傳遞方式接口。目前spring-session自帶兩個(gè)實(shí)現(xiàn)類
   1.cookie方式 :CookieHttpSessionStrategy
   2.http header 方式:HeaderHttpSessionStrategy
   當(dāng)然,我們也可以自定義其他方式。
  **/
  private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();

  public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
    if (sessionRepository == null) {
      throw new IllegalArgumentException("sessionRepository cannot be null");
    }
    this.sessionRepository = sessionRepository;
  }


  public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
    if (httpSessionStrategy == null) {
      throw new IllegalArgumentException("httpSessionStrategy cannot be null");
    }
    /** 
    通過前面的spring-session功能介紹,我們知道spring-session可以支持單瀏覽器多
    session, 就是通過MultiHttpSessionStrategyAdapter來實(shí)現(xiàn)的。
    每個(gè)瀏覽器擁有一個(gè)sessionID,但是這個(gè)sessionID擁有多個(gè)別名(根據(jù)瀏覽器的tab)。如:
        別名1 sessionID
        別名2 sessionID
        ...
        而這個(gè)別名通過url來傳遞,這就是單瀏覽器多session原理了
        **/
    this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(
        httpSessionStrategy);
  }


  public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {
    if (httpSessionStrategy == null) {
      throw new IllegalArgumentException("httpSessionStrategy cannot be null");
    }
    this.httpSessionStrategy = httpSessionStrategy;
  }
   /**
  該方法相當(dāng)于重寫了doFilter,只是spring-session又做了多一層封裝。
  在這個(gè)方法里創(chuàng)建自定義的 request和response,然后傳遞到過濾器鏈filterChain
   **/
  @Override
  protected void doFilterInternal(HttpServletRequest request,
      HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
        /**
        spring-session重寫的ServletRequest。這個(gè)類繼承了HttpServletRequestWrapper 
        **/
    SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
        request, response, this.servletContext);
    SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
        wrappedRequest, response);

    HttpServletRequest strategyRequest = this.httpSessionStrategy
        .wrapRequest(wrappedRequest, wrappedResponse);
    HttpServletResponse strategyResponse = this.httpSessionStrategy
        .wrapResponse(wrappedRequest, wrappedResponse);

    try { 
       /** 
       傳遞自定義 request和response到鏈中,想象下如果
       該spring-sessionFilter位于過濾器鏈的第一個(gè),那么后續(xù)的Filter,
       以及到達(dá)最后的控制層所獲取的 request和response,是不是就是我們自定義的了?
       **/
      filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
      wrappedRequest.commitSession();
    }
  }

  public void setServletContext(ServletContext servletContext) {
    this.servletContext = servletContext;
  }

  /**
  這個(gè)就是Servlet response的重寫類了
   */
  private final class SessionRepositoryResponseWrapper
      extends OnCommittedResponseWrapper {

    private final SessionRepositoryRequestWrapper request;


    SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request,
        HttpServletResponse response) {
      super(response);
      if (request == null) {
        throw new IllegalArgumentException("request cannot be null");
      }
      this.request = request;
    }
     /** 
      這步是持久化session到存儲(chǔ)容器,我們可能會(huì)在一個(gè)控制層里多次調(diào)用session的操作方法
      如果我們每次對session的操作都持久化到存儲(chǔ)容器,必定會(huì)帶來性能的影響。比如redis
      所以我們可以在整個(gè)控制層執(zhí)行完畢了,response返回信息到瀏覽器時(shí),才持久化session
     **/
    @Override
    protected void onResponseCommitted() {
      this.request.commitSession();
    }
  }

  /**
  spring-session 的request重寫類,這幾乎是最重要的一個(gè)重寫類。里面重寫了獲取getSession,Session等方法以及類
   */
  private final class SessionRepositoryRequestWrapper
      extends HttpServletRequestWrapper {
    private Boolean requestedSessionIdValid;
    private boolean requestedSessionInvalidated;
    private final HttpServletResponse response;
    private final ServletContext servletContext;

    private SessionRepositoryRequestWrapper(HttpServletRequest request,
        HttpServletResponse response, ServletContext servletContext) {
      super(request);
      this.response = response;
      this.servletContext = servletContext;
    }

    /**
     * Uses the HttpSessionStrategy to write the session id to the response and
     * persist the Session.
     */
    private void commitSession() {
      HttpSessionWrapper wrappedSession = getCurrentSession();
      if (wrappedSession == null) {
          // session失效,刪除cookie或者h(yuǎn)eader
        if (isInvalidateClientSession()) {
          SessionRepositoryFilter.this.httpSessionStrategy
              .onInvalidateSession(this, this.response);
        }
      }
      else {
        S session = wrappedSession.getSession();
        SessionRepositoryFilter.this.sessionRepository.save(session);
        if (!isRequestedSessionIdValid()
            || !session.getId().equals(getRequestedSessionId())) {
        // 把cookie或者h(yuǎn)eader寫回給瀏覽器保存 
        SessionRepositoryFilter.this.httpSessionStrategy.onNewSession(session,
              this, this.response);
        }
      }
    }

    @SuppressWarnings("unchecked")
    private HttpSessionWrapper getCurrentSession() {
      return (HttpSessionWrapper) getAttribute(CURRENT_SESSION_ATTR);
    }

    private void setCurrentSession(HttpSessionWrapper currentSession) {
      if (currentSession == null) {
        removeAttribute(CURRENT_SESSION_ATTR);
      }
      else {
        setAttribute(CURRENT_SESSION_ATTR, currentSession);
      }
    }

    @SuppressWarnings("unused")
    public String changeSessionId() {
      HttpSession session = getSession(false);

      if (session == null) {
        throw new IllegalStateException(
            "Cannot change session ID. There is no session associated with this request.");
      }

      // eagerly get session attributes in case implementation lazily loads them
      Map<String, Object> attrs = new HashMap<String, Object>();
      Enumeration<String> iAttrNames = session.getAttributeNames();
      while (iAttrNames.hasMoreElements()) {
        String attrName = iAttrNames.nextElement();
        Object value = session.getAttribute(attrName);

        attrs.put(attrName, value);
      }

      SessionRepositoryFilter.this.sessionRepository.delete(session.getId());
      HttpSessionWrapper original = getCurrentSession();
      setCurrentSession(null);

      HttpSessionWrapper newSession = getSession();
      original.setSession(newSession.getSession());

      newSession.setMaxInactiveInterval(session.getMaxInactiveInterval());
      for (Map.Entry<String, Object> attr : attrs.entrySet()) {
        String attrName = attr.getKey();
        Object attrValue = attr.getValue();
        newSession.setAttribute(attrName, attrValue);
      }
      return newSession.getId();
    }
    // 判斷session是否有效
    @Override
    public boolean isRequestedSessionIdValid() {
      if (this.requestedSessionIdValid == null) {
        String sessionId = getRequestedSessionId();
        S session = sessionId == null ? null : getSession(sessionId);
        return isRequestedSessionIdValid(session);
      }

      return this.requestedSessionIdValid;
    }

    private boolean isRequestedSessionIdValid(S session) {
      if (this.requestedSessionIdValid == null) {
        this.requestedSessionIdValid = session != null;
      }
      return this.requestedSessionIdValid;
    }

    private boolean isInvalidateClientSession() {
      return getCurrentSession() == null && this.requestedSessionInvalidated;
    }

    private S getSession(String sessionId) {
       // 從session存儲(chǔ)容器中根據(jù)sessionID獲取session
      S session = SessionRepositoryFilter.this.sessionRepository
          .getSession(sessionId);
      if (session == null) {
        return null;
      }
      // 設(shè)置sesison的最后訪問時(shí)間,以防過期
      session.setLastAccessedTime(System.currentTimeMillis());
      return session;
    }
     /**
     這個(gè)方法是不是很熟悉,下面還有個(gè)getSession()才更加熟悉。沒錯(cuò),就是在這里重新獲取session方法 
     **/
    @Override
    public HttpSessionWrapper getSession(boolean create) {
      //快速獲取session,可以理解為一級(jí)緩存、二級(jí)緩存這種關(guān)系
      HttpSessionWrapper currentSession = getCurrentSession();
      if (currentSession != null) {
        return currentSession;
      }
      //從httpSessionStratge里面根據(jù)cookie或者h(yuǎn)eader獲取sessionID
      String requestedSessionId = getRequestedSessionId();
      if (requestedSessionId != null
          && getAttribute(INVALID_SESSION_ID_ATTR) == null) {                                           
        //從存儲(chǔ)容器獲取session以及設(shè)置當(dāng)次初始化屬性                      
        S session = getSession(requestedSessionId);
        if (session != null) {
          this.requestedSessionIdValid = true;
          currentSession = new HttpSessionWrapper(session, getServletContext());
          currentSession.setNew(false);
          setCurrentSession(currentSession);
          return currentSession;
        }
        else {

          if (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");
        }
      }
      if (!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)"));
      }
      // 如果該瀏覽器或者其他http訪問者是初次訪問服務(wù)器,則為他創(chuàng)建個(gè)新的session
      S session = SessionRepositoryFilter.this.sessionRepository.createSession();
      session.setLastAccessedTime(System.currentTimeMillis());
      currentSession = new HttpSessionWrapper(session, getServletContext());
      setCurrentSession(currentSession);
      return currentSession;
    }

    @Override
    public ServletContext getServletContext() {
      if (this.servletContext != null) {
        return this.servletContext;
      }
      // Servlet 3.0+
      return super.getServletContext();
    }

    @Override
    public HttpSessionWrapper getSession() {
      return getSession(true);
    }

    @Override
    public String getRequestedSessionId() {
      return SessionRepositoryFilter.this.httpSessionStrategy
          .getRequestedSessionId(this);
    }

    /**
    HttpSession的重寫類
     */
    private final class HttpSessionWrapper extends ExpiringSessionHttpSession<S> {

      HttpSessionWrapper(S session, ServletContext servletContext) {
        super(session, servletContext);
      }

      @Override
      public void invalidate() {
        super.invalidate();
        SessionRepositoryRequestWrapper.this.requestedSessionInvalidated = true;
        setCurrentSession(null);
        SessionRepositoryFilter.this.sessionRepository.delete(getId());
      }
    }
  }
}

總結(jié)

以上就是本文關(guān)于spring-session簡介及實(shí)現(xiàn)原理源碼分析的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出!

相關(guān)文章

  • 基于Java+SpringBoot+Vue前后端分離實(shí)現(xiàn)倉庫管理系統(tǒng)

    基于Java+SpringBoot+Vue前后端分離實(shí)現(xiàn)倉庫管理系統(tǒng)

    這篇文章主要介紹了一個(gè)完整的倉庫管理系統(tǒng)是基于Java+Springboot + Vue前后端分離編寫的,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • 使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密

    使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密

    這篇文章主要介紹了使用java自帶des加密算法實(shí)現(xiàn)文件加密和字符串加密的示例,需要的朋友可以參考下
    2014-03-03
  • spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄

    spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄

    這篇文章主要為大家介紹了spring-boot項(xiàng)目啟動(dòng)遲緩異常排查解決記錄,突然在本地啟動(dòng)不起來了,表象特征就是在本地IDEA上運(yùn)行時(shí),進(jìn)程卡住也不退出,應(yīng)用啟動(dòng)時(shí)加載相關(guān)組件的日志也不輸出
    2022-02-02
  • java連接Mongodb實(shí)現(xiàn)增刪改查

    java連接Mongodb實(shí)現(xiàn)增刪改查

    這篇文章主要為大家詳細(xì)介紹了java連接Mongodb實(shí)現(xiàn)增刪改查,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • SpringBoot配置文件密碼加密的三種方案

    SpringBoot配置文件密碼加密的三種方案

    這篇文章主要介紹了SpringBoot配置文件密碼加密的三種方案,文中通過代碼示例給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • 圖解Java經(jīng)典算法希爾排序的原理與實(shí)現(xiàn)

    圖解Java經(jīng)典算法希爾排序的原理與實(shí)現(xiàn)

    希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經(jīng)過改進(jìn)之后的一個(gè)更高效的版本,也稱為縮小增量排序,同時(shí)該算法是沖破O(n2)的第一批算法之一。本文會(huì)以圖解的方式詳細(xì)介紹希爾排序的基本思想及其代碼實(shí)現(xiàn)
    2022-09-09
  • Mybatis實(shí)現(xiàn)單個(gè)和批量定義別名typeAliases

    Mybatis實(shí)現(xiàn)單個(gè)和批量定義別名typeAliases

    這篇文章主要介紹了Mybatis實(shí)現(xiàn)單個(gè)和批量定義別名typeAliases,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • RabbitMQ中的Channel和Exchange詳解

    RabbitMQ中的Channel和Exchange詳解

    這篇文章主要介紹了RabbitMQ中的Channel和Exchange詳解,創(chuàng)建和銷毀TCP連接很耗時(shí),打開太多TCP連接,耗操作系統(tǒng)資源,并發(fā)量大到一定程度,系統(tǒng)的吞吐量會(huì)降低,使用一個(gè)connection多channel的方式,可以提升連接的利用率,需要的朋友可以參考下
    2023-08-08
  • Java并發(fā)編程之原子變量與非阻塞同步機(jī)制

    Java并發(fā)編程之原子變量與非阻塞同步機(jī)制

    這篇文章主要介紹了Java并發(fā)編程之原子變量與非阻塞同步機(jī)制,本文講解了非阻塞算法、悲觀技術(shù)、樂觀技術(shù)、CAS操作、原子變量、性能比較:鎖與原子變量等內(nèi)容,需要的朋友可以參考下
    2015-04-04
  • 詳解在Spring Boot中使用JPA

    詳解在Spring Boot中使用JPA

    本篇文章主要介紹了詳解在Spring Boot中使用JPA,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05

最新評(píng)論