栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

spring-session简介及实现原理源码分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

spring-session简介及实现原理源码分析

一:spring-session介绍

1.简介

session一直都是我们做集群时需要解决的一个难题,过去我们可以从serlvet容器上解决,比如开源servlet容器-tomcat提供的tomcat-redis-session-manager、memcached-session-manager。

或者通过nginx之类的负载均衡做ip_hash,路由到特定的服务器上..

但是这两种办法都存在弊端。

spring-session是spring旗下的一个项目,把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题。可简单快速且无缝的集成到我们的应用中。

2.支持功能

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

2)同一个浏览器同一个网站,支持多个session问题。

3)RestfulAPI,不依赖于cookie。可通过header来传递jessionID

4)WebSocket和spring-session结合,同步生命周期管理。

3.集成方式

集成方式非常简单,直接看官网的samplesandguide。http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/

主要分为以下几个集成步骤:

1)引入依赖jar包

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

  

  
 

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


     springSessionRepositoryFilter
     org.springframework.web.filter.DelegatingFilterProxy 
    
   
     springSessionRepositoryFilter
     
public class SessionRepositoryFilter
    extends oncePerRequestFilter {

  
  private final SessionRepository sessionRepository;

  private ServletContext servletContext;
  
  private MultiHttpSessionStrategy httpSessionStrategy = new cookieHttpSessionStrategy();

  public SessionRepositoryFilter(SessionRepository 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");
    }
    
    this.httpSessionStrategy = new MultiHttpSessionStrategyAdapter(
 httpSessionStrategy);
  }


  public void setHttpSessionStrategy(MultiHttpSessionStrategy httpSessionStrategy) {
    if (httpSessionStrategy == null) {
      throw new IllegalArgumentException("httpSessionStrategy cannot be null");
    }
    this.httpSessionStrategy = httpSessionStrategy;
  }
   
  @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);

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

    try { 

      filterChain.doFilter(strategyRequest, strategyResponse);
    }
    finally {
      wrappedRequest.commitSession();
    }
  }

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

  
  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;
    }
     
    @Override
    protected void onResponseCommitted() {
      this.request.commitSession();
    }
  }

  
  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;
    }

    
    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 attrs = new HashMap();
      Enumeration 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 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存储容器中根据sessionID获取session
      S session = SessionRepositoryFilter.this.sessionRepository
   .getSession(sessionId);
      if (session == null) {
 return null;
      }
      // 设置sesison的最后访问时间,以防过期
      session.setLastAccessedTime(System.currentTimeMillis());
      return session;
    }
     
    @Override
    public HttpSessionWrapper getSession(boolean create) {
      //快速获取session,可以理解为一级缓存、二级缓存这种关系
      HttpSessionWrapper currentSession = getCurrentSession();
      if (currentSession != null) {
 return currentSession;
      }
      //从httpSessionStratge里面根据cookie或者header获取sessionID
      String requestedSessionId = getRequestedSessionId();
      if (requestedSessionId != null
   && getAttribute(INVALID_SESSION_ID_ATTR) == null) { 
 //从存储容器获取session以及设置当次初始化属性 
 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访问者是初次访问服务器,则为他创建个新的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);
    }

    
    private final class HttpSessionWrapper extends ExpiringSessionHttpSession {

      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());
      }
    }
  }
}

总结

以上就是本文关于spring-session简介及实现原理源码分析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出!

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号