- 在首次获取session后,将sessionid作为键将对应session域信息序列化后保存到redis数据库内
- 需要获取session域信息时,拿着request携带的cookie中的sessionId值向redis数据库查找对应的数据,并反序列化为Session对象
/**
* 保存session
*
* @param req 请求信心
* @return 结果
*/
@GetMapping("/saveSession")
public AjaxResult saveSession(HttpServletRequest req){
System.out.println(new Date()+":::"+req.getSession().getId());
HttpSession session = req.getSession();
session.setAttribute("num",num++);
String sessionString = JSON.toJSONString(session);
redisFullUtil.set(session.getId(),sessionString);
return AjaxResult.success(session.getId()+"::"+session.getAttribute("num"));
}
/**
* 获取保存的session
*
* @param req 请求信息
* @return 结果
*/
@GetMapping("/getSession")
public AjaxResult getSession(HttpServletRequest req){
System.out.println(new Date()+":::"+req.getSession().getId());
String sessionString = redisFullUtil.get(req.getSession().getId());
HttpSession session = JSON.parseObject(sessionString, HttpSession.class);
return AjaxResult.success(session.getAttribute("num"));
}
程序运行后直接报错
原因是redis序列化依赖于set和get方法,session中有些数据属性是没有的,
spring-session-data-redis
转变思路一番搜索,现有技术是如何实现session分离,发现spring-session-data-redis就是专门做这件事的,还帮我们完成了大部分的工作,只需要做一些简答的配置工作即可完成session的分离
1. pom引入依赖
<!-- spring boot 与 redis 应用的基本配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring session 与redis 应用环境基本配置, 需要开启redis后才可以使用, 不然启动会报错 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2. 配置: application.yml
spring:
redis:
database: 0
host: 你的redisip
port: 6379
password: 你的redis密码
pool:
max-idle: 8
min-idle: 0
max-active: 8
max-wait: 1
timeout: 5000
session:
store-type: redis
redis:
hostname: 你的redisip
port: 6379
pawssword: 你的redis密码
3. 增加配置类
SessionConfig.Class
package org.huber.sharesession.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.DefaultCookieSerializer;
import javax.validation.Valid;
/**
* @Classname SessionConfig
* @Description TODO
* @Date 2020/1/17 12:43
* @Author by Ren Jie
*/
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class SessionConfig {
@Value("{$redis.hostname}")
private String hostName;
@Value("${redis.port}")
private int port;
@Bean
public RedisStandaloneConfiguration connectionFactory() {
RedisStandaloneConfiguration jedisConnectionFactory = new RedisStandaloneConfiguration();
jedisConnectionFactory.setHostName(hostName);
jedisConnectionFactory.setPort(port);
return jedisConnectionFactory;
}
@Bean
public DefaultCookieSerializer defaultCookieSerializer(){
DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
defaultCookieSerializer.setCookiePath("/");
return defaultCookieSerializer;
}
}
核心就在这个反序列化上
package org.huber.sharesession.config;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
/**
* @Classname SessionInitializer
* @Description TODO
* @Date 2020/1/17 12:47
* @Author by Ren Jie
*/
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {
public SessionInitializer(){
super(SessionConfig.class);
}
}
结果测试
- 开启会话,获取sessionid
- 重启服务器
- 再次访问后台,获取sessionid
如果是同一个sessionid,则说明session分离成功
package org.huber.sharesession.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @Classname SessionController
* @Description TODO
* @Date 2020/1/17 12:49
* @Author by Ren Jie
*/
@RestController
@RequestMapping("/shareSession/session")
public class SessionController {
@GetMapping("/setSession")
public String setSession(HttpServletRequest req){
String sessionId = req.getSession().getId();
System.out.println("sessionId = " + sessionId);
return sessionId;
}
}
演示如下:
完了还是可能有问题:
我们来看看redis的序列化:
对redis进行配置,实现发序列化,因为reids序列化已经实现了,这里就不用再对序列化进行研究了(redis序列化见文章:DefaultSerializer requires a Serializable payload but received an object of type [select.system.dto.User] - 叶语婷 - 博客园)。
这里对key和value分别进行反序列化,进而在redis中生成字符型的key和对象型value。
/**
* @author yeyuting
* @create 2021/2/19
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(new JdkSerializationRedisSerializer());
//value hashmap序列化
template.setHashValueSerializer(redisSerializer);
//key haspmap序列化
template.setHashKeySerializer(redisSerializer);
return template;
}
}
执行后redis中是这样的
可知,key反序列话成功了,但是value对象反序列化失败了,接着就将注意力转移到value对象的反序列化中来。
当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。 用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。我们要求是存储的数据可以方便查看,也方便反系列化,方便读取数据。JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer,两者都能系列化成json,但是后者会在json中加入@class属性,类的全路径包名,方便反系列化。前者如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to 。
因此保险起见,我们将JdkSerializationRedisSerializer换成GenericJackson2JsonRedisSerializer,修改后的代码如下:
/**
* @author yeyuting
* @create 2021/2/19
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//value hashmap序列化
template.setHashValueSerializer(redisSerializer);
return template;
}
}
执行后redis缓存内容正常显示,如下:
原理就是将httpSession替换成自己实现的httpsession
参考:1、Redis应用:session分离 【session序列化失败 JSONException异常 spring-session-data-redis实现】_奋斗zhe的博客-CSDN博客
2、Java简单实现session保存到redis的方法示例_李修睿的博客-CSDN博客_java session存入redis
3、