9. API Documentation

您可以在线浏览完整的Javadoc。关键 API 如下所述:

9.1 Session

Session是名称值对的简化Map

典型用法如下所示:

public class RepositoryDemo<S extends Session> {
	private SessionRepository<S> repository; (1)

	public void demo() {
		S toSave = this.repository.createSession(); (2)

		(3)
		User rwinch = new User("rwinch");
		toSave.setAttribute(ATTR_USER, rwinch);

		this.repository.save(toSave); (4)

		S session = this.repository.findById(toSave.getId()); (5)

		(6)
		User user = session.getAttribute(ATTR_USER);
		assertThat(user).isEqualTo(rwinch);
	}

	// ... setter methods ...
}
  • (1) 我们创建一个通用类型SSessionRepository实例,该实例扩展了Session。泛型类型在我们的类中定义。
  • (2) 我们使用SessionRepository创建一个新的Session并将其分配给S类型的变量。
  • (3) 我们与Session互动。在我们的示例中,我们演示了将User保存到Session
  • (4) 我们现在保存Session。这就是为什么我们需要通用类型S的原因。 SessionRepository仅允许保存使用相同SessionRepository创建或检索的Session实例。这允许SessionRepository进行特定于实现的优化(即仅写入已更改的属性)。
  • (5) 我们从SessionRepository检索Session
  • (6) 我们从Session获得了持久化的User,而无需显式转换属性。

Session API 还提供与Session实例的到期相关的属性。

典型用法如下所示:

public class ExpiringRepositoryDemo<S extends Session> {
	private SessionRepository<S> repository; (1)

	public void demo() {
		S toSave = this.repository.createSession(); (2)
		// ...
		toSave.setMaxInactiveInterval(Duration.ofSeconds(30)); (3)

		this.repository.save(toSave); (4)

		S session = this.repository.findById(toSave.getId()); (5)
		// ...
	}

	// ... setter methods ...
}
  • (1) 我们创建一个通用类型SSessionRepository实例,该实例扩展了Session。泛型类型在我们的类中定义。
  • (2) 我们使用SessionRepository创建一个新的Session并将其分配给S类型的变量。
  • (3) 我们与Session互动。在我们的示例中,我们演示了更新Session可以在其失效之前处于非活动状态的时间。
  • (4) 我们现在保存Session。这就是为什么我们需要通用类型S的原因。 SessionRepository仅允许保存使用相同SessionRepository创建或检索的Session实例。这允许SessionRepository进行特定于实现的优化(即仅写入已更改的属性)。保存Session后,上次访问的时间会自动更新。
  • (5) 我们从SessionRepository检索Session。如果Session过期,则结果为 null。

9.2 SessionRepository

SessionRepository负责创建,检索和保留Session实例。

如果可能,开发人员不应直接与SessionRepositorySession进行交互。相反,开发人员应该更喜欢通过HttpSessionWebSocket集成与SessionRepositorySession间接交互。

9.3 FindByIndexNameSessionRepository

Spring Session 使用Session的最基本 API 是SessionRepository。该 API 故意非常简单,因此很容易为其他实现提供基本功能。

一些SessionRepository实现可能也选择实现FindByIndexNameSessionRepository。例如,Spring 的 Redis,JDBC 和 Hazelcast 支持都实现FindByIndexNameSessionRepository

FindByIndexNameSessionRepository提供了一种查找具有给定索引名称和索引值的所有会话的方法。作为所有提供的FindByIndexNameSessionRepository实现都支持的常见用例,有一种便捷的方法可以为特定用户查找所有会话。通过确保使用用户名填充名称为FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME的会话属性来完成此操作。由于 Spring Session 不知道所使用的身份验证机制,因此开发人员有责任确保填充属性。下面显示了如何使用此示例:

String username = "username";
this.session.setAttribute(
		FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, username);

Note

FindByIndexNameSessionRepository的某些实现将提供钩子以自动索引其他会话属性。例如,许多实现将自动确保当前的 Spring Security 用户名使用索引名FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME进行索引。

会话构建索引后,可以使用以下命令找到它:

String username = "username";
Map<String, Session> sessionIdToSession = this.sessionRepository
		.findByPrincipalName(username);

9.4 ReactiveSessionRepository

