问题出现的原因: 使用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());

到这里,问题解决了,知识学到了,那就点个赞来个收藏吧!!!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