搭建一个简单的Spring Session例子

引入依赖包

    <dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.10.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>3.5.0.Final</version>
</dependency>
</dependencies>

注册Spring IoC、Spring Session和一些Servlet

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>Spring-Session-Redis</display-name> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring-session.xml
</param-value>
</context-param> <filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> <servlet>
<description></description>
<display-name>SessionTestServlet</display-name>
<servlet-name>SessionTestServlet</servlet-name>
<servlet-class>com.nicchagil.SessionTestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SessionTestServlet</servlet-name>
<url-pattern>/SessionTestServlet</url-pattern>
</servlet-mapping> </web-app>

最简单的Spring Session的Bean配置

<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:annotation-config /> <!-- Jedis连接工厂 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="nick-huang.example" />
<property name="port" value="6379" />
</bean> <bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="1800" />
</bean> </beans>

一个测试的Servlet

package com.nicchagil;

import java.io.IOException;
import java.util.logging.Logger; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; /**
* Servlet implementation class SessionTestServlet
*/
public class SessionTestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Logger logger = Logger.getLogger("SessionTestServlet"); /**
* Default constructor.
*/
public SessionTestServlet() {
// TODO Auto-generated constructor stub
} /**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(); String username = request.getParameter("username");
if (username != null && username.length() > 0) {
session.setAttribute("username", username);
} response.getWriter().append("Served at: ").append(request.getContextPath()).append(", userName : " + session.getAttribute("username"));
} /**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
} }

测试

启动,用浏览器访问该Servlet:http://127.0.0.1:8080/SessionTestServlet?username=123,然后不带参数可能获取用户名:http://127.0.0.1:8080/SessionTestServlet。

看下Redis,有没有持久化Session:

[root@blog ~]# /opt/redis-3.2.1/src/redis-cli
127.0.0.1:6379> keys *
1) "spring:session:sessions:expires:5b29c067-a4b1-4d51-98b2-be084703fc78"
2) "spring:session:sessions:5b29c067-a4b1-4d51-98b2-be084703fc78"
3) "spring:session:expirations:1497195000000"

接下来看下实现原理。

委托过滤器代理类,DelegatingFilterProxy

这个类不在Spring Session中,但它是我们上述例子配置的入口。

它是委托过滤器代理类,可以看到它的继承与实现关系:DelegatingFilterProxy -> GenericFilterBean -> Filter,其中GenericFilterBean的init()调用DelegatingFilterProxy的initFilterBean()。

initFilterBean()的作用是获取委托的过滤器,并调用委托过滤器的doFilter(),这个过滤器是springSessionRepositoryFilter

Spring Session主要配置类,RedisHttpSessionConfiguration

说到springSessionRepositoryFilter,那么它在哪里实例化的呢?我们先看看RedisHttpSessionConfiguration

RedisHttpSessionConfiguration,这是一个配置类,它注册了一些Spring Session所需的Bean。我们通过以下的xml显式注册一个配置Bean:

    <bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="1800" />
</bean>

这个类的继承关系是:RedisHttpSessionConfiguration -> SpringHttpSessionConfigurationSpringHttpSessionConfiguration有个方法叫springSessionRepositoryFilter,这里就是注册springSessionRepositoryFilterBean的地方。关于@Configuration、@Bean方式注册Bean,请点击这里

Session存储过滤器,SessionRepositoryFilter

它的继承关系是SessionRepositoryFilter -> OncePerRequestFilter -> Filter

OncePerRequestFilter的名字说明了其作用,看doFilter方法可知,它通过request中的一个Attribute判断是否已做过滤,以保证对于每次请求只做一次此过滤逻辑。如果此请求是首次进入此过滤,则调用SessionRepositoryFilter.doFilterInternal

SessionRepositoryFilter.doFilterInternal,将Servlet容器传入的Request和Response包装成自己封装的Request和Response,然后传给下一任Filter,后续的Filter和Servlet都使用Spring Session封装的Request和Response,此Request和Response分别继承HttpServletRequestWrapper、HttpServletResponseWrapper,并作了自己业务的覆盖。

HTTP请求包装类,SessionRepositoryRequestWrapper

SessionRepositoryRequestWrapper根据自身业务,覆盖了许多方法,这里不多讨论,简单举例,比如getSession(boolean)



commitSession()

Http请求与Session的关系策略,HttpSessionStrategy

HttpSessionStrategy接口定义了几个方法,另外有几个实现类,这里只讨论一部分:

// 从request获取请求的SessionID,比如SessionID有可能放在Cookie或请求头中
String getRequestedSessionId(HttpServletRequest request); // 当新Session被创建且应通知客户端新SessionID时此方法会被调用。此方法的实现可能为Cookie或响应头设置新SessionID,当然也可以设置其他信息
void onNewSession(Session session, HttpServletRequest request,
HttpServletResponse response); // 当Session销毁时且需通知客户端该SessionID不再有效时会调用此方法。此方法的实现可能为从Cookie或响应头移除SessionID。
void onInvalidateSession(HttpServletRequest request, HttpServletResponse response);

Cookie方式,CookieHttpSessionStrategy

getRequestedSessionId:

onNewSession:

onInvalidateSession:

HTTP请求头方式,HeaderHttpSessionStrategy

HeaderHttpSessionStrategy相对简单,根据头键值x-auth-token,从请求中读取sessionID,或设置响应头。

值得注意的是,销毁Session时onInvalidateSession设置响应头x-auth-token的值为空。

Session持久化,SessionRepository

这是持久化Session的接口,定义有几个方法:

// 创建能被此实现持久化的新Session。
S createSession(); // 持久化Session
void save(S session); // 根据ID查询Session
S getSession(String id); // 删除Session
void delete(String id);

最新文章

  1. jquery--常用的函数2
  2. 我为开源做贡献,网页正文提取——Html2Article
  3. 读Lua游戏开发实践指南
  4. 学习笔记:The Log(我所读过的最好的一篇分布式技术文章)
  5. (转)c语言学习volatile
  6. Spring3.0实现REST实例
  7. BZOJ 1297 迷路(矩阵)
  8. aix5下安装python和cx_Oracle
  9. 服务器卡死,重启报错: INFO: task blocked for more than 120 seconds
  10. .net core高性能通讯开源组件BeetleX
  11. Python学习笔记_1
  12. 对Java中properties类的理解
  13. 安装owncloud出现:Error while trying to create admin user: An exception occurred while executing
  14. Oracle 巡检命令
  15. 2019.03.25 bzoj2329: [HNOI2011]括号修复(fhq_treap)
  16. 【XSY1515】【GDKOI2016】小学生数学题 组合数学
  17. C语言宏定义##连接符和#符的使用
  18. 快速搭建python程序
  19. python基础(15)-socket网络编程&amp;socketserver
  20. Rails6.0 Beta版本1: Action Text的简单使用

热门文章

  1. C#正则验证字符串是否全是数字
  2. Silverlight for Windows Phone开发系列课程
  3. 最新的 CocoaPods 的使用教程(一)
  4. Nginx防盗链的3种方法 文件防盗链 图片防盗链 视频防盗链 linux防盗链
  5. python 生成图表
  6. Openssl aes加解密例程
  7. mongodb3.4 安装及用户名密码设置
  8. Fix Backup Database is terminating abnormally When performing a Farm Backup
  9. Shell脚本开发规范
  10. Python 文件 next() 方法