ReactiveSessionRepository负责以非阻塞和反应方式创建,检索和持久保存Session实例。

如果可能,开发人员不应直接与ReactiveSessionRepositorySession进行交互。相反,开发人员应该更喜欢通过WebSession集成与ReactiveSessionRepositorySession间接交互。

9.5 EnableSpringHttpSession

可以将@EnableSpringHttpSession注解添加到@Configuration类,以将SessionRepositoryFilter作为名为“ springSessionRepositoryFilter”的 bean 公开。为了利用 Comments,必须提供单个SessionRepository bean。例如:

@EnableSpringHttpSession
@Configuration
public class SpringHttpSessionConfig {
	@Bean
	public MapSessionRepository sessionRepository() {
		return new MapSessionRepository(new ConcurrentHashMap<>());
	}
}

重要的是要注意,没有为您配置开箱即用的会话过期基础结构。这是因为会话期满之类的东西在很大程度上取决于实现。这意味着,如果您需要清理过期的会话,则您有责任清理过期的会话。

9.6 EnableSpringWebSession

可以将@EnableSpringWebSessionComments 添加到@Configuration类中,以将WebSessionManager作为名为“ webSessionManager”的 bean 公开。为了利用 Comments,必须提供单个ReactiveSessionRepository bean。例如:

@EnableSpringWebSession
public class SpringWebSessionConfig {
	@Bean
	public ReactiveSessionRepository reactiveSessionRepository() {
		return new ReactiveMapSessionRepository(new ConcurrentHashMap<>());
	}
}

重要的是要注意,没有为您配置开箱即用的会话过期基础结构。这是因为会话期满之类的东西在很大程度上取决于实现。这意味着,如果您需要清理过期的会话,则您有责任清理过期的会话。

9.7 RedisOperationsSessionRepository

RedisOperationsSessionRepository是使用 Spring Data 的RedisOperations实现的SessionRepository。在 Web 环境中,通常与SessionRepositoryFilter结合使用。该实现支持SessionDestroyedEventSessionCreatedEventSessionMessageListener

9.7.1 实例化 RedisOperationsSessionRepository

下面是如何创建新实例的典型示例:

RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

// ... configure redisTemplate ...

SessionRepository<? extends Session> repository =
		new RedisOperationsSessionRepository(redisTemplate);

有关如何创建RedisConnectionFactory的其他信息,请参阅《 Spring Data Redis 参考》。

9.7.2 EnableRedisHttpSession

在 Web 环境中,创建新RedisOperationsSessionRepository的最简单方法是使用@EnableRedisHttpSession。完整的示例用法可在第 3 章,示例和指南(从此处开始)中找到。您可以使用以下属性来自定义配置:

  • maxInactiveIntervalInSeconds -会话过期前的时间(以秒为单位)

  • redisNamespace –允许为会话配置应用程序特定的名称空间。 Redis 键和通道 ID 的前缀为<redisNamespace>:

  • redisFlushMode -允许指定何时将数据写入 Redis。仅当在SessionRepository上调用save时才是默认值。值RedisFlushMode.IMMEDIATE将尽快写入 Redis。

Custom RedisSerializer

您可以通过创建一个实现springSessionDefaultRedisSerializer的名为springSessionDefaultRedisSerializer的 Bean 来自定义序列化。

9.7.3 Redis TaskExecutor

订阅RedisOperationsSessionRepository以使用RedisMessageListenerContainer从 redis 接收事件。您可以通过创建名为springSessionRedisTaskExecutor和/或 Bean springSessionRedisSubscriptionExecutor的 Bean 来自定义这些事件的调度方式。有关配置 Redis 任务执行程序的更多详细信息,请参见here

9.7.4 存储详细信息

以下各节概述了如何为每个操作更新 Redis。可以在下面找到创建新会话的示例。以下各节描述了详细信息。

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
	maxInactiveInterval 1800 \
	lastAccessedTime 1404360000000 \
	sessionAttr:attrName someAttrValue \
	sessionAttr2:attrName someAttrValue2
EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100
APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800
SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100

保存会话

每个会话都作为哈希存储在 Redis 中。使用 HMSET 命令设置和更新每个会话。每个会话如何存储的示例如下所示。

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe creationTime 1404360000000 \
	maxInactiveInterval 1800 \
	lastAccessedTime 1404360000000 \
	sessionAttr:attrName someAttrValue \
	sessionAttr2:attrName someAttrValue2

