最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一:spring-session介紹

1.簡介

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

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

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

spring-session是spring旗下的一個項目,把servlet容器實現(xiàn)的httpSession替換為spring-session,專注于解決 session管理問題??珊唵慰焖偾覠o縫的集成到我們的應(yīng)用中。

2.支持功能

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

2)同一個瀏覽器同一個網(wǎng)站,支持多個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/

主要分為以下幾個集成步驟:

1)引入依賴jar包

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

<context:annotation-config/>  
/** 初始化一切spring-session準(zhǔn)備,且把springSessionFilter放入IOC     **/
<beanclass="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>  
/** 這是存儲容器的鏈接池 **/ 
<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實現(xiàn)存儲相關(guān)問題

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

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

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

  private ServletContext servletContext;
  /** 
   sessionID的傳遞方式接口。目前spring-session自帶兩個實現(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來實現(xiàn)的。
    每個瀏覽器擁有一個sessionID,但是這個sessionID擁有多個別名(根據(jù)瀏覽器的tab)。如:
        別名1 sessionID
        別名2 sessionID
        ...
        而這個別名通過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又做了多一層封裝。
  在這個方法里創(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。這個類繼承了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位于過濾器鏈的第一個,那么后續(xù)的Filter,
       以及到達最后的控制層所獲取的 request和response,是不是就是我們自定義的了?
       **/
      filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
      wrappedRequest.commitSession();
    }
  }

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

  /**
  這個就是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到存儲容器,我們可能會在一個控制層里多次調(diào)用session的操作方法
      如果我們每次對session的操作都持久化到存儲容器,必定會帶來性能的影響。比如redis
      所以我們可以在整個控制層執(zhí)行完畢了,response返回信息到瀏覽器時,才持久化session
     **/
    @Override
    protected void onResponseCommitted() {
      this.request.commitSession();
    }
  }

  /**
  spring-session 的request重寫類,這幾乎是最重要的一個重寫類。里面重寫了獲取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或者header
        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或者header寫回給瀏覽器保存 
        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存儲容器中根據(jù)sessionID獲取session
      S session = SessionRepositoryFilter.this.sessionRepository
          .getSession(sessionId);
      if (session == null) {
        return null;
      }
      // 設(shè)置sesison的最后訪問時間,以防過期
      session.setLastAccessedTime(System.currentTimeMillis());
      return session;
    }
     /**
     這個方法是不是很熟悉,下面還有個getSession()才更加熟悉。沒錯,就是在這里重新獲取session方法 
     **/
    @Override
    public HttpSessionWrapper getSession(boolean create) {
      //快速獲取session,可以理解為一級緩存、二級緩存這種關(guān)系
      HttpSessionWrapper currentSession = getCurrentSession();
      if (currentSession != null) {
        return currentSession;
      }
      //從httpSessionStratge里面根據(jù)cookie或者header獲取sessionID
      String requestedSessionId = getRequestedSessionId();
      if (requestedSessionId != null
          && getAttribute(INVALID_SESSION_ID_ATTR) == null) {                                           
        //從存儲容器獲取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)建個新的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簡介及實現(xiàn)原理源碼分析的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出!

相關(guān)文章

最新評論

富锦市| 龙山县| 多伦县| 罗平县| 伊春市| 扬中市| 宁乡县| 佛山市| 遂宁市| 桂平市| 安仁县| 吉安市| 海口市| 上蔡县| 宝应县| 韶山市| 淮滨县| 南城县| 灵武市| 台江县| 绥宁县| 闵行区| 桦甸市| 扎鲁特旗| 三门县| 马鞍山市| 获嘉县| 长汀县| 固原市| 惠安县| 定边县| 定南县| 桃园县| 隆昌县| 隆化县| 大理市| 颍上县| 关岭| 延津县| 望都县| 舞阳县|