Redis缓存 -对象序列化及反序列化问题
1. 简介
- 项目框架:SSM + redis + jedis
- 使用场景:楼主遇到的场景是, 现有的项目架构中有n多个项目,现在的需求是把其中一个项目作为主项目,其他的则均是子项目,日后主项目中会在前端提供链接到子项目的地址,那么在这个过程中就涉及到session信息传递的问题。
- 解决思路:
1.前端处理:前端链接地址拼接用户名,密码,token等session信息参数
2.后端处理:使用分布式session的思想,刚好项目中有用到redis,将计就计使用redis实现分布式sesison共享
2. 前期准备
- 楼主这个项目比较老,没用maven,因此引入如下jar包(
redis开发和json开发相关jar
)
1.spring-data-redis-1.3.4.RELEASE.jar
2.jedis-2.5.2.jar
3.commons-pool2-2.4.2.jar
4.jackson-databind-2.3.1.jar
5.jackson-core-2.3.1.jar
6.jackson-annotations-2.3.0.jar
7.fastjson-1.1.33.jar
3. 具体开发流程
3.1 相关配置 (redis-bean.xml)
<?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:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd"
default-autowire="byName" >
<!-- redis配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxTotal}"/>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
<property name="testOnReturn" value="${redis.testOnReturn}"/>
</bean>
<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.password}" />
<property name="usePool" value="true" />
<property name="poolConfig" ref="poolConfig" />
</bean>
<!-- redis template definition -->
<!--<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory" />
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>-->
<!-- 此句不加则不会使用缓存 -->
<cache:annotation-driven cache-manager="cacheManager"/>
<bean id="redisCacheManager" class="com.***.***.cache.RedisCacheManager">
<constructor-arg name="template" ref="redisTemplate" />
<constructor-arg name="cacheNames" >
<set>
<value>sessionCache</value>
</set>
</constructor-arg>
<constructor-arg name="expires">
<map>
<entry key="thirdOpenCache" value="60"></entry>
<entry key="personCache" value="3600"></entry>
<entry key="defaultDeptCache" value="3600"></entry>
<entry key="DefaultCache" value="3600"></entry>
</map>
</constructor-arg>
</bean>
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
<property name="cacheManagers">
<list>
<ref bean="redisCacheManager" />
</list>
</property>
<property name="fallbackToNoOpCache" value="true" />
</bean>
</beans>
3.2 相关java类
CacheUtil
缓存工具类
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration("cacheUtil")
public class CacheUtil {
@Resource
private RedisCacheManager redisCacheManager;
public void add(String cacheName, Object key, Object value) {
RedisCache cache = (RedisCache) redisCacheManager.getCache(cacheName);
cache.put(key, value);
}
public Object get(String cacheName, Object key) {
RedisCache cache = (RedisCache) redisCacheManager.getCache(cacheName);
return cache.get(key, Object.class);
}
public void delete(String cacheName, Object key) {
RedisCache cache = (RedisCache) redisCacheManager.getCache(cacheName);
cache.evict(key);
}
}
RedisCache
具体功能及每个方法的作用后续解释
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.Set;
class RedisCache
implements Cache {
private static final int PAGE_SIZE = 128;
private final String name;
private final RedisTemplate template;
private final byte[] prefix;
private final byte[] setName;
private final byte[] cacheLockName;
private long WAIT_FOR_LOCK = 300L;
private final long expiration;
RedisCache(String name, byte[] prefix, RedisTemplate<? extends Object, ? extends Object> template, long expiration) {
Assert.hasText(name, "non-empty cache name is required");
this.name = name;
this.template = template;
this.prefix = prefix;
this.expiration = expiration;
StringRedisSerializer stringSerializer = new StringRedisSerializer();
setName = stringSerializer.serialize(name + "~keys");
cacheLockName = stringSerializer.serialize(name + "~lock");
}
public String getName() {
return name;
}
public Object getNativeCache() {
return template;
}
public Cache.ValueWrapper get(final Object key) {
return (Cache.ValueWrapper) template.execute(new RedisCallback() {
public Cache.ValueWrapper doInRedis(RedisConnection connection) throws DataAccessException {
RedisCache.this.waitForLock(connection);
byte[] bs = connection.get(RedisCache.this.computeKey(key));
Object value = template.getValueSerializer() != null ? template.getValueSerializer().deserialize(bs) : bs;
return bs == null ? null : new SimpleValueWrapper(value);
}
}, true);
}
public <T> T get(Object key, Class<T> type) {
Cache.ValueWrapper wrapper = get(key);
return wrapper == null ? null : (T) wrapper.get();
}
public void put(Object key, Object value) {
final byte[] keyBytes = computeKey(key);
final byte[] valueBytes = convertToBytesIfNecessary(template.getValueSerializer(), value);
template.execute(new RedisCallback() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
RedisCache.this.waitForLock(connection);
connection.multi();
connection.set(keyBytes, valueBytes);
connection.zAdd(setName, 0.0D, keyBytes);
if (expiration > 0L) {
connection.expire(keyBytes, expiration);
connection.expire(setName, expiration);
}
connection.exec();
return null;
}
}, true);
}
@Override
public ValueWrapper putIfAbsent(Object o, Object o1) {
return null;
}
public void evict(Object key) {
final byte[] k = computeKey(key);
template.execute(new RedisCallback() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
connection.del(new byte[][]{k});
connection.zRem(setName, new byte[][]{k});
return null;
}
}, true);
}
public void clear() {
template.execute(new RedisCallback() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
if (connection.exists(cacheLockName).booleanValue()) {
return null;
}
try {
connection.set(cacheLockName, cacheLockName);
int offset = 0;
boolean finished = false;
Set<byte[]> keys;
do {
keys = connection.zRange(setName, offset * 128, (offset + 1) * 128 - 1);
finished = keys.size() < 128;
offset++;
if (!keys.isEmpty()) {
connection.del((byte[][]) keys.toArray(new byte[keys.size()][]));
}
} while (!finished);
connection.del(new byte[][]{setName});
return null;
} finally {
connection.del(new byte[][]{cacheLockName});
}
}
}, true);
}
private byte[] computeKey(Object key) {
byte[] keyBytes = convertToBytesIfNecessary(template.getKeySerializer(), getName() + key);
if ((prefix == null) || (prefix.length == 0)) {
return keyBytes;
}
byte[] result = Arrays.copyOf(prefix, prefix.length + keyBytes.length);
System.arraycopy(keyBytes, 0, result, prefix.length, keyBytes.length);
return result;
}
private boolean waitForLock(RedisConnection connection) {
boolean foundLock = false;
boolean retry;
do {
retry = false;
if (connection.exists(cacheLockName).booleanValue()) {
foundLock = true;
try {
Thread.sleep(WAIT_FOR_LOCK);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
retry = true;
}
} while (retry);
return foundLock;
}
private byte[] convertToBytesIfNecessary(RedisSerializer<Object> serializer, Object value) {
if ((serializer == null) && ((value instanceof byte[]))) {
return (byte[]) value;
}
if (serializer != null) {
return serializer.serialize(value);
}
return new byte[0];
}
}
RedisCacheManage
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.cache.DefaultRedisCachePrefix;
import org.springframework.data.redis.cache.RedisCachePrefix;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class RedisCacheManager
extends AbstractCacheManager {
private final Log logger = LogFactory.getLog(RedisCacheManager.class);
private final RedisTemplate template;
private boolean usePrefix = false;
private RedisCachePrefix cachePrefix = new DefaultRedisCachePrefix();
private boolean loadRemoteCachesOnStartup = false;
private boolean dynamic = true;
private long defaultExpiration = 0L;
private Map<String, Long> expires = null;
public RedisCacheManager(RedisTemplate<?, ?> template) {
this(template, new ArrayList(0), new HashMap(0));
}
public RedisCacheManager(RedisTemplate<?, ?> template, Collection<String> cacheNames, Map<String, Long> expires) {
this.expires = expires;
this.template = template;
setCacheNames(cacheNames);
}
public Cache getCache(String name) {
Cache cache = super.getCache(name);
if ((cache == null) && (dynamic)) {
return createAndAddCache(name);
}
return cache;
}
public void setCacheNames(Collection<String> cacheNames) {
if (!CollectionUtils.isEmpty(cacheNames)) {
for (String cacheName : cacheNames) {
createAndAddCache(cacheName);
}
dynamic = false;
}
}
public void setUsePrefix(boolean usePrefix) {
this.usePrefix = usePrefix;
}
public void setCachePrefix(RedisCachePrefix cachePrefix) {
this.cachePrefix = cachePrefix;
}
public void setDefaultExpiration(long defaultExpireTime) {
defaultExpiration = defaultExpireTime;
}
public void setExpires(Map<String, Long> expires) {
this.expires = (expires != null ? new ConcurrentHashMap(expires) : null);
}
public void setLoadRemoteCachesOnStartup(boolean loadRemoteCachesOnStartup) {
this.loadRemoteCachesOnStartup = loadRemoteCachesOnStartup;
}
protected Collection<? extends Cache> loadCaches() {
Assert.notNull(template, "A redis template is required in order to interact with data store");
return addConfiguredCachesIfNecessary(loadRemoteCachesOnStartup ? loadAndInitRemoteCaches() : new ArrayList(0));
}
private Collection<? extends Cache> addConfiguredCachesIfNecessary(List<? extends Cache> caches) {
Assert.notNull(caches, "Caches must not be null!");
Collection<Cache> result = new ArrayList(caches);
for (String cacheName : getCacheNames()) {
boolean configuredCacheAlreadyPresent = false;
for (Cache cache : caches) {
if (cache.getName().equals(cacheName)) {
configuredCacheAlreadyPresent = true;
break;
}
}
if (!configuredCacheAlreadyPresent) {
result.add(getCache(cacheName));
}
}
return result;
}
private Cache createAndAddCache(String cacheName) {
addCache(createCache(cacheName));
return super.getCache(cacheName);
}
private RedisCache createCache(String cacheName) {
long expiration = computeExpiration(cacheName);
return new RedisCache(cacheName, usePrefix ? cachePrefix.prefix(cacheName) : null, template, expiration);
}
private long computeExpiration(String name) {
Long expiration = null;
if (expires != null) {
expiration = (Long) expires.get(name);
}
return expiration != null ? expiration.longValue() : defaultExpiration;
}
private List<RedisCache> loadAndInitRemoteCaches() {
List<RedisCache> caches = new ArrayList();
try {
Set<String> cacheNames = loadRemoteCacheKeys();
if (!CollectionUtils.isEmpty(cacheNames)) {
for (String cacheName : cacheNames)
if (null == super.getCache(cacheName))
caches.add(createCache(cacheName));
}
} catch (Exception e) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to initialize cache with remote cache keys.", e);
}
}
return caches;
}
private Set<String> loadRemoteCacheKeys() {
return (Set) template.execute(new RedisCallback() {
public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {
Set<byte[]> keys = connection.keys(template.getKeySerializer().serialize("*~keys"));
Set cacheKeys = new LinkedHashSet();
if (!CollectionUtils.isEmpty(keys)) {
for (byte[] key : keys) {
cacheKeys.add(template.getKeySerializer().deserialize(key).toString().replace("~keys", ""));
}
}
return cacheKeys;
}
});
}
}
通过上述几个类将缓存应用起来基本没问题,但是在用的过程中楼主发现,当缓存一些字符串类型数据时,没任何问题,当我准备获取缓存对象时,报错信息如下 (主项目缓存存储信息,其余子项目获取缓存信息):
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.myeye.ssm.session.UserSessionInfo
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:41)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:190)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:152)
at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:134)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:1025)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:445)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1136)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2555)
at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2544)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.myeye.ssm.session.UserSessionInfo
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:64)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:34)
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.deserialize(JdkSerializationRedisSerializer.java:39)
... 41 more
Caused by: org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException: com.myeye.ssm.session.UserSessionInfo
at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:44)
at org.springframework.core.serializer.support.DeserializingConverter.convert(DeserializingConverter.java:59)
... 43 more
Caused by: java.lang.ClassNotFoundException: com.myeye.ssm.session.UserSessionInfo
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1928)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1771)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:628)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1620)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
at org.springframework.core.serializer.DefaultDeserializer.deserialize(DefaultDeserializer.java:41)
... 44 more
关键报错信息:
java.lang.ClassNotFoundException:com.myeye.ssm.session.UserSessionInfo
,这个类是我主项目的类,在子项目获取时为什么会去找个类,显然在主项目进行序列化时,很明显序列化了该类的包名以及类型信息,因此在子项目进行反序列化时会去找这个类,子项目中没有这个类,所以问题就出现了。
- 如何解决:首先要明确在redis在对数据进行缓存时由谁来进行数据转化,不难发现是
RedisTemplate
,其包含四个属性,分别为四个序列化器:RedisSerializer keySerializer
RedisSerializer valueSerializer
RedisSerializer hashKeySerializer
RedisSerializer hashValueSerializer
而目前我使用的valueSerializer 是JdkSerializationRedisSerializer
,JDK序列化器是redis默认的,其底层使用的是java的IO流,会序列化类的包名以及类名信息,因此如果其他项目没有同包同类与其对应时,就会反序列失败。
解决:既然JDK序列化器会序列化包名,那我们就替换成其他序列化器不就行了,目前常用的就两种吧
1.JaskSon
2.FastJson使用这两种序列化器相比JDK而言要更轻量级、效率至少提高四倍。
4. 使用JackSon序列化器
1.注释掉配置文件中redisTemplate的相关配置
2.创建RedisConfig.java
package com.myeye.ssm.cache;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory jedisConnFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
/**设置存储到redis中的日期格式*/
/* FastJsonRedisSerializer fastJsonRedisSerializer= new FastJsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);*/
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
5. 使用FastJson序列化器
1.注释掉配置文件中redisTemplate的相关配置
2.自定义序列化类:FastJsonRedisSerializer.java
2.创建RedisConfig.java
package com.myeye.ssm.cache;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
package com.myeye.ssm.cache;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
/**
* 自定义序列化类
* @param <T>
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (null == t) {
return new byte[0];
}
return JSON.toJSONString(t).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (null == bytes || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
@Configuration
public class RedisConfig {
@Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory jedisConnFactory){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
/**设置存储到redis中的日期格式*/
FastJsonRedisSerializer fastJsonRedisSerializer= new FastJsonRedisSerializer(Object.class);
redisTemplate.setValueSerializer(fastJsonRedisSerializer);
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
return redisTemplate;
}
}
子项目拦截器中代码如下:
CacheUtil cacheUtil = (CacheUtil) SpringUtil.getBean("cacheUtil");
Object obj = cacheUtil.get("sessionCache", "userSessionInfo");
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(obj);
UserSessionInfo userSessionInfo = mapper.readValue(jsonString, UserSessionInfo.class);
if (!StringUtil.isEmpty(userSessionInfo)) {
UserSessionInfo sessionInfo = new UserSessionInfo();
sessionInfo.setToken(userSessionInfo.getToken());
sessionInfo.setUserId(userSessionInfo.getUserId());
sessionInfo.setUserName(userSessionInfo.getUserName());
sessionInfo.setUserIp(userSessionInfo.getUserIp());
sessionInfo.setLocale(userSessionInfo.getLocale());
if (StringUtil.isEmpty(client)) {
sessionInfo.setClient(Boolean.valueOf(false));
}
if (!StringUtil.isEmpty(clientIP)) {
sessionInfo.setUserIp(clientIP);
}
request.getSession().setAttribute("userInfo", sessionInfo);
}
由此,问题解决,当然肯定还有更好的解决方案,这只是一种思路罢了,这里为什么要在建RedisConfig.java而没有直接在配文件中配置序列化器,原因是xml文件实在不易写太过复杂的代码,也不利于后续的扩展,跟人偏向喜欢注解配置,有兴趣的也可以去在xml文件中进行序列化配置。
6. 总结
前提:同一个实体,在两个项目中使用
- 使用fastjson及Jackson进行序列化反序列化,两个实体的包名无需保持一致:fastjson序列化反序列化是根据@JSONField注解中的name等字段进行处理的。
- 使用io流进行序列化反序列化,两个实体的包名必须保持一致:否则反序列化时根据实体类的全路径找不到对应的实体类会反序列化失败。`