SpringSession 集成redis之后是如何实现的session有效期自动延长的呢?(源码分析)

SpringSession 集成redis之后是如何实现的session有效期自动延长的呢?

首先我们先在自己的springboot项目中集成springsession + redis,只需要在springboot启动类加

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 600)

这行注释就可以,上面的注释默认设置springsession在redis中的有效时长为10分钟,那么接下来就让我们跟随源码去了解一下,springsession在redis中的有效期是如何自动延时的

HttpSession session = request.getSession();
session.setAttribute(RedisConstants.USER_SAVE_NAME, sysUser.getUsername());

以这两句代码为例,我在controller中获取request中的session,然后在session中设置了一个参数

通过debug模式,F5进入setAttribute方法

org.springframework.session.web.http.HttpSessionAdapter类
public void setAttribute(String name, Object value) {
    this.checkState();
    Object oldValue = this.session.getAttribute(name);
    this.session.setAttribute(name, value);
    if (value != oldValue) {
        if (oldValue instanceof HttpSessionBindingListener) {
            try {
                ((HttpSessionBindingListener)oldValue).valueUnbound(new HttpSessionBindingEvent(this, name, oldValue));
            } catch (Throwable var6) {
                logger.error("Error invoking session binding event listener", var6);
            }
        }

        if (value instanceof HttpSessionBindingListener) {
            try {
                ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this, name, value));
            } catch (Throwable var5) {
                logger.error("Error invoking session binding event listener", var5);
            }
        }
    }

}

源码如上,然后在划线部分继续F5跟进

org.springframework.session.data.redis.RedisOperationsSessionRepository
public void setAttribute(String attributeName, Object attributeValue) {
    this.cached.setAttribute(attributeName, attributeValue);
    this.putAndFlush(RedisOperationsSessionRepository.getSessionAttrNameKey(attributeName), attributeValue);
}

进入RedisOperationsSessionRepository类 的setAttribute方法,发现他在将我们设置参数保存之后,执行了一个putAndFlush方法,继续跟进

org.springframework.session.data.redis.RedisOperationsSessionRepository

private void putAndFlush(String a, Object v) {
    this.delta.put(a, v);
    this.flushImmediateIfNecessary();
}

还是该类中的方法,再进入flushImmediateIfNecessary方法,发现有一个save方法,我们的session有效时长顺延就是在这个方法中做的

