这时介绍的版本,是基于以下版本:

  <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
            <version>1.1.0.M1</version>
  </dependency>

先上示例demo

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
			http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
			  http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.2.xsd">


    <context:annotation-config />

    <bean id="v2redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          p:host-name="${config.redis.host}" p:port="${redis.port}" p:use-pool="true" p:database="${redis.database}"
          p:password="${config.redis.password}"
            />

    <bean id="stringRedisSerializer"
          class="org.springframework.data.redis.serializer.StringRedisSerializer"/>

    <bean id="genericToStringSerializer" class="org.springframework.data.redis.serializer.GenericToStringSerializer">
        <constructor-arg type="java.lang.Class" value="java.lang.Object"></constructor-arg>
    </bean>

    <bean id="object" class="java.lang.Object"/>

    <bean id="v2redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
          p:connection-factory-ref="v2redisConnectionFactory"
          p:keySerializer-ref="stringRedisSerializer"
          p:valueSerializer-ref="genericToStringSerializer"
          p:hashKeySerializer-ref="stringRedisSerializer"
          p:hashValueSerializer-ref="genericToStringSerializer"/>

    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

    <!-- 2017-2-13 更新: -->
    <!-- YourHttpSessionListener 加上 @Component 注解即可,不需要下面的配置了,因为上面 RedisHttpSessionConfiguration 会自动将实现了
    HttpSessionListener 的 bean 注册 ,下面再注册的话,就会执行多次了 -->
    <!-- Http session Listener Start -->
    <!--<bean id="l" class="org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter">-->
        <!--<constructor-arg>-->
            <!--<list>-->
                <!--<ref bean="webHttpSessionListener" />-->
            <!--</list>-->
        <!--</constructor-arg>-->
    <!--</bean>-->

    <!--<bean class="org.springframework.security.web.session.HttpSessionEventPublisher"/>-->
    <!-- END -->
</beans>

实现原理

事件类型

通过查看源码,可知在包org.springframework.session.events下,有4个session生命周期的类型,比默认的javax.servlet.http.HttpSessionListener还多了两个. 分别是以下四种事件类型:

  1. SessionCreatedEvent; Session创建
  2. SessionDeletedEvent; Session删除
  3. SessionDestroyedEvent; Session销毁
  4. SessionExpiredEvent; Session过期

事件的触发

查看源码org.springframework.session.data.redis.RedisOperationsSessionRepository可知:

事件的触发,是由org.springframework.context.ApplicationEventPublisher来发布事件的,它利用了Spring自带的事件模型来进行session的生命周期的事件发布。

Spring自带的事件模型例子

步骤:

  1. 创建一个自定义的事件,它要继承于org.springframework.context.ApplicationEvent ,然后在这里定义事件的属性和方法

  2. 创建一个自定义事件的发布器(广播器),实现org.springframework.context.ApplicationEventPublisherAware接口,或者@Autowire ApplicationEventPublisher applicationEventPublisher,然后通过它来发布自定义事件.

  3. 创建一个自定义事件的监听器,它要实现org.springframework.context.ApplicationListener即可。

同步事件模型

默认情况下,就是同步的.

创建一个登录的事件:

public class TestLoginEvent extends ApplicationEvent {

    private int userId;

    public TestLoginEvent(int userId) {
        super(userId);
        this.userId = userId;
    }


    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

创建一个登录的事件的发布者:

@Service
public class TestLoginEventPubisher implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher applicationEventPublisher;


    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void login(int userId) {
        applicationEventPublisher.publishEvent(new TestLoginEvent(userId));
    }
}

创建一个登录事件的监听器:

@Service
public class TestLoginEventListener implements ApplicationListener<TestLoginEvent> {
    @Override
    public void onApplicationEvent(TestLoginEvent event) {
        int userId = event.getUserId();
        System.out.println("user " + userId + " login");
    }
}

这样子就OK了。不过,这样子的模型,是同步的事件处理模型。

异步事件模型

默认的情况下,是同步的事件处理模型,想改为异步,则进行如下修改即可:

  1. 在spring应用的配置文件里,添加以下配置:

    <context:annotation-config />
    <aop:aspectj-autoproxy />
    <aop:aspectj-autoproxy proxy-target-class="true" />
    
    <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
    <task:executor id="myExecutor" pool-size="10" />
    <task:scheduler id="myScheduler" pool-size="10" />
    
  2. 在发布者(publisher)或者监听器(listener)相应的方法上,添加注解 @Async 即可.

注意

使用 Spring-session ,它会将 Session 的生命周期的事件,发布给所有集群中的 Tomcat 的,所以,如果你需要监听这些事件,而且事件的动作,只需要执行一次的话,那就要进行额外的处理了(比如,一个用户退出时,插入一条日志到数据库,如果不进行额外的处理的话,那你的集群里有多少个 Tomcat,就会插入多少条日志了,这可能并不是我们想要的)