Spring集成Redis

最近在做一个关于群聊的项目,由于聊天要求实时性,不可能直接访问数据库,所以使用了Redis来做缓存,这里将使用过程中遇到的问题记录一下。
使用Redis之前需要合理设计存储在其中的数据结构,比如聊天系统,就需要存储包括用户登录状态信息,可以使用Hash数据结构,以用户Id做键,状态做值,还可以存储每个用户在每个群的未读消息计数,
客户端使用长轮询拉取。
最主要的就是群里的聊天记录的缓存了,这里可以使用Redis的List数据结构来做消息队列,其中按顺序存储消息实体对象(序列化后存入Redis)。还可以维护一个List存储聊天记录的主键便于定位聊天记录,等等的一系列数据都可以缓存到Redis。
Redis在Java中的实现是Jedis,其中封装了连接Redis的连接池实现、对Redis数据库的各种操作等。
在JavaWeb项目中使用Redis做缓存时有两种实现方式,第一种是使用原生的Jedis包,其中封装的方法可以直接操作Redis。第二种方法是使用Spring框架集成的Redis,其中封装Jedis对Redis的各种操作,包括事务、连接、连接池的维护等,其对Redis的操作进行了很多封装。
这个聊天项目一开始采用的是Spring封装的Redis,使用过程中发现,经常会报一些类型转换异常的问题,后来发现这种错误经常在网络连接异常或是长轮询的地方出现。深入底层发现是由于Redis底层的Socket连接使用了缓冲区,导致有些时候如果前面的连接出现问题,后面的请求再使用前面的连接会获取到前面连接的数据,导致类型转换异常的出现,
尤其是长轮询不断请求Redis服务器的时候,问题出现的频率尤其高,而且一旦出现问题,后面的连接都会出问题,这也许是由于Redis连接池中出问题的连接没有完全释放的原因。
因此建议大家如果使用Redis做缓存,最好使用原生的Jedis,这样可以在连接异常时自己实现异常连接的处理,而使用Spring集成的Redis由于连接已经被封装了,限制了自己对异常连接的处理。
下面就将两种集成Redis的方法分别介绍一下。
第一种是使用Spring集成的Redis。

1、首先是Jar包,需要jedis包、Spring集成Redis的包spring-data-redis、Redis连接池需要的commons-pool2包,其中spring-data-redis包和jedis包的版本需要适配,否则会报错,具体要求网上可以查到,这3个包的下载可以从Maven仓库获取。

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
	<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.4.2</version>
	</dependency>
<!-- spring-data-redis2.0.7版本下载失败 -->
	<dependency>
	   <groupId>org.springframework.data</groupId>
	   <artifactId>spring-data-redis</artifactId>
	   <version>1.8.4.RELEASE</version>
	</dependency>
	<dependency>
	   <groupId>redis.clients</groupId>
	   <artifactId>jedis</artifactId>
	   <version>2.9.0</version>
	</dependency>
2、Spring集成Redis的Xml配置

2.1、Redis.properties配置(相当于jdbc.properties)

#============================#
#==== Redis settings ====#
#============================#
#redis 服务器 IP
redis.host=192.168.1.222
#192.168.1.222

#格式:redis://:[密码]@[服务器地址]:[端口]/[db index]
redis.uri = redis://:12345@127.0.0.1:6379/0

#redis 服务器端口
redis.port=6379

#redis 密码
redis.pass=
#redis#2018

#超时时间
redis.timeOut=2000
#redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
redis.dbIndex=0

#redis 缓存数据过期时间单位秒
redis.expiration=3000

#连接池中最大连接数。高版本:maxTotal,低版本:maxActive
#控制一个 pool 可分配多少个jedis实例
redis.maxActive=6

#连接池中最少空闲的连接数
redis.minIdle=1

#连接池中最大空闲的连接数,控制一个 pool 最多有多少个状态为 idle 的jedis实例
redis.maxIdle=300

#当连接池资源耗尽时,调用者最大阻塞的时间,超时将抛出异常。单位,毫秒数;默认为-1.表示永不超时。高版本:maxWaitMillis,低版本:maxWait
#当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
redis.maxWait=1000

#连接空闲的最小时间,达到此值后空闲连接将可能会被移除。负值(-1)表示不移除
redis.minEvictableIdleTimeMillis=60000

#对于“空闲链接”检测线程而言,每次检测的链接资源的个数。默认为3
redis.numTestsPerEvictionRun=3

#“空闲链接”检测线程,检测的周期,毫秒数。如果为负值,表示不运行“检测线程”。默认为-1
redis.timeBetweenEvictionRunsMillis=30000

#testOnBorrow:向调用者输出“链接”资源时,是否检测是有有效,如果无效则从连接池中移除,并尝试获取继续获取。默认为false。建议保持默认值
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true

#testOnReturn:向连接池“归还”链接时,是否检测“链接”对象的有效性。默认为false。建议保持默认值
#testWhileIdle:向调用者输出“链接”对象时,是否检测它的空闲超时;默认为false。如果“链接”空闲超时,将会被移除。建议保持默认值
#whenExhaustedAction:当“连接池”中active数量达到阀值时,即“链接”资源耗尽时,连接池需要采取的手段, 默认为1(0:抛出异常。1:阻塞,直到有可用链接资源。2:强制创建新的链接资源)

2.2、applicationContext.xml引入redis.properties文件

<!-- 这里配置数据库properties文件位置 -->
	<!-- 使用PropertyPlaceholderConfigurer加载redis.properties文件失败改用PropertySourcesPlaceholderConfigurer -->
	<bean id="propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
		<property name="locations">
		    <list>
			    <value>classpath:jdbc.properties</value>
			    <value>classpath:system.properties</value>	
			    <value>classpath:redis.properties</value>
			</list>
		</property>
	</bean>