public void save(RedisOperationsSessionRepository.RedisSession session) {
    session.save();
    if (session.isNew()) {
        String sessionCreatedKey = this.getSessionCreatedChannel(session.getId());
        this.sessionRedisOperations.convertAndSend(sessionCreatedKey, session.delta);
        session.setNew(false);
    }

}
private void save() {
    this.saveChangeSessionId();
    this.saveDelta();
}
private void saveDelta() {
    if (!this.delta.isEmpty()) {
        String sessionId = this.getId();
        RedisOperationsSessionRepository.this.getSessionBoundHashOperations(sessionId).putAll(this.delta);
        String principalSessionKey = RedisOperationsSessionRepository.getSessionAttrNameKey(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME);
        String securityPrincipalSessionKey = RedisOperationsSessionRepository.getSessionAttrNameKey("SPRING_SECURITY_CONTEXT");
        if (this.delta.containsKey(principalSessionKey) || this.delta.containsKey(securityPrincipalSessionKey)) {
            String principal;
            if (this.originalPrincipalName != null) {
                principal = RedisOperationsSessionRepository.this.getPrincipalKey(this.originalPrincipalName);
                RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(principal).remove(new Object[]{sessionId});
            }

            principal = RedisOperationsSessionRepository.PRINCIPAL_NAME_RESOLVER.resolvePrincipal(this);
            this.originalPrincipalName = principal;
            if (principal != null) {
                String principalRedisKey = RedisOperationsSessionRepository.this.getPrincipalKey(principal);
                RedisOperationsSessionRepository.this.sessionRedisOperations.boundSetOps(principalRedisKey).add(new Object[]{sessionId});
            }
        }

        this.delta = new HashMap(this.delta.size());
        Long originalExpiration = this.originalLastAccessTime != null ? this.originalLastAccessTime.plus(this.getMaxInactiveInterval()).toEpochMilli() : null;
        RedisOperationsSessionRepository.this.expirationPolicy.onExpirationUpdated(originalExpiration, this);
    }
}
public void onExpirationUpdated(Long originalExpirationTimeInMilli, Session session) {
    String keyToExpire = "expires:" + session.getId();
    long toExpire = roundUpToNextMinute(expiresInMillis(session));
    long sessionExpireInSeconds;
    String sessionKey;
    if (originalExpirationTimeInMilli != null) {
        sessionExpireInSeconds = roundUpToNextMinute(originalExpirationTimeInMilli);
        if (toExpire != sessionExpireInSeconds) {
            sessionKey = this.getExpirationKey(sessionExpireInSeconds);
            this.redis.boundSetOps(sessionKey).remove(new Object[]{keyToExpire});
        }
    }

    sessionExpireInSeconds = session.getMaxInactiveInterval().getSeconds();
    sessionKey = this.getSessionKey(keyToExpire);
    if (sessionExpireInSeconds < 0L) {
        this.redis.boundValueOps(sessionKey).append("");
        this.redis.boundValueOps(sessionKey).persist();
        this.redis.boundHashOps(this.getSessionKey(session.getId())).persist();
    } else {
        String expireKey = this.getExpirationKey(toExpire);
        BoundSetOperations<Object, Object> expireOperations = this.redis.boundSetOps(expireKey);
        expireOperations.add(new Object[]{keyToExpire});
        long fiveMinutesAfterExpires = sessionExpireInSeconds + TimeUnit.MINUTES.toSeconds(5L);
        expireOperations.expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
        if (sessionExpireInSeconds == 0L) {
            this.redis.delete(sessionKey);
        } else {
            this.redis.boundValueOps(sessionKey).append("");
            this.redis.boundValueOps(sessionKey).expire(sessionExpireInSeconds, TimeUnit.SECONDS);
        }

        this.redis.boundHashOps(this.getSessionKey(session.getId())).expire(fiveMinutesAfterExpires, TimeUnit.SECONDS);
    }
}

上面的onExpirationUpdated方法实现的就是延时功能,但是save方法其实并不是在此处调用的,而是

org.springframework.session.web.http.SessionRepositoryFilter类中
private void commitSession() {
    SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper.HttpSessionWrapper wrappedSession = this.getCurrentSession();
    if (wrappedSession == null) {
        if (this.isInvalidateClientSession()) {
            SessionRepositoryFilter.this.httpSessionIdResolver.expireSession(this, this.response);
        }
    } else {
        S session = wrappedSession.getSession();
        this.clearRequestedSessionCache();
        SessionRepositoryFilter.this.sessionRepository.save(session);
        String sessionId = session.getId();
        if (!this.isRequestedSessionIdValid() || !sessionId.equals(this.getRequestedSessionId())) {
            SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
        }
    }

}

这个commitSession()方法中调用的,具体链路这里就不贴了,太深,有兴趣的同学可以自己dubug跟一下

那么最后我们宏观整理一下整个链路,就是EnableRedisHttpSession引用的这个标签中,引入了下面这个类,

@Import({RedisHttpSessionConfiguration.class}),该类中注入了
@Bean
public RedisOperationsSessionRepository sessionRepository() {...}

这个bean,而这个bean中又new了 RedisSessionExpirationPolicy 这个类的对象,然后在这个类中实现了onExpirationUpdated这个方法,具体这个方法的调用其实是通过过滤器在对session做操作后,通过commitSession方法调用到延时方法的,由此实现了session在redis中的有效时长自动延时的功能。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我爱编程编程不爱我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值