问题出现的原因: 使用springsession在设置属性自动添加到redis后,它会在值前面出现16进制的乱码,即使配置了redis序列化配置也没用,因为springsession内部自己创建了一个redis,默认使用的是new JdkSerializationRedisSerializer();,所以要想解决这个问题就要从根源入手!
解决问题的最好办法就是明白它的原理,那么我们就从springsession下手一步步解决问题
想要使用session肯定第一步就要获取它
// request 是HttpServletRequest类型,通过方法传参获得
HttpSession session = request.getSession();
来让我们深入下去
我们可以知道HttpServletRequest是一个接口,那么肯定有实现类
那么在这么多的实现类中,到底是哪一个那?
既然你想使用springsession,那么实现类肯定与这个有关,为什么就能使用这类,而不是别的类那?由于道行原因,现在未能明白其原理,但是我在实现类上找到了order注解,默认值为0+50 ,不知是不是这个原因!
让我们看一下这个类
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
@Override
public HttpSessionWrapper getSession(boolean create) { // create = true
// 获取当前session
HttpSessionWrapper currentSession = getCurrentSession();
if (currentSession != null) {
return currentSession;
}
// 获取一个session
S requestedSession = getRequestedSession();
//不为空 为其设置一些东西
if (requestedSession != null) {
if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
requestedSession.setLastAccessedTime(Instant.now());
this.requestedSessionIdValid = true;
currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
currentSession.markNotNew();
setCurrentSession(currentSession);
return currentSession;
}
}
else {
// This is an invalid session id. No need to ask again if
// request.getSession is invoked for the duration of this request
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER.debug(
"No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
}
setAttribute(INVALID_SESSION_ID_ATTR, "true");
}
if (!create) {
return null;
}
// session日志是否打开
if (SESSION_LOGGER.isDebugEnabled()) {
SESSION_LOGGER.debug(
"A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "
+ SESSION_LOGGER_NAME,
new RuntimeException("For debugging purposes only (not an error)"));
}
// 当我们第一次登陆时肯定是运行这一步
// 获取session
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
// 设置session超时时间
session.setLastAccessedTime(Instant.now());
// 创建当前session 其实是使用HttpSessionWrapper 将其与Servlet上下文封装起来
currentSession = new HttpSessionWrapper(session, getServletContext());
setCurrentSession(currentSession);
return currentSession;
}
@Override
public HttpSessionWrapper getSession() {
return getSession(true);
}
}
}
}
那么说了这么半天,跟标题怎么没什么关系阿!
各位别急,马上就到了!!!
由此我们可以知道,session是由
SessionRepositoryFilter.this.sessionRepository.createSession();
创建的,那么创建之前肯定是配置一些参数的,那么配置参数是不是就有配置序列化的方法。
public class RedisIndexedSessionRepository
implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {
// 上面的代码省略
private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer();
// 下面的代码省略
}
相信大家一眼就看出了,这里说明session在配置时,使用的默认序列化为JdkSerializationRedisSerializer。这也能说明为什么我们在使用session添加一个String字符串时,在redis里存在字符串前面有一串16进制乱码(请允许我这样称呼它)的原因。
既然找到了这个配置,那么我们是不是可以修改它那???
让我们回到spring本源,如果想填充类的属性,要么使用最终原理都是使用的元素的set方法(排除构造函数这种方式)。那么我们就寻找以下它的配置类咯
代码我只弄了重要的,如果向深入了解请自行查找
@Configuration(proxyBeanMethods = false)
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
// 默认序列化
private RedisSerializer<Object> defaultRedisSerializer;
// 看这个熟悉把,就是我们上面说的RedisIndexedSessionRepository类
// 只是这里使用了bean注解方式
@Bean
public RedisIndexedSessionRepository sessionRepository() {
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
sessionRepository.setApplicationEventPublisher(this.applicationEventPublisher);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
// 来来来
// 高潮开启
// 这里才是重点
// 看看,如果默认的序列化不为空那么就设置使用
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
}
sessionRepository.setDefaultMaxInactiveInterval(this.maxInactiveIntervalInSeconds);
if (StringUtils.hasText(this.redisNamespace)) {
sessionRepository.setRedisKeyNamespace(this.redisNamespace);
}
sessionRepository.setFlushMode(this.flushMode);
sessionRepository.setSaveMode(this.saveMode);
int database = resolveDatabase();
sessionRepository.setDatabase(database);
this.sessionRepositoryCustomizers
.forEach((sessionRepositoryCustomizer) -> sessionRepositoryCustomizer.customize(sessionRepository));
return sessionRepository;
}
// 看到这个大家们肯定就能猜出,解决办法是什么了吧!
@Autowired(required = false)
@Qualifier("springSessionDefaultRedisSerializer")
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
this.defaultRedisSerializer = defaultRedisSerializer;
}
}
没错,创建一个配置类设置序列化方式,然后让spring自动注入就行了
说干就干
@Configuration
public class RedisConfig {
// 这里是设置redis序列化方式,可以忽略
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
// 这里才是设置session序列化的方法
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
// 因为我使用的是FastJson
// 如果使用别的请自行设置,就是返回时返回序列化类即可
return new FastJsonRedisSerializer(Object.class);
}
}
那么值设置进去了,我说说我的是如何获取值的
// 使用JSON,字符串转类 由于stringRedisTemplate.get 返回的为byte[] 所以我强制转换成了String字符串!!!
UserVO userVO1 = JSON.parseObject((String) stringRedisTemplate.opsForHash().get(key, "sessionAttr:UserVo"), UserVO.class);
System.out.println(userVO1.getUsername());
到这里,问题解决了,知识学到了,那就点个赞来个收藏吧!!!