将Redis相关配置单独放在一个配置文件中:

<import resource="spring-redis-context.xml" /> 

spring-redis-context.xml中的配置包括,连接池的配置、连接工厂的配置以及RedisTemplate类的相关配置。

<?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:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                       http://www.springframework.org/schema/context
                       http://www.springframework.org/schema/context/spring-context.xsd">
   <description>Spring容器管理redis连接和使用 </description>

   <!--载入 redis 配置文件-->
   <!-- Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer 或 <content:property-placeholder>其余的会被Spring忽略 -->
   <!-- <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> -->
   
   <!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
   <!-- 连接池配置 -->
   <!-- 配置 JedisPoolConfig 实例 -->
   <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
       <property name="maxIdle" value="${redis.maxIdle}"/>
       <property name="maxTotal" value="${redis.maxActive}"/>
       <property name="maxWaitMillis" value="${redis.maxWait}"/>
       <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
   </bean>
   
   <!-- <bean id="clusterRedisNodes1" class="org.springframework.data.redis.connection.RedisNode">
   		<constructor-arg value="${redis.host}" />
   		<constructor-arg value="${redis.port}" type="int"/>
   </bean> -->
   
   <!-- ERR This instance has cluster support disabled -->
   <!-- <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
   		<property name="maxRedirects" value="3"></property>
   		节点配置
   		<property name="clusterNodes">
   			<set>
   				<bean class="org.springframework.data.redis.connection.RedisClusterNode">
   					<constructor-arg name="host" value="${redis.host}"/>
   					<constructor-arg name="port" value="${redis.port}"/>
   				</bean>
   				<bean class="org.springframework.data.redis.connection.RedisClusterNode">
   					<constructor-arg name="host" value="192.168.1.111"/>
   					<constructor-arg name="port" value="6380"/>
   				</bean>
   			</set>
   		</property>
   </bean> -->
   
	<!-- Spring提供的Redis连接工厂 -->
   <!-- 配置JedisConnectionFactory -->
   <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
       <property name="hostName" value="${redis.host}"/>
       <property name="port" value="${redis.port}"/>
       <property name="password" value="${redis.pass}"/>
       <property name="database" value="${redis.dbIndex}"/> 
       <property name="poolConfig" ref="poolConfig"/>
       <!-- <constructor-arg ref="redisClusterConfiguration"></constructor-arg>
       <constructor-arg ref="poolConfig"></constructor-arg> -->
       <!-- <property name="clusterConfig" ref="redisClusterConfiguration"></property> -->
   </bean>
	<bean id="redisConfig" class="com.teriste.redis.util.RedisConfig"></bean>
   <!-- Spring提供的访问Redis类 -->
   <!-- 配置RedisTemplate -->
   <!-- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" scope="prototype">
       <property name="connectionFactory" ref="jedisConnectionFactory"/>
       下面是设置key和value序列化的方式,不同方式导致存入的字节序列不一样,什么样的序列化方式就要使用相应的反序列化方式查询,否则差不多redis存入的数据
       由此也可以知道spring对redis序列化有很多种方式
       <property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="valueSerializer">
			defaultSerializer是JdkSerializationRedisSerializer在调试时看不出来键是什么样的
			<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
			<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
			fastjson序列化
			<bean class="com.teriste.redis.util.FastJson2JsonRedisSerializer">
				<constructor-arg name="clazz" value=Object.class></constructor-arg>
			</bean>
		</property>
		<property name="hashKeySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		配置true可以使用transactional控制事务,spring已经提供支持
		<property name="enableTransactionSupport" value="true"/>
   </bean> -->

   <!-- 配置RedisCacheManager -->
   <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
       <constructor-arg name="redisOperations" ref="redisTemplate"/>
       <property name="defaultExpiration" value="${redis.expiration}"/>
   </bean>

   <!-- 配置RedisCacheConfig -->
   <!-- <bean id="redisCacheConfig" class="com.teriste.redis.util.RedisCacheConfig">
       <constructor-arg ref="jedisConnectionFactory"/>
       <constructor-arg ref="redisTemplate"/>
       <constructor-arg ref="redisCacheManager"/>
   </bean> -->
   
   <!-- Redis sentinel集群配置 -->
	<!-- <bean id="sentinelConfig" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
	    <constructor-arg index="0" type="java.lang.String" value="host6379" />
	    <constructor-arg index="1" type="java.util.Set">
	        <set>
	            <value>192.168.1.111:6380</value>
	            <value>192.168.1.111:6381</value>
	        </set>
	    </constructor-arg>
	</bean> -->
	<!-- Spring提供的Redis连接工厂 -->
	<!-- <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
	    Redis sentinel集群配置
	    <constructor-arg index="0" type="org.springframework.data.redis.connection.RedisSentinelConfiguration" ref="sentinelConfig" />
	    连接池配置.
	    <constructor-arg index="1" type="redis.clients.jedis.JedisPoolConfig" ref="poolConfig" />
	    
	    Redis服务主机.
	    <property name="hostName" value="192.168.1.111" />
	    Redis服务端口号.
	    <property name="port" value="26379" />
	    Redis服务连接密码.
	    <property name="password" value="${redis.pass}" />
	    连超时设置.
	    <property name="timeout" value="15000" />
	    是否使用连接池.
	    <property name="usePool" value="true" />
	</bean> -->
</beans>
RedisTemplate相关的配置在RedisConfig类中统一管理,再由容器注入,其中主要包括的是Java对象序列化存入Redis和从Redis反序列化到java对象的配置,因为Spring集成的redis默认是

JdkSerializationRedisSerializer,序列化后是字节数组,存入Redis不便于查看,所以需要自己配置键值的序列化方式,这里使用的是alibaba的fastjson2:

//RedisConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.alibaba.fastjson.parser.ParserConfig;

@Configuration
public class RedisConfig
{
    /**
     * 重写Redis序列化方式,使用Json方式:
     * 当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer。
     * Spring Data JPA为我们提供了下面的Serializer:
     * GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
     * 在此我们将自己配置RedisTemplate并定义Serializer。
     * com.teriste.redis.util.FastJson2JsonRedisSerializer
     * @param redisConnectionFactory
     * @return
     */
    @Bean(name="fastJson2JsonRedisSerializer")
    @SuppressWarnings("rawtypes")
    public RedisSerializer fastJson2JsonRedisSerializer(){
        return new FastJson2JsonRedisSerializer<Object>(Object.class);
    }
    
    @Bean(name="redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        FastJson2JsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJson2JsonRedisSerializer<Object>(Object.class);
        // 全局开启AutoType,不建议使用
        // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        // 建议使用这种方式,小范围指定白名单
        ParserConfig.getGlobalInstance().addAccept("com.teriste.");
        //设置键值序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        //设置hash键值序列化方式
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
//FastJson2JsonRedisSerializer.java

import java.nio.charset.Charset;

import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
 * 自定义序列化配置类
 * @author Administrator
 * @version 2018年5月24日
 * @see FastJson2JsonRedisSerializer
 * @since
 */
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    private Class<T> clazz;
    
    public FastJson2JsonRedisSerializer(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,SerializerFeature.WriteClassName).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);
    }

}
还可以根据需要配置Redis键生成规则:
//RedisCacheConfig.java
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * 
 *  spring-context since 4.1 才有CachingConfigurerSupport类
 * @author Administrator
 * @version 2018年5月21日
 * @see RedisCacheConfig
 * @since
 */
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
   protected final static Logger log = LoggerFactory.getLogger(RedisCacheConfig.class);

   private volatile JedisConnectionFactory mJedisConnectionFactory;
   private volatile RedisTemplate<String, String> mRedisTemplate;
   private volatile RedisCacheManager mRedisCacheManager;

   public RedisCacheConfig() {
       super();
   }

   public RedisCacheConfig(JedisConnectionFactory mJedisConnectionFactory, RedisTemplate<String, String> mRedisTemplate, RedisCacheManager mRedisCacheManager) {
       super();
       this.mJedisConnectionFactory = mJedisConnectionFactory;
       this.mRedisTemplate = mRedisTemplate;
       this.mRedisCacheManager = mRedisCacheManager;
   }

   public JedisConnectionFactory redisConnectionFactory() {
       return mJedisConnectionFactory;
   }

   public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
       return mRedisTemplate;
   }

   public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
       return mRedisCacheManager;
   }

   /**
    * 该类告诉 spring 当前使用的缓存服务为 redis 并自定义了缓存 key 生成的规则。
    */
   @Bean
   public KeyGenerator keyGenerator() {
       return new KeyGenerator() {
           public Object generate(Object o, Method method, Object... objects) {
               StringBuilder sb = new StringBuilder();
               sb.append(o.getClass().getName());
               sb.append(method.getName());
               for (Object obj : objects) {
                   sb.append(obj.toString());
               }
               return sb.toString();
           }
       };
   }
}
下面就可以使用RedisTemplate类来操作Redis数据库了。
redisTemplate有两个方法经常用到,一个是opsForXXX一个是boundXXXOps,XXX是value的类型,前者获取到一个Opercation,但是没有指定操作的key,可以在一个连接(事务)内操作多个key以及对应的value;后者会获取到一个指定了key的operation,在一个连接内只操作这个key对应的value.
使用中发现一个问题,如果方法使用了Spring的@Transactional注解,那么在该方法里使用读取redis数据的方法获取不到数据,写入redis数据库的方法可以生效。
下面开始介绍第二种使用Redis的方式:原生Jedis。
1、同样还是需要redis.properties,以及在applicationContext.xml中引入redis.properties和单独的spring集成redis的配置文件。

2、配置jedis连接池

spring-redis-context.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:context="http://www.springframework.org/schema/context"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                       http://www.springframework.org/schema/context
                       http://www.springframework.org/schema/context/spring-context.xsd">
   <description>Spring容器管理redis连接和使用 </description>
   <!--载入 redis 配置文件-->
   <!-- Spring容器仅允许最多定义一个PropertyPlaceholderConfigurer 或 <content:property-placeholder>其余的会被Spring忽略 -->
   <!-- <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> -->

	<!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
   <!-- 连接池配置 -->
   <!--shardedJedisPool的相关配置-->
   <!-- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        新版是maxTotal,旧版是maxActive
        <property name="maxTotal">
            <value>${redis.maxActive}</value>
        </property>
        <property name="maxIdle">
            <value>${redis.maxIdle}</value>
        </property>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
        <property name="testOnReturn" value="true"/>
    </bean>
    
   <bean id="sharedJedisPool" class="redis.clients.jedis.ShardedJedisPool" scope="="singleton">
   		<constructor-arg index="0" ref="jedisPoolConfig"></constructor-arg>
   		<constructor-arg index="1">
   			<list>
   				<bean class="redis.clients.jedis.JedisShardInfo">
   					<constructor-arg name="host" value="${redis.uri}"></constructor-arg>
   				</bean>
   			</list>
   		</constructor-arg>
   </bean> -->
   
   <!-- 高版本Jedis没有maxActive变量,具体有哪些可以看JedisPoolConfig继承的类GenericObjectPoolConfig -->
   <!-- 连接池配置 -->
   <!-- 配置 JedisPoolConfig 实例 -->
   <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
       <property name="maxIdle" value="${redis.maxIdle}"/>
       <property name="maxTotal" value="${redis.maxActive}"/>
       <property name="maxWaitMillis" value="${redis.maxWait}"/>
       <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
   </bean>
   
   <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
   		<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
   		<constructor-arg name="host" value="${redis.host}" />
   		<constructor-arg name="port" value="${redis.port}" type="int" />
   		<constructor-arg name="timeout" value="${redis.timeOut}" type="int" />
   		<!-- <constructor-arg name="password" value="${redis.pass}" />
   		<constructor-arg name="database" value="${redis.dbIndex}" type="int" /> -->
   </bean>
	
	<bean id="jedisClient" class="com.teriste.redis.util.JedisClient">
		<constructor-arg name="jedisPool" ref="jedisPool"></constructor-arg>
		<constructor-arg name="jedisPoolConfig" ref="jedisPoolConfig"></constructor-arg>
		<constructor-arg name="host" value="${redis.host}" />
   		<constructor-arg name="port" value="${redis.port}" type="int" />
   		<constructor-arg name="timeout" value="${redis.timeOut}" type="int" />
	</bean>
	
	<bean id="redisClient" class="com.teriste.redis.util.RedisClient">
		<constructor-arg name="jedisClient" ref="jedisClient"></constructor-arg>
	</bean>