在此示例中,关于会话的以下语句是正确的:

  • 会话 ID 为 33fdd1b6-b496-4b33-9f7d-df96679d32fe

  • 该会话的创建时间为格林尼治标准时间 1970 年 1 月 1 日午夜以来的 1404360000000.

  • 会话将在 1800 秒(30 分钟)后过期。

  • 自格林尼治标准时间 1970 年 1 月 1 日午夜以来,该会话的最后访问时间为 1404360000000.

  • 会话具有两个属性。第一个是值为“ someAttrValue”的“ attrName”。第二个会话属性被命名为“ attrName2”,其值为“ someAttrValue2”。

Optimized Writes

RedisOperationsSessionRepositoryManagement 的Session实例会跟踪已更改的属性,并且只会更新这些属性。这意味着,如果一次写入一个属性并读取多次,我们只需要写入一次该属性。例如,假设先前的会话属性“ sessionAttr2”已更新。保存后将执行以下操作:

HMSET spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe sessionAttr:attrName2 newValue

Session Expiration

使用基于Session.getMaxInactiveInterval()的 EXPIRE 命令将到期与每个会话相关联。例如:

EXPIRE spring:session:sessions:33fdd1b6-b496-4b33-9f7d-df96679d32fe 2100

您将注意到,所设置的到期时间是会话实际到期后的 5 分钟。这是必需的,以便在会话过期时可以访问该会话的值。会话本身实际上会过期五分钟后才在会话本身上设置一个过期,以确保将其清除,但是仅在我们执行了必要的处理之后。

Note

SessionRepository.findById(String)方法可确保不会返回任何过期的会话。这意味着在使用会话之前无需检查到期时间。

Spring Session 依靠从 Redis 删除和过期keyspace notifications分别触发SessionDeletedEventSessionExpiredEventSessionDeletedEventSessionExpiredEvent确保与会话相关联的资源被清除。例如,当使用 Spring Session 的 WebSocket 支持时,Redis 过期或删除事件将触发与该会话关联的所有 WebSocket 连接被关闭。

不会直接在会话密钥本身上跟踪到期时间,因为这将意味着会话数据将不再可用。而是使用特殊的会话过期密钥。在我们的示例中,expires 键是:

APPEND spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe ""
EXPIRE spring:session:sessions:expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe 1800

当会话过期时,密钥将被删除或过期,密钥空间通知将触发对实际会话的查找,并触发 SessionDestroyedEvent。

完全依赖 Redis 到期的一个问题是,Redis 无法保证如果尚未访问密钥,则何时触发过期事件。特别是,Redis 用于清除过期密钥的后台任务是低优先级任务,可能不会触发密钥过期。有关更多详细信息,请参见 Redis 文档中的过期事件的时间部分。

为了规避不能保证发生过期事件的事实,我们可以确保在预期每个密钥都到期时可以访问每个密钥。这意味着,如果密钥上的 TTL 过期,当我们尝试访问密钥时,Redis 将删除密钥并触发过期事件。

因此,每个会话的到期时间也会被追踪到最近的分钟。这允许后台任务访问可能过期的会话,以确保以更确定的方式触发 Redis 过期事件。例如:

SADD spring:session:expirations:1439245080000 expires:33fdd1b6-b496-4b33-9f7d-df96679d32fe
EXPIRE spring:session:expirations1439245080000 2100

然后,后台任务将使用这些 Map 来显式请求每个键。通过访问密钥而不是删除密钥,我们确保 Redis 仅在 TTL 过期时才为我们删除密钥。

Note

我们不会显式删除密钥,因为在某些情况下,可能会出现竞争情况,错误地将密钥标识为未过期。缺少使用分布式锁(这会损害我们的性能)的方法无法确保到期 Map 的一致性。通过简单地访问密钥,我们确保仅在该密钥上的 TTL 过期时才删除该密钥。

9.7.5 SessionDeletedEvent 和 SessionExpiredEvent

SessionDeletedEventSessionExpiredEvent都是SessionDestroyedEvent的类型。

RedisOperationsSessionRepository支持在删除Session或在SessionExpiredEvent过期时触发SessionDeletedEvent。这是确保正确清理与Session关联的资源所必需的。

