Java Spring数据Redis NullPointerException与@Cacheable
框架代码中出现此空指针的原因是什么?我最近开始将我们的一些应用程序堆栈从带有JGroups的ehcache迁移到Redis。作为这项工作的一部分,我们正在维护将ehcache用作某些功能的二级缓存。在一些集成测试期间(其中约900个),我在客户端库(Jedis或莴苣)的连接代码中得到了一致的NullPointerException。但是,缓存机制在正常的应用程序运行期间可以正常工作,并且缓存在某些集成测试期间可以正常工作。现有的应用程序代码使用@Cacheable,这对我来说很好。我使用以下gradle依赖项和缓存配置进行了集成:Java Spring数据Redis NullPointerException与@Cacheable,java,spring,redis,integration-testing,spring-data-redis,Java,Spring,Redis,Integration Testing,Spring Data Redis,框架代码中出现此空指针的原因是什么?我最近开始将我们的一些应用程序堆栈从带有JGroups的ehcache迁移到Redis。作为这项工作的一部分,我们正在维护将ehcache用作某些功能的二级缓存。在一些集成测试期间(其中约900个),我在客户端库(Jedis或莴苣)的连接代码中得到了一致的NullPointerException。但是,缓存机制在正常的应用程序运行期间可以正常工作,并且缓存在某些集成测试期间可以正常工作。现有的应用程序代码使用@Cacheable,这对我来说很好。我使用以下gr
compile 'org.springframework.data:spring-data-redis:1.6.0.RELEASE'
compile 'biz.paluch.redis:lettuce:'3.3.Final'
redisContext.xml
<bean id="lettucePool" class="org.springframework.data.redis.connection.lettuce.DefaultLettucePool">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="poolConfig" ref="lettucePoolConfiguration" />
</bean>
<bean id="lettucePoolConfiguration" class="org.springframework.data.redis.connection.PoolConfig">
<property name="maxIdle" value="900"/>
<property name="maxActive" value="1000" />
<property name="maxTotal" value="1200" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
</bean>
<bean id="lettuceConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}">
<constructor-arg index="0" ref="lettucePool"/>
<property name="validateConnection" value="true"></property>
<property name="shareNativeConnection" value="true"></property>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="lettuceConnectionFactory">
<property name="keySerializer">
<bean class="c.h.c.c.util.MultiKeySerializer"/> <!-- Used to translate SPeL array to key String -->
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
</bean>
<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg index="0" ref="redisTemplate"></constructor-arg>
<property name="cacheNames">
<set>
<value>cachenames......</value>
</set>
</property>
<property name="expires">
<map>
<entry key="cachenames......" value="times...."/>
</map>
</property>
</bean>
<cache:annotation-driven cache-manager="ehcacheManager"/>
<cache:annotation-driven cache-manager="redisCacheManager"/>
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcache" />
</bean>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml" />
<property name="acceptExisting" value="true" />
</bean>
<bean id="userCacheInstance" factory-bean="ehcacheManager" factory-method="getCache">
<constructor-arg value="user" />
</bean>
<import resource="classpath:redisContext.xml />
好的,这里有很多问题。它们都没有使用SpringDataRedis或连接客户端库。第一个问题涉及我们如何生成密钥。如果您使用:
@Cacheable(....., key={#key1,#key2})
然后Spring会将生成的密钥作为ArrayList处理。我无法使用默认的org.springframework.data.redis.serializer.StringRedisSerializer,因为由于ArrayList!=一串因此,我创建了MultiKeySerializer,以便在ArrayList获得一个实例以生成密钥时,循环遍历ArrayList
public class MultiKeySerializer implements RedisSerializer<Object>{
private final Charset charset;
public MultiKeySerializer() {
this(Charset.forName("UTF8"));
}
public MultiKeySerializer(Charset charset) {
Assert.notNull(charset);
this.charset = charset;
}
@Override
public byte[] serialize(Object t) throws SerializationException {
if(t instanceof ArrayList){
StringBuilder sb = new StringBuilder();
ArrayList tList = (ArrayList) t;
for(Object o: tList){
sb.append(o == null ? null: o.toString());
}
String string = sb.toString();
return (string == null ? null : string.getBytes(charset));
}
// return null - used to return null. Root cause.
return t == null ? null : t.toString().getBytes(charset);
}
@Override
public String deserialize(byte[] bytes) throws SerializationException {
return (bytes == null ? null : new String(bytes, charset));
}
}
公共类MultiKeySerializer实现重新序列化程序{
私有最终字符集;
公共多密钥序列化程序(){
这个(Charset.forName(“UTF8”);
}
公共多键序列化器(字符集字符集){
Assert.notNull(字符集);
this.charset=charset;
}
@凌驾
公共字节[]序列化(对象t)引发序列化异常{
if(ArrayList的t实例){
StringBuilder sb=新的StringBuilder();
ArrayList tList=(ArrayList)t;
for(对象o:tList){
sb.append(o==null?null:o.toString());
}
String=sb.toString();
返回(string==null?null:string.getBytes(charset));
}
//return null-用于返回null。根本原因。
返回t==null?null:t.toString().getBytes(字符集);
}
@凌驾
公共字符串反序列化(字节[]字节)引发序列化异常{
return(bytes==null?null:新字符串(bytes,charset));
}
}
问题是有些代码使用@Cacheable,没有指定key属性。这导致Spring将其视为一个SimpleKey,并因此跌入关键gen逻辑。虽然SpringDataRedis不是罪魁祸首,但如果他们在键为null时给出更具描述性的异常,那就更好了
public class MultiKeySerializer implements RedisSerializer<Object>{
private final Charset charset;
public MultiKeySerializer() {
this(Charset.forName("UTF8"));
}
public MultiKeySerializer(Charset charset) {
Assert.notNull(charset);
this.charset = charset;
}
@Override
public byte[] serialize(Object t) throws SerializationException {
if(t instanceof ArrayList){
StringBuilder sb = new StringBuilder();
ArrayList tList = (ArrayList) t;
for(Object o: tList){
sb.append(o == null ? null: o.toString());
}
String string = sb.toString();
return (string == null ? null : string.getBytes(charset));
}
// return null - used to return null. Root cause.
return t == null ? null : t.toString().getBytes(charset);
}
@Override
public String deserialize(byte[] bytes) throws SerializationException {
return (bytes == null ? null : new String(bytes, charset));
}
}