</beans>
其中的JedisClient.java类封装了连接和释放redis连接的方法,RedisClient类封装了对redis中各种数据类型的操作。
同样涉及到Java对象存入redis的问题,键是String类型的,所以直接使用String类的getBytes(charset)和new String(bytes, charset)来序列化和反序列化。值使用fastjson2。

StringSerializer.java实现键的序列化

import java.nio.charset.Charset;

/**
 * 读写Hash数据结构数据的序列化与发序列化工具类
 * @author Administrator
 * @version 2018年7月9日
 * @see StringSerializer
 * @since
 */
public class StringSerializer
{
    private static final Charset charset=Charset.forName("UTF-8");
    
    /*public StringSerializer(Charset charset) {
      Assert.notNull(charset, "Charset must not be null!");
      this.charset = charset;
    }*/
    
    public static String deserialize(byte[] bytes) {
      return bytes == null ? null : new String(bytes, charset);
    }
    
    public static byte[] serialize(String string) {
      return string == null ? null : string.getBytes(charset);
    }
  }
FastJson2JsonSerializer.java实现值的序列化
import java.nio.charset.Charset;

import org.springframework.data.redis.serializer.SerializationException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

public class FastJson2JsonSerializer
{
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    
    public static byte[] serialize(Object t)
        throws SerializationException
    {
        if (null==t)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t,SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }
    
    @SuppressWarnings(value={"unchecked","rawtypes"})
    public static Object deserialize(byte[] bytes, Class clazz)
        throws SerializationException
    {
        if (null==bytes||bytes.length<=0)
        {
            return null;
        }
        String str=new String(bytes,DEFAULT_CHARSET);
        return JSON.parseObject(str, clazz);
    }
}
下面是对redis连接的封装:JedisClient.java
import org.apache.log4j.Logger;

import com.teriste.core.utils.SpringContextHolder;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.exceptions.JedisException;

public class JedisClient
{
    /**
     * 日志 
     */
    private static Logger logger = Logger.getLogger(JedisClient.class);
    
    private JedisPool jedisPool;//注入JedisPool
    
    private JedisPoolConfig jedisPoolConfig;
    
    private String host;
    private int port;
    private int timeout;
    
    public JedisClient(JedisPool jedisPool,JedisPoolConfig jedisPoolConfig,String host, int port,int timeout){
        this.jedisPool=jedisPool;
        this.jedisPoolConfig=jedisPoolConfig;
        this.host=host;
        this.port=port;
        this.timeout=timeout;
    }
    //获取Redis资源
    public synchronized Jedis getJedis(){
        try
        {
            if (jedisPool==null)
            {
                jedisPool=(JedisPool)SpringContextHolder.getApplicationContext().getBean("jedisPool");
            }
            Jedis jedis=jedisPool.getResource();
            return jedis;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            //获取Redis连接时如果发生异常就重置连接池
            if (null!=jedisPool)
            {
                jedisPool.destroy();
                jedisPool=new JedisPool(jedisPoolConfig,host,port,timeout);
            }
            System.out.println(jedisPool.isClosed()+"----------------");
            if (jedisPool!=null)
            {
                Jedis jedis=jedisPool.getResource();
                return jedis;
            }
        }
        return null;
    }
    
    
  //释放redis资源
    public synchronized void releaseConn(Jedis jedis,Exception exception){
        /*if (null!=exception)
        {
            if (exception instanceof JedisConnectionException) {
                logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " lost.", exception);
            } else if (exception instanceof JedisDataException) {
                if ((exception.getMessage() != null) && (exception.getMessage().indexOf("READONLY") != -1)) {
                    logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " are read-only slave.", exception);
                } else {
                    // dataException, isBroken=false
                }
            } else {
                logger.error("Jedis exception happen.", exception);
            }
        }*/
        
        try
        {
            if (null!=jedis)
            {
                //Jedis的close()会先判断连接是否损毁如果是就走释放损毁连接的方法,否则走正常连接的方法。
                jedis.close();
            }
        }
        catch (Exception e)
        {
            logger.error("return back jedis failed, will fore close the jedis.", e);
            //如果close()方法执行失败就直接将jedis引用置空
            jedis=null;
        }
        
        /*try
        {
            if (jedis!=null)
            {
                jedisPool.returnResource(jedis);
            }
        }
        catch (Exception e)
        {
            if (jedis!=null)
            {
                try
                {
                    jedisPool.returnBrokenResource(jedis);
                }
                catch (Exception e2)
                {
                    jedis=null;
                }
                
            }
        }*/
        
    }
    