例如,与 WebSockets 集成时,SessionDestroyedEvent负责关闭所有活动的 WebSocket 连接。

可以通过监听Redis 键空间事件SessionMessageListener来触发SessionDeletedEventSessionExpiredEvent。为了使其正常工作,需要启用通用命令的 Redis 键空间事件和过期事件。例如:

redis-cli config set notify-keyspace-events Egx

如果您使用的是@EnableRedisHttpSessionSessionMessageListener并启用必要的 Redis Keyspace 事件,则会自动完成。但是,在安全的 Redis 环境中,禁用 config 命令。这意味着 Spring Session 无法为您配置 Redis Keyspace 事件。要禁用自动配置,请将ConfigureRedisAction.NO_OP作为 bean 添加。

例如,Java 配置可以使用以下内容:

@Bean
public static ConfigureRedisAction configureRedisAction() {
	return ConfigureRedisAction.NO_OP;
}

XML 配置可以使用以下内容:

<util:constant
	static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>

9.7.6 SessionCreatedEvent

创建会话后,会使用spring:session:channel:created:33fdd1b6-b496-4b33-9f7d-df96679d32fe通道将事件发送到 Redis,使得33fdd1b6-b496-4b33-9f7d-df96679d32fe是会话 ID。事件的主体将是创建的会话。

如果注册为 MessageListener(默认),则RedisOperationsSessionRepository会将 Redis 消息转换为SessionCreatedEvent

9.7.7 在 Redis 中查看会话

installing redis-cli之后,您可以检查 Redis 使用 redis-cli中的值。例如,在终端中 Importing 以下内容:

$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
2) "spring:session:expirations:1418772300000" (2)
  • (1) 此项的后缀是 Spring Session 的会话标识符。
  • (2) 此项包含在1418772300000时间应删除的所有会话 ID。

您还可以查看每个会话的属性。

redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"

9.8 ReactiveRedisOperationsSessionRepository

ReactiveRedisOperationsSessionRepository是使用 Spring Data 的ReactiveRedisOperations实现的ReactiveSessionRepository。在 Web 环境中,通常与WebSessionStore结合使用。

9.8.1 实例化 ReactiveRedisOperationsSessionRepository

下面是如何创建新实例的典型示例:

// ... create and configure connectionFactory and serializationContext ...

ReactiveRedisTemplate<String, Object> redisTemplate = new ReactiveRedisTemplate<>(
		connectionFactory, serializationContext);

ReactiveSessionRepository<? extends Session> repository =
		new ReactiveRedisOperationsSessionRepository(redisTemplate);

有关如何创建ReactiveRedisConnectionFactory的其他信息,请参阅《 Spring Data Redis 参考》。

9.8.2 EnableRedisWebSession

在 Web 环境中,创建新的ReactiveRedisOperationsSessionRepository的最简单方法是使用@EnableRedisWebSession。您可以使用以下属性来自定义配置:

  • maxInactiveIntervalInSeconds -会话过期前的时间(以秒为单位)

  • redisNamespace –允许为会话配置应用程序特定的名称空间。 Redis 键和通道 ID 的前缀为<redisNamespace>:

  • redisFlushMode -允许指定何时将数据写入 Redis。仅当在ReactiveSessionRepository上调用save时才是默认值。值RedisFlushMode.IMMEDIATE将尽快写入 Redis。

Optimized Writes

ReactiveRedisOperationsSessionRepositoryManagement 的Session实例会跟踪已更改的属性,并且只会更新这些属性。这意味着,如果一次写入一个属性并读取多次,我们只需要写入一次该属性。

9.8.3 在 Redis 中查看会话

installing redis-cli之后,您可以检查 Redis 使用 redis-cli中的值。例如,在终端中 Importing 以下内容:

$ redis-cli
redis 127.0.0.1:6379> keys *
1) "spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021" (1)
  • (1) 此项的后缀是 Spring Session 的会话标识符。

您还可以查看每个会话的属性。

redis 127.0.0.1:6379> hkeys spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021
1) "lastAccessedTime"
2) "creationTime"
3) "maxInactiveInterval"
4) "sessionAttr:username"
redis 127.0.0.1:6379> hget spring:session:sessions:4fc39ce3-63b3-4e17-b1c4-5e1ed96fb021 sessionAttr:username
"\xac\xed\x00\x05t\x00\x03rob"

