【spring-session】使用spring-session后Session事件监听不生效

结论

废话后边说。先上结论

spring boot依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/>
    </parent>

spring session依赖

        <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
        </dependency>

session事件监听代码

@Component
public class SessionEventListener {
    @EventListener
    public void handleCreated(SessionCreatedEvent event) {
        System.out.println("Session created: " + event.getClass().getName() + " - " + event.getSessionId());
    }

    @EventListener
    public void handleDestroyed(SessionDestroyedEvent event) {
        System.out.println("Session destroyed: " + event.getClass().getName() + " - " + event.getSessionId());
    }

    @EventListener
    public void handleExpired(SessionExpiredEvent event) {
        System.out.println("Session expired: " + event.getClass().getName() + " - " + event.getSessionId());
    }
}

解决方案

application.ymlspring:session:redis下增加 repository-type: indexed

spring:
  session:
    timeout: 864000 # 默认10天
    redis:
      namespace: xxxxx:session
      repository-type: indexed  # 增加这句

源码分析

话不多说,直接看重点。。。RedisSessionConfiguration类中可以看到为什么要修改默认配置

  • repository-typedefault时创建RedisSessionRepository
  • repository-typeindexed时创建RedisIndexedSessionRepository

RedisSessionRepository

	/**
 * A {@link SessionRepository} implementation that uses Spring Data's
 * {@link RedisOperations} to store sessions is Redis.
 * <p>
 * This implementation does not support publishing of session events.
 *
 * @author Vedran Pavic
 * @since 2.2.0
 */
public class RedisSessionRepository implements SessionRepository<RedisSessionRepository.RedisSession> {}

RedisIndexedSessionRepository

	@Override
	public void onMessage(Message message, byte[] pattern) {
		byte[] messageChannel = message.getChannel();

		if (ByteUtils.startsWith(messageChannel, this.sessionCreatedChannelPrefixBytes)) {
			// TODO: is this thread safe?
			String channel = new String(messageChannel);
			String sessionId = channel.substring(channel.lastIndexOf(":") + 1);
			@SuppressWarnings("unchecked")
			Map<String, Object> entries = (Map<String, Object>) this.defaultSerializer.deserialize(message.getBody());
			MapSession loaded = this.redisSessionMapper.apply(sessionId, entries);
			if (loaded != null) {
				RedisSession session = new RedisSession(loaded, false);
				handleCreated(session);
			}
			return;
		}

		byte[] messageBody = message.getBody();

		if (!ByteUtils.startsWith(messageBody, this.expiredKeyPrefixBytes)) {
			return;
		}

		boolean isDeleted = Arrays.equals(messageChannel, this.sessionDeletedChannelBytes);
		if (isDeleted || Arrays.equals(messageChannel, this.sessionExpiredChannelBytes)) {
			String body = new String(messageBody);
			int beginIndex = body.lastIndexOf(":") + 1;
			int endIndex = body.length();
			String sessionId = body.substring(beginIndex, endIndex);

			RedisSession session = getSession(sessionId, true);

			if (session == null) {
				logger.warn("Unable to publish SessionDestroyedEvent for session " + sessionId);
				return;
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Publishing SessionDestroyedEvent for session " + sessionId);
			}

			cleanupPrincipalIndex(session);

			if (isDeleted) {
				handleDeleted(session);
			}
			else {
				handleExpired(session);
			}
		}
	}

其中handleCreatedhandleDeletedhandleExpired方法是发布session事件,以下源码

	private void handleCreated(RedisSession session) {
		publishEvent(new SessionCreatedEvent(this, session));
	}

	private void handleDeleted(RedisSession session) {
		publishEvent(new SessionDeletedEvent(this, session));
	}

	private void handleExpired(RedisSession session) {
		publishEvent(new SessionExpiredEvent(this, session));
	}

	private void publishEvent(ApplicationEvent event) {
		try {
			this.eventPublisher.publishEvent(event);
		}
		catch (Throwable ex) {
			logger.error("Error publishing " + event + ".", ex);
		}
	}

总结

  • spring-session-redis 默认实现为RedisSessionRepository,不支持发布session事件
  • 改配置文件(spring:session:redis下增加 repository-type: indexed)后为RedisIndexedSessionRepository实现,此时支持session事件,监听生效
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值