    /**
     * Handle jedisException, write log and return whether the connection is broken.
     */
    protected boolean handleJedisException(JedisException jedisException) {
        if (jedisException instanceof JedisConnectionException) {
            logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " lost.", jedisException);
        } else if (jedisException instanceof JedisDataException) {
            if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
                logger.error("Redis connection " + jedisPool.getResource().getClient().getHost() + " are read-only slave.", jedisException);
            } else {
                // dataException, isBroken=false
                return false;
            }
        } else {
            logger.error("Jedis exception happen.", jedisException);
        }
        return true;
    }
    /**
     * Return jedis connection to the pool, call different return methods depends on the conectionBroken status.
     */
    @SuppressWarnings("deprecation")
    protected void closeResource(Jedis jedis, boolean conectionBroken) {
        try {
            if (conectionBroken) {
                jedisPool.returnBrokenResource(jedis);
            } else {
                jedisPool.returnResource(jedis);
            }
        } catch (Exception e) {
            logger.error("return back jedis failed, will fore close the jedis.", e);
            jedis=null;
        }
    }
    
    public JedisPool getJedisPool()
    {
        return jedisPool;
    }

    public void setJedisPool(JedisPool jedisPool)
    {
        this.jedisPool = jedisPool;
    }
    public JedisPoolConfig getJedisPoolConfig()
    {
        return jedisPoolConfig;
    }
    public void setJedisPoolConfig(JedisPoolConfig jedisPoolConfig)
    {
        this.jedisPoolConfig = jedisPoolConfig;
    }
    
    public String getHost()
    {
        return host;
    }
    public void setHost(String host)
    {
        this.host = host;
    }
    public int getPort()
    {
        return port;
    }
    public void setPort(int port)
    {
        this.port = port;
    }
    public int getTimeout()
    {
        return timeout;
    }
    public void setTimeout(int timeout)
    {
        this.timeout = timeout;
    }
    public static Logger getLogger()
    {
        return logger;
    }
    public static void setLogger(Logger logger)
    {
        JedisClient.logger = logger;
    }
    
}
下面是对部分操作Redis数据的封装:RedisClient.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import  redis.clients.jedis.BinaryClient.LIST_POSITION;

import org.springframework.data.redis.connection.jedis.JedisConverters;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.Assert;

import redis.clients.jedis.Jedis;

public class RedisClient
{
    private JedisClient jedisClient;
    
    public RedisClient(JedisClient jedisClient){
        this.jedisClient=jedisClient;
    }
    
    //键序列化与反序列化:StringSerializer
    //值序列化与反序列化:FastJson2JsonSerializer
    /**
     * 数据结构之外的处理
     */
    