9.9 MapSessionRepository

MapSessionRepository允许将Session保留在Map中,其中密钥为Session ID,值为Session。该实现可以与ConcurrentHashMap一起用作测试或便利机制。或者,它可以与分布式Map实现一起使用。例如,它可以与 Hazelcast 一起使用。

9.9.1 实例化 MapSessionRepository

创建一个新实例非常简单:

SessionRepository<? extends Session> repository = new MapSessionRepository(
		new ConcurrentHashMap<>());

9.9.2 使用 Spring Session 和 Hazlecast

Hazelcast Sample是一个完整的应用程序,演示了将 Spring Session 与 Hazelcast 一起使用。

要运行它,请使用以下命令:

./gradlew :samples:hazelcast:tomcatRun

榛果 Springsample是一个完整的应用程序,演示了将 Spring Session 与 Hazelcast 和 Spring Security 一起使用。

它包括示例 Hazelcast MapListener实现,它们支持触发SessionCreatedEventSessionDeletedEventSessionExpiredEvent

要运行它,请使用以下命令:

./gradlew :samples:hazelcast-spring:tomcatRun

9.10 ReactiveMapSessionRepository

ReactiveMapSessionRepository允许将Session保留在Map中,其中密钥为Session ID,值为Session。该实现可以与ConcurrentHashMap一起用作测试或便利机制。或者,它可以与分布式Map实现一起使用,要求提供的Map必须是非阻塞的。

9.11 JdbcOperationsSessionRepository

JdbcOperationsSessionRepositorySessionRepository实现,它使用 Spring 的JdbcOperations将会话存储在关系数据库中。在 Web 环境中,通常与SessionRepositoryFilter结合使用。请注意,此实现不支持发布会话事件。

9.11.1 实例化 JdbcOperationsSessionRepository

下面是如何创建新实例的典型示例:

JdbcTemplate jdbcTemplate = new JdbcTemplate();

// ... configure JdbcTemplate ...

PlatformTransactionManager transactionManager = new DataSourceTransactionManager();

// ... configure transactionManager ...

SessionRepository<? extends Session> repository =
		new JdbcOperationsSessionRepository(jdbcTemplate, transactionManager);

有关如何创建和配置JdbcTemplatePlatformTransactionManager的其他信息,请参考Spring 框架参考文档

9.11.2 EnableJdbcHttpSession

在 Web 环境中,创建新JdbcOperationsSessionRepository的最简单方法是使用@EnableJdbcHttpSession。完整的示例用法可在第 3 章,示例和指南(从此处开始)中找到。您可以使用以下属性来自定义配置:

  • tableName -Spring Session 用于存储会话的数据库表的名称

  • maxInactiveIntervalInSeconds -会话过期前的时间(以秒为单位)

Custom LobHandler

您可以通过创建实现springSessionLobHandler的名为springSessionLobHandler的 Bean 来自定义 BLOB 处理。

Custom ConversionService

您可以通过提供ConversionService实例来自定义会话的默认序列化和反序列化。在典型的 Spring 环境中工作时,默认的ConversionService Bean(名为conversionService)将被自动拾取并用于序列化和反序列化。但是,您可以通过提供一个名为springSessionConversionService的 Bean 来覆盖默认的ConversionService

9.11.3 存储详细信息

默认情况下,此实现使用SPRING_SESSIONSPRING_SESSION_ATTRIBUTES表存储会话。注意,表名可以很容易地自定义,如前所述。在这种情况下,将使用提供的表名(后缀_ATTRIBUTES)来命名用于存储属性的表。如果需要进一步的自定义,则可以使用set*Query setter 方法自定义存储库使用的 SQL 查询。在这种情况下,您需要手动配置sessionRepository bean。

由于各个数据库供应商之间的差异,尤其是在存储二进制数据时,请确保使用特定于数据库的 SQL 脚本。大多数主要数据库供应商的脚本打包为org/springframework/session/jdbc/schema-*.sql,其中*是目标数据库类型。

例如,对于 PostgreSQL 数据库,您将使用以下模式脚本:

