Java Spring数据Redis NullPointerException与@Cacheable

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

框架代码中出现此空指针的原因是什么?我最近开始将我们的一些应用程序堆栈从带有JGroups的ehcache迁移到Redis。作为这项工作的一部分,我们正在维护将ehcache用作某些功能的二级缓存。在一些集成测试期间(其中约900个),我在客户端库(Jedis或莴苣)的连接代码中得到了一致的NullPointerException。但是,缓存机制在正常的应用程序运行期间可以正常工作,并且缓存在某些集成测试期间可以正常工作。现有的应用程序代码使用@Cacheable,这对我来说很好。我使用以下gradle依赖项和缓存配置进行了集成:

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));
}
}