    /**
     * 删除Redis中指定的键
     * @param key 
     * @see
     */
    public void del(String key) {
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            jedis.del(new byte[][] { rawKey });
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * 删除Redis中多个键
     * @param keys 
     * @see
     */
    public void del(Collection<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }
        final byte[][] rawKeys = rawKeys(keys);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            jedis.del(rawKeys);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * 判断Redis中键是否存在
     * @param key
     * @return 
     * @see
     */
    public Boolean exists(String key){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        Boolean exists=null;
        try
        {
            jedis=jedisClient.getJedis();
            exists=jedis.exists(rawKey);
            return exists;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    
    private byte[][] rawKeys(Collection<String> keys) {
        byte[][] rawKeys = new byte[keys.size()][];
        int i = 0;
        for (String key : keys) {
            rawKeys[(i++)] = rawString(key);
        }
        return rawKeys;
    }
    
    /**
     * ################Hash##################
     */
    
    /**
     * Hash数据结构判断值的键是否存在
     * @param key
     * @param hashKey
     * @return 
     * @see
     */
    public Boolean hExists(String key, String hashKey) {
        final byte[] rawKey = rawString(key);
        final byte[] rawHashKey = rawHashKey(hashKey);
        Jedis jedis=null;
        Boolean exists=null;
        try
        {
            jedis=jedisClient.getJedis();
            exists=jedis.hexists(rawKey, rawHashKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return exists;
    }
    
    /**
     * Hash数据结构删除值键
     * @param key
     * @param hashKeys
     * @return 
     * @see
     */
    public Long hDel(String key,Object... hashKeys){
        final byte[] rawKey = rawString(key);
        final byte[][] rawHashKeys = rawHashKeys(hashKeys);
        Jedis jedis=null;
        Long delNum=null;
        try
        {
            jedis=jedisClient.getJedis();
            delNum=jedis.hdel(rawKey, rawHashKeys);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return delNum;
    }
    /**
     * Hash数据结构获取键对应的值键的集合
     * @param key
     * @return 
     * @see
     */
    public Set<String> hKeys(String key){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        Set<byte[]> rawValues=null;
        try
        {
            jedis=jedisClient.getJedis();
            rawValues=jedis.hkeys(rawKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return deserializeHashKeys(rawValues);
    }
    
    /**
     * Hash数据结构获取值键对应的值的集合
     * @param key
     * @param clazz
     * @return 
     * @see
     */
    public List<Object> values(String key,Class clazz){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        List<byte[]> rawValues=null;
        try
        {
            jedis=jedisClient.getJedis();
            rawValues=jedis.hvals(rawKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return deserializeHashValues(rawValues,clazz);
    }
    
    /**
     * Hash数据结构获取键对应的值键的个数
     * @param key
     * @return 
     * @see
     */
    public Long hLen(String key) {
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        Long len=null;
        try
        {
            jedis=jedisClient.getJedis();
            len=jedis.hlen(rawKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return len;
    }
    /**
     * 获取hash数据结果的值
     * @param key
     * @param hashKey
     * @param clazz
     * @return 
     * @see
     */
    public Object hGet(String key,String hashKey,Class clazz){
        final byte[] rawKey=rawString(key);
        final byte[] rawHashKey=rawHashKey(hashKey);
        byte[] rawHashValue =null;
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            rawHashValue=jedis.hget(rawKey,rawHashKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        finally{
            jedisClient.releaseConn(jedis,null);
        }
        return FastJson2JsonSerializer.deserialize(rawHashValue,clazz);
    }
    
    /**
     * Hash数据结构获取多个键的值列表
     * @param key
     * @param fields
     * @param clazz
     * @return 
     * @see
     */
    public List<Object> hMGet(String key,List<String> fields,Class clazz){
        if (fields.isEmpty()) {
            return Collections.emptyList();
        }
        final byte[] rawKey = rawString(key);
        final byte[][] rawHashKeys = new byte[fields.size()][];
        int counter = 0;
        for (String hashKey : fields) {
            rawHashKeys[(counter++)] = rawHashKey(hashKey);
        }
        Jedis jedis=null;
        List<byte[]> rawValues=null;
        try
        {
            jedis=jedisClient.getJedis();
            rawValues=jedis.hmget(rawKey, rawHashKeys);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return deserializeHashValues(rawValues,clazz);
    }
    
    /**
     * 根据键获取hash数据结构所有数据
     * @param key
     * @param hashKey
     * @param clazz:值的类型
     * @return 
     * @see
     */
    public Map<Object, Object> hGetAll(String key,Class clazz){
        final byte[] rawKey=rawString(key);
        Map<byte[], byte[]> rawHashValue =null;
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            rawHashValue=jedis.hgetAll(rawKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return deserializeHashMap(rawHashValue,clazz);
    }
    
    /**
     * 保存或更新hash数据
     * @param key
     * @param hashKey
     * @param value
     * @return 
     * @see
     */
    public Boolean hSet(String key,String hashKey,Object value){
        final byte[] rawKey = rawString(key);
        final byte[] rawHashKey = rawHashKey(hashKey);
        final byte[] rawHashValue = rawValue(value);
        Jedis jedis=jedisClient.getJedis();
        try
        {
            Boolean result=JedisConverters.toBoolean(jedis.hset(rawKey, rawHashKey, rawHashValue));
            jedisClient.releaseConn(jedis,null);
            return result;
        }
        catch (Exception e)
        {
            jedisClient.releaseConn(jedis,e);
            return false;
        }
        
    }
    
    /**
     * hash数据结构保存多个键值对
     * @param key
     * @param m
     * @return 
     * @see
     */
    public Boolean hMSet(String key,Map<String, Object> m){
        if (m.isEmpty()) {
            return false;
        }
        final byte[] rawKey = rawString(key);
        final Map<byte[], byte[]> hashes = new LinkedHashMap<byte[], byte[]>(m.size());
        for (Map.Entry<String, Object> entry : m.entrySet()) {
            hashes.put(rawHashKey(entry.getKey()), rawValue(entry.getValue()));
        }
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            jedis.hmset(rawKey, hashes);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return true;
    }
    
    
    //key序列化
    public byte[] rawString(String key){
        return StringSerializer.serialize(key);
    }
    
    //hashkey序列化
    public byte[] rawHashKey(Object hashKey){
        return StringSerializer.serialize(hashKey.toString());
    }
    
    //多个值键序列化
    public byte[][] rawHashKeys(Object... hashKeys) {
        byte[][] rawHashKeys = new byte[hashKeys.length][];
        int i = 0;
        for (Object hashKey : hashKeys) {
            rawHashKeys[(i++)] = rawHashKey(hashKey);
        }
        return rawHashKeys;
    }
    
    //值序列化
    public byte[] rawValue(Object value){
        return FastJson2JsonSerializer.serialize(value);
    }
    
    //hash键反序列化
    public Set<String> deserializeHashKeys(Set<byte[]> rawKeys){
        if (rawKeys == null) {
            return null;
        }
        Set<String> values =new LinkedHashSet<String>(rawKeys.size());
        for (byte[] bs : rawKeys) {
            values.add(StringSerializer.deserialize(bs));
        }
        return values;
    }
    
    //hash结构值是String类型的反序列化
    public List<Object> deserializeHashValues(List<byte[]> rawValues,Class clazz){
        if (rawValues == null) {
            return null;
        }
        List<Object> values =new ArrayList<Object>();
        for (byte[] bs : rawValues) {
            values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
        }
        return values;
    }
    
    /**
     * ################List##################
     */
    
    /**
     * List数据结构获取指定索引位置的值
     * @param key
     * @param index
     * @param clazz
     * @return 
     * @see
     */
    public Object lIndex(String key, final long index,Class clazz) {
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        byte[] value=null;
        try
        {
            jedis=jedisClient.getJedis();
            value=jedis.lindex(rawKey, index);
            return deserializeValue(value,clazz);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构获取键对应的值集合大小
     * @param key
     * @return 
     * @see
     */
    public Long lLen(String key)
    {
      final byte[] rawKey = rawString(key);
      Jedis jedis=null;
      Long len=null;
      try
      {
          jedis=jedisClient.getJedis();
          len=jedis.llen(rawKey);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return len;
    }
    
    /**
     * List数据结构获取指定范围的值集合
     * @param key
     * @param start
     * @param end
     * @param clazz
     * @return 
     * @see
     */
    public List<Object> lRange(String key, final long start, long end,Class clazz)
    {
      final byte[] rawKey = rawString(key);
      Jedis jedis=null;
      List<byte[]> rawValues=null;
      try
      {
            jedis=jedisClient.getJedis();
            rawValues=jedis.lrange(rawKey, start, end);
            return deserializeValues(rawValues,clazz);
      }
      catch (Exception e)
      {
          e.printStackTrace();
          return null;
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
    }
    
    /**
     * List数据结构移除指定值
     * @param key
     * @param count
     * @param value
     * @return 
     * @see
     */
    public Long lRem(String key, final long count, Object value)
    {
      final byte[] rawKey = rawString(key);
      byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long remNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            remNum=jedis.lrem(rawKey, count, rawValue);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return remNum;
    }
    
    
    /**
     * List数据结构从左侧弹出值对象
     * @param key
     * @param clazz
     * @return 
     * @see
     */
    public Object lLPop(String key,Class clazz){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        byte[] value=null;
        try
        {
            jedis=jedisClient.getJedis();
            value=jedis.lpop(rawKey);
            return deserializeValue(value,clazz);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构从左侧弹出值的字节数组
     * @param key
     * @param timeout
     * @param unit
     * @return 
     * @see
     */
    public byte[] lLPop(String key, long timeout, TimeUnit unit) 
    { 
        final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            List<byte[]> bLPop=jedis.blpop(tm, new byte[][] { rawKey });
            return CollectionUtils.isEmpty(bLPop) ? null : (byte[])bLPop.get(1);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构从左侧插入数据
     * @param key
     * @param value
     * @return 
     * @see
     */
    public Long lLeftPush(String key, Object value)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.lpush(rawKey, new byte[][] { rawValue });
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构左侧插入值数组
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long lLeftPushAll(String key, Object[] values)
    {
      final byte[] rawKey = rawString(key);
      final byte[][] rawValues = rawValues(values);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.lpush(rawKey, rawValues);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构左侧插入值集合
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long lLeftPushAll(String key, Collection<Object> values)
    {
      final byte[] rawKey = rawString(key);
      final byte[][] rawValues = rawValues(values);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.lpush(rawKey, rawValues);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构如果值存在从左侧插入
     * @param key
     * @param value
     * @return 
     * @see
     */
    public Long lLeftPushIfPresent(String key, Object value)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.lpushx(rawKey, rawValue);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构按优先级从左侧插入
     * @param key
     * @param pivot
     * @param value
     * @return 
     * @see
     */
    public Long lLeftPush(String key, Object pivot, Object value)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawPivot = rawValue(pivot);
      final byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.linsert(rawKey, LIST_POSITION.BEFORE, rawPivot, rawValue);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构从右侧弹出值对象
     * @param key
     * @param clazz
     * @return 
     * @see
     */
    public Object lRPop(String key,Class clazz){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        byte[] value=null;
        try
        {
            jedis=jedisClient.getJedis();
            value=jedis.rpop(rawKey);
            return deserializeValue(value,clazz);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构从右侧弹出值的字节数组
     * @param key
     * @param timeout
     * @param unit
     * @return 
     * @see
     */
    public byte[] lRPop(String key, long timeout, TimeUnit unit) 
    { 
        final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            List<byte[]> bRPop=jedis.brpop(tm, new byte[][] { rawKey });
            return CollectionUtils.isEmpty(bRPop) ? null : (byte[])bRPop.get(1);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构右侧插入值
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long lRightPush(String key, Object value)
    {
        final byte[] rawKey = rawString(key);
        final byte[] rawValue = rawValue(value);
        Jedis jedis=null;
        Long pushNum=null;
        try
        {
              jedis=jedisClient.getJedis();
              pushNum=jedis.rpush(rawKey, new byte[][] { rawValue });
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return pushNum;
    }

    /**
     * List数据结构右侧插入值数组
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long lRightPushAll(String key, Object[] values)
    {
        final byte[] rawKey = rawString(key);
        final byte[][] rawValues = rawValues(values);
        Jedis jedis=null;
        Long pushNum=null;
        try
        {
              jedis=jedisClient.getJedis();
              pushNum=jedis.rpush(rawKey, rawValues);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return pushNum;
    }
    
    /**
     * List数据结构右侧插入值集合
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long lRightPushAll(String key, Collection<Object> values)
    {
        final byte[] rawKey = rawString(key);
        final byte[][] rawValues = rawValues(values);
        Jedis jedis=null;
        Long pushNum=null;
        try
        {
              jedis=jedisClient.getJedis();
              pushNum=jedis.rpush(rawKey, rawValues);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return pushNum;
    }
    
    
    /**
     * List数据结构如果值存在从右侧插入
     * @param key
     * @param value
     * @return 
     * @see
     */
    public Long lRightPushIfPresent(String key, Object value)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.rpushx(rawKey, rawValue);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构按优先级从右侧插入
     * @param key
     * @param pivot
     * @param value
     * @return 
     * @see
     */
    public Long lRightPush(String key, Object pivot, Object value)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawPivot = rawValue(pivot);
      final byte[] rawValue = rawValue(value);
      Jedis jedis=null;
      Long pushNum=null;
      try
      {
            jedis=jedisClient.getJedis();
            pushNum=jedis.linsert(rawKey, LIST_POSITION.AFTER, rawPivot, rawValue);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return pushNum;
    }
    
    /**
     * List数据结构从一个列表的右侧弹出值从另一个列表的左侧插入
     * @param sourceKey
     * @param destinationKey
     * @return 
     * @see
     */
    public byte[] rightPopAndLeftPush(String sourceKey, String destinationKey){
        final byte[] rawSourceKey = rawString(sourceKey);
        final byte[] rawDestKey = rawString(destinationKey);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            return jedis.rpoplpush(rawSourceKey, rawDestKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构从一个列表的右侧弹出值从另一个列表的左侧插入
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     * @param unit
     * @return 
     * @see
     */
    public byte[] rightPopAndLeftPush(String sourceKey, String destinationKey, long timeout, TimeUnit unit){
        final int tm = (int)TimeoutUtils.toSeconds(timeout, unit);
        final byte[] rawSourceKey = rawString(sourceKey);
        final byte[] rawDestKey = rawString(destinationKey);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            return jedis.brpoplpush(rawSourceKey, rawDestKey,tm);
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构替换掉在键的指定索引处的值
     * @param key
     * @param index
     * @param value 
     * @see
     */
    public void set(String key, final long index, Object value){
        final byte[] rawKey = rawString(key);
        final byte[] rawValue = rawValue(value);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            jedis.lset(rawKey, index, rawValue);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    /**
     * List数据结构按索引位置截取指定键的列表
     * @param key
     * @param start
     * @param end 
     * @see
     */
    public void trim(String key, final long start, long end){
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        try
        {
            jedis=jedisClient.getJedis();
            jedis.ltrim(rawKey, start, end);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
    }
    
    
    /**
     * List数据结构序列化值
     * @param values
     * @return 
     * @see
     */
    public byte[][] rawValues(Collection<Object> values)
    {
      Assert.notEmpty(values, "Values must not be 'null' or empty.");
      Assert.noNullElements(values.toArray(), "Values must not contain 'null' value.");
      byte[][] rawValues = new byte[values.size()][];
      int i = 0;
      for (Iterator<Object> localIterator = values.iterator(); localIterator.hasNext(); ) {
          Object value = localIterator.next();
          rawValues[(i++)] = rawValue(value);
      }
      return rawValues;
    }

    /**
     * List数据结构值反序列化
     * @param value
     * @param clazz
     * @return 
     * @see
     */
    public Object deserializeValue(byte[] value,Class clazz)
    {
      if (value == null) {
        return null;
      }
      return FastJson2JsonSerializer.deserialize(value,clazz);
    }
    
    /**
     * List数据结构值集合反序列化
     * @param rawValues
     * @param clazz
     * @return 
     * @see
     */
    public List<Object> deserializeValues(List<byte[]> rawValues,Class clazz){
        if (rawValues == null) {
            return null;
        }
        List<Object> values =new ArrayList<Object>();
        for (byte[] bs : rawValues) {
            values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
        }
        return values;
    }
    /**
     * ################Set##################
     */
    /**
     * Set数据结构中是否存在某个值
     * @param key
     * @param o
     * @return 
     * @see
     */
    public Boolean sIsMember(String key, Object o)
    {
      final byte[] rawKey = rawString(key);
      final byte[] rawValue = rawValue(o);
      Jedis jedis=null;
      Boolean isMember=null;
      try
      {
          jedis=jedisClient.getJedis();
          isMember=jedis.sismember(rawKey, rawValue);
          return isMember;
      }
      catch (Exception e)
      {
          e.printStackTrace();
          return false;
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
    }
    
    /**
     * Set数据结构中值的集合
     * @param key
     * @param clazz
     * @return 
     * @see
     */
    public Set<Object> sMembers(String key,Class clazz)
    {
      final byte[] rawKey = rawString(key);
      Jedis jedis=null;
      Set<byte[]> rawValues=null;
      try
      {
          jedis=jedisClient.getJedis();
          rawValues=jedis.smembers(rawKey);
      }
      catch (Exception e)
      {
          e.printStackTrace();
      }finally{
          jedisClient.releaseConn(jedis,null);
      }
      return deserializeValues(rawValues,clazz);
    }
    
    /**
     * Set数据结构移除指定值
     * @param key
     * @param values
     * @return 
     * @see
     */
    public Long sRem(String key, Object[] values) {
        final byte[] rawKey = rawString(key);
        final byte[][] rawValues = rawValues(values);
        Jedis jedis=null;
        Long remNum=null;
        try
        {
            jedis=jedisClient.getJedis();
            remNum=jedis.srem(rawKey, rawValues);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return remNum;
    }
    
    /**
     * Set数据结构获取键包含的值集合大小
     * @param key
     * @return 
     * @see
     */
    public Long sCard(String key) { 
        final byte[] rawKey = rawString(key);
        Jedis jedis=null;
        Long size=null;
        try
        {
            jedis=jedisClient.getJedis();
            size=jedis.scard(rawKey);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }finally{
            jedisClient.releaseConn(jedis,null);
        }
        return size;
    }
    
    /**
     * Set数据结构反序列化值集合
     * @param rawValues
     * @param clazz
     * @return 
     * @see
     */
    public Set<Object> deserializeValues(Set<byte[]> rawValues,Class clazz){
        if (rawValues == null) {
            return null;
        }
        Set<Object> values =new LinkedHashSet<Object>(rawValues.size());
        for (byte[] bs : rawValues) {
            values.add(FastJson2JsonSerializer.deserialize(bs,clazz));
        }
        return values;
    }
    
    //Set值序列化
    public byte[][] rawValues(Object[] values){
        if (null==values)
        {
            return null;
        }
        byte[][] rawValues = new byte[values.length][];
        int i = 0;
        for (Object value : values) {
            rawValues[(i++)] = rawValue(value);
        }
        return rawValues;
    }
    
    //序列化Map数据结构
    public Map<Object, Object> deserializeHashMap(Map<byte[], byte[]> entries,Class clazz){
        if (entries == null) {
            return null;
        }
        Map<Object, Object> map = new LinkedHashMap<Object, Object>(entries.size());
        for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
            map.put(StringSerializer.deserialize((byte[])entry.getKey()), FastJson2JsonSerializer.deserialize((byte[])entry.getValue(), clazz));
        }
        return map;
    }
    
    public JedisClient getJedisClient()
    {
        return jedisClient;
    }
    public void setJedisClient(JedisClient jedisClient)
    {
        this.jedisClient = jedisClient;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值