CREATE TABLE SPRING_SESSION (
	PRIMARY_ID CHAR(36) NOT NULL,
	SESSION_ID CHAR(36) NOT NULL,
	CREATION_TIME BIGINT NOT NULL,
	LAST_ACCESS_TIME BIGINT NOT NULL,
	MAX_INACTIVE_INTERVAL INT NOT NULL,
	EXPIRY_TIME BIGINT NOT NULL,
	PRINCIPAL_NAME VARCHAR(100),
	CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE SPRING_SESSION_ATTRIBUTES (
	SESSION_PRIMARY_ID CHAR(36) NOT NULL,
	ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
	ATTRIBUTE_BYTES BYTEA NOT NULL,
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
);

并带有 MySQL 数据库:

CREATE TABLE SPRING_SESSION (
	PRIMARY_ID CHAR(36) NOT NULL,
	SESSION_ID CHAR(36) NOT NULL,
	CREATION_TIME BIGINT NOT NULL,
	LAST_ACCESS_TIME BIGINT NOT NULL,
	MAX_INACTIVE_INTERVAL INT NOT NULL,
	EXPIRY_TIME BIGINT NOT NULL,
	PRINCIPAL_NAME VARCHAR(100),
	CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE SPRING_SESSION_ATTRIBUTES (
	SESSION_PRIMARY_ID CHAR(36) NOT NULL,
	ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
	ATTRIBUTE_BYTES BLOB NOT NULL,
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
	CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

9.11.4 TransactionManagement

JdbcOperationsSessionRepository中的所有 JDBC 操作都以事务方式执行。在传播设置为REQUIRES_NEW的情况下执行事务,以避免由于对现有事务的干扰而导致意外行为(例如,在已经参与只读事务的线程中执行save操作)。

9.12 HazelcastSessionRepository

HazelcastSessionRepositorySessionRepository实现,它将会话存储在 Hazelcast 的分布式IMap中。在 Web 环境中,通常与SessionRepositoryFilter结合使用。

9.12.1 实例化 HazelcastSessionRepository

下面是如何创建新实例的典型示例:

Config config = new Config();

// ... configure Hazelcast ...

HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(config);

HazelcastSessionRepository repository =
		new HazelcastSessionRepository(hazelcastInstance);

有关如何创建和配置 Hazelcast 实例的其他信息,请参阅Hazelcast documentation

9.12.2 EnableHazelcastHttpSession

如果您希望使用Hazelcast作为SessionRepository的后备源,则可以将@EnableHazelcastHttpSessionComments 添加到@Configuration类中。这扩展了@EnableSpringHttpSession注解提供的功能,但在 Hazelcast 中为您提供了SessionRepository。您必须提供一个HazelcastInstance bean 才能使配置生效。完整的配置示例可以在第 3 章,示例和指南(从此处开始)中找到

9.12.3 基本自定义

您可以在@EnableHazelcastHttpSession上使用以下属性来自定义配置:

  • maxInactiveIntervalInSeconds -会话过期之前的时间(以秒为单位)。默认值为 1800 秒(30 分钟)

  • sessionMapName -分布式Map的名称,将在 Hazelcast 中用于存储会话数据。

9.12.4 会话事件

使用MapListener响应添加,删除和从分布式Map中删除的条目,这些事件将分别触发使用ApplicationEventPublisher发布SessionCreatedEventSessionExpiredEventSessionDeletedEvent事件。

9.12.5 存储详细信息

会话将存储在 Hazelcast 中的分布式IMap中。 IMap接口方法将用于get()put()会话。另外,values()方法用于支持FindByIndexNameSessionRepository#findByIndexNameAndIndexValue操作,以及需要在 Hazelcast 中注册的适当的ValueExtractor。有关此配置的更多详细信息,请参考榛果 SpringsampleIMap中会话的到期由 Hazelcast 支持,用于设置将条目put() IMap保留在条目上的生存时间。闲置时间超过生存时间的条目(会话)将自动从IMap中删除。

您无需在 Hazelcast 配置中为IMap配置任何设置,例如max-idle-secondstime-to-live-seconds

请注意,如果您使用 Hazelcast 的MapStore来保留会话IMap,则从MapStore重新加载会话时会受到一些限制:

  • 重新加载触发器EntryAddedListener,导致SessionCreatedEvent重新发布

  • 重新加载为给定的IMap使用默认的 TTL,导致会话丢失其原始 TTL