Spring session 添加HttpSessionListener
Contents
这时介绍的版本,是基于以下版本:
<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
还多了两个.
分别是以下四种事件类型:
- SessionCreatedEvent; Session创建
- SessionDeletedEvent; Session删除
- SessionDestroyedEvent; Session销毁
- SessionExpiredEvent; Session过期
事件的触发
查看源码org.springframework.session.data.redis.RedisOperationsSessionRepository
可知:
事件的触发,是由org.springframework.context.ApplicationEventPublisher
来发布事件的,它利用了Spring自带的事件模型来进行session的生命周期的事件发布。
Spring自带的事件模型例子
步骤:
创建一个自定义的事件,它要继承于
org.springframework.context.ApplicationEvent
,然后在这里定义事件的属性和方法创建一个自定义事件的发布器(广播器),实现
org.springframework.context.ApplicationEventPublisherAware
接口,或者@Autowire ApplicationEventPublisher applicationEventPublisher
,然后通过它来发布自定义事件.创建一个自定义事件的监听器,它要实现
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了。不过,这样子的模型,是同步的事件处理模型。
异步事件模型
默认的情况下,是同步的事件处理模型,想改为异步,则进行如下修改即可:
在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" />
在发布者(publisher)或者监听器(listener)相应的方法上,添加注解
@Async
即可.
注意
使用 Spring-session ,它会将 Session 的生命周期的事件,发布给所有集群中的 Tomcat 的,所以,如果你需要监听这些事件,而且事件的动作,只需要执行一次的话,那就要进行额外的处理了(比如,一个用户退出时,插入一条日志到数据库,如果不进行额外的处理的话,那你的集群里有多少个 Tomcat,就会插入多少条日志了,这可能并不是我们想要的)