SpringBoot+Redis使用jedis实现了对Redis基本类型操作超全工具类

这编文章主要介绍了springboot整合redis,使用jedis实现了对Redis基本类型操作,一些redis的常用命令总结到了一个公共的工具类中,其中使用了fastjson作为了序列化工具。
注:使用了 jdk 1.8 新特性 ,jdk版本需要>=1.8

一.添加maven依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.12.RELEASE</version>
    </parent>
    <!--统一版本号配置 -->
    <properties>
        <fastjson.version>1.2.47</fastjson.version>
        <redis.clients.version>2.9.0</redis.clients.version>
    </properties>
    <dependencies>
        <!-- springBoot 相关 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <!-- redis client -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${redis.clients.version}</version>
        </dependency>
        <!-- json -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>

二.配置文件
创建一个application.properties 配置文件

#端口配置
server.port=8081

#redis 基础配置
# Redis数据库索引(默认为0)
spring.redis.database=1
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
# Redis服务器连接端口
spring.redis.port=6379
# 连接超时时间(毫秒)
spring.redis.timeout=0

#redis 连接池配置
#池中最大链接数
spring.redis.pool-config.max-total=256
# 连接池中的最大空闲连接
spring.redis.pool-config.max-idle=128
# 连接池中的最小空闲连接
spring.redis.pool-config.min-idle=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool-config.max-wait-millis=1000
# 调用者获取链接时,是否检测当前链接有效性
spring.redis.pool-config.test-on-borrow=false
# 向链接池中归还链接时,是否检测链接有效性
spring.redis.pool-config.test-on-return=false
# 调用者获取链接时,是否检测空闲超时, 如果超时,则会被移除-
spring.redis.pool-config.test-while-idle=true
# 空闲链接检测线程一次运行检测多少条链接
spring.redis.pool-config.num-tests-per-eviction-run=8
#空闲链接检测线程检测周期。如果为负值,表示不运行检测线程。(单位:毫秒,默认为-1)
spring.redis.pool-config.time-between-eviction-runs-millis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.redis.pool-config.min-evictable-idle-time-millis=300000

#spring-session
spring.session.store-type=none
#日志配置
logging.level.root=WARN
logging.level.net.sf=WARN
logging.level.com.boot.redis=TRACE

三、代码实现
1)创建启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
/**
 * 启动类
 * springboot启动时会自动注入数据源和配置jpa 在不连接数据库情况下需要移除
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

由于不需要使用mysql等数据库所以移除默认数据源配置

2)创建一个RedisConfigProperties的配置类方便直接使用配置的参数

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
 * redis 配置属性类
 */
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class RedisConfigProperties {
    /**
     * Redis服务器地址
     */
    private String host;
    /**
     * Redis服务器连接密码(默认为空)
     */
    private String password;
    /**
     * Redis数据库索引(默认为0)
     */
    private int database;
    /**
     * Redis服务器连接端口
     */
    private int port;
    /**
     * 连接超时时间(毫秒)
     */
    private int timeout;
    //省略 get/set方法
}

3)创建JedisConfig

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
 * 配置类
 */
@Configuration
public class JedisConfig extends CachingConfigurerSupport {
    private Logger LOGGER = LoggerFactory.getLogger(JedisConfig.class);
    @Autowired
    private RedisConfigProperties redisConfigProperties;

    @Bean(name = "jedisPoolConfig")
    @ConfigurationProperties(prefix = "spring.redis.pool-config")
    public JedisPoolConfig getRedisConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        return config;
    }
    @Bean(name = "jedisPool")
    public JedisPool jedisPool(@Qualifier(value = "jedisPoolConfig") final JedisPoolConfig jedisPoolConfig) {
        LOGGER.info("Jedis Pool build start ");
        String host = redisConfigProperties.getHost();
        Integer timeout = redisConfigProperties.getTimeout();
        int port = redisConfigProperties.getPort();
        String password = redisConfigProperties.getPassword();
        int database = redisConfigProperties.getDatabase();
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password, database);
        LOGGER.info("Jedis Pool build success  host={} , port={}", host, port);
        return jedisPool;
    }
}

创建序列化工具类

import com.alibaba.fastjson.JSON;
import java.nio.charset.Charset;
/**
 * json  Serializer util
 */
public class JsonSerializer {
    public static final String UTF_8 = "UTF-8";
    /**
     * @param obj
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        return JSON.toJSONString(obj).getBytes(Charset.forName(UTF_8));
    }
    /**
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        return JSON.parseObject(data, clazz);
    }
}

创建时间计算工具类

import java.util.concurrent.TimeUnit;
/**
 * 时间计算工具类
 */
public class TimeUnitUtil {
    /**
     * 时间秒数计算
     *
     * @param timeUnit 单位枚举
     * @param duration 时间量
     * @return 秒数
     */
    public static int getSeconds(TimeUnit timeUnit, int duration) {
        return (int) timeUnit.toSeconds(duration);
    }
    /**
     * 时间毫秒数计算
     *
     * @param timeUnit 单位枚举
     * @param duration 时间量
     * @return 毫秒数
     */
    public static long getMillis(TimeUnit timeUnit, int duration) {
        return timeUnit.toMillis(duration);
    }
}

创建时间戳工具类SystemClock

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
 * 高并发场景下System.currentTimeMillis()的性能问题的优化
 * 时间戳打印建议使用
 */
public class SystemClock {
    private static final String THREAD_NAME = "system.clock";
    private static final SystemClock MILLIS_CLOCK = new SystemClock(1);
    private final long precision;
    private final AtomicLong now;

    private SystemClock(long precision) {
        this.precision = precision;
        now = new AtomicLong(System.currentTimeMillis());
        scheduleClockUpdating();
    }
    public static SystemClock millisClock() {
        return MILLIS_CLOCK;
    }
    private void scheduleClockUpdating() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
            Thread thread = new Thread(runnable, THREAD_NAME);
            thread.setDaemon(true);
            return thread;
        });
        scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), precision, precision, TimeUnit.MILLISECONDS);
    }
    public long now() {
        return now.get();
    }
}

这里未使用 java 自带的 System.currentTimeMillis() 作为时间戳,
具体原因请至上一篇博客https://blog.csdn.net/qq_38011415/article/details/82813299

创建redis 客户端接口

import redis.clients.jedis.Jedis;
import java.io.IOException;
public interface RedisClientInvoker<T> {
    T invoke(Jedis jedis) throws IOException;
}

创建redis 客户端工具类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisException;
import java.io.IOException;
@Component
public class RedisClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisClient.class);
    public <T> T invoke(JedisPool pool, RedisClientInvoker<T> clients) {
        T obj = null;
        Jedis jedis = null;
        boolean broken = false;
        try {
            jedis = pool.getResource();
            obj = clients.invoke(jedis);
        } catch (JedisException | IOException ex) {
            LOGGER.error(ex.getMessage(), ex);
        } finally {
            if (jedis != null) {
                if (jedis.isConnected())
                jedis.close();
            }
        }
        return obj;
    }
}

创建Redis缓存实现公共工具类

import com.alibaba.fastjson.JSONArray;
import com.boot.redis.util.JsonSerializer;
import com.boot.redis.util.SystemClock;
import com.boot.redis.util.TimeUnitUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisCache {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisCache.class);
    private static final int ZERO = 0;
    private static final int FIVE = 5;
    private static final String OK = "OK";
    private static final Long LONG_ZERO = 0l;
    /**
     * 默认失效时间5 分钟
     */
    public static final int DEFAULT_CACHE_SECONDS = TimeUnitUtil.getSeconds(TimeUnit.SECONDS, FIVE);

    /**
     * 默认失效时间毫秒 5 分钟
     */
    public static final long DEFAULT_CACHE_MILLIS = TimeUnitUtil.getMillis(TimeUnit.MINUTES, FIVE);

    @Autowired
    private JedisPool jedisPool;
    @Autowired
    private RedisClient redisClient;


    /**------------------key 普通 相关操作--------------------------------*/
    /**
     * 是否存在校验
     *
     * @param key
     * @return 是否存在
     */
    public boolean exists(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.exists(key.getBytes()));
    }

    /**
     * 用于设置 key 的过期时间,
     * key 过期后将不再可用。单位以秒计
     *
     * @param key
     * @return 是否设置成功
     */
    public Boolean expire(String key, int seconds) {
        return this.expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * 用于设置 key 的过期时间,key 过期后将不再可用。
     * 设置成功返回 1
     * 当 key 不存在或者不能为 key 设置过期时间时返回 0
     * <p>
     * 时间枚举介绍
     * TimeUnit.DAYS          //天
     * TimeUnit.HOURS         //小时
     * TimeUnit.MINUTES       //分钟
     * TimeUnit.SECONDS       //秒
     * TimeUnit.MILLISECONDS  //毫秒
     * TimeUnit.NANOSECONDS   //毫微秒
     * TimeUnit.MICROSECONDS  //微秒
     *
     * @param key
     * @param duration 时间量与单位一起使用
     * @param timeUnit 单位枚举类
     * @return
     */
    public Boolean expire(String key, int duration, TimeUnit timeUnit) {
        validateKeyParam(key);
        //时间转换成毫秒
        long millis = TimeUnitUtil.getMillis(timeUnit, duration);
        Long lResult = redisClient.invoke(jedisPool, (jedis) -> jedis.pexpire(key.getBytes(), millis));
        if (LONG_ZERO.equals(lResult)) {
            return false;
        }
        return true;
    }

    /**
     * 根据key 获取过期时间秒数
     * 不存在时返回负数
     *
     * @param key
     * @return 剩余过期时间秒数
     * 当 key 不存在时,返回 -2 。
     * 当 key 存在但没有设置剩余生存时间时,返回 -1 。
     * 否则,以秒为单位,返回 key 的剩余生存时间
     */
    public Long getExpiresTtl(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.ttl(key.getBytes()));
    }

    /**
     * 根据key 获取过期时间毫秒数
     * 不存在时返回负数
     *
     * @param key
     * @return 剩余过期时间毫秒数
     * 当 key 不存在时,返回 -2
     * 当 key 存在但没有设置剩余生存时间时,返回 -1
     * 否则,以毫秒为单位,返回 key 的剩余生存时间
     */
    public Long getExpiresPttl(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.pttl(key.getBytes()));
    }

    /**
     * 移除 key 的过期时间,key 将持久保持。
     * 当过期时间移除成功时,返回 1
     * 如果 key 不存在或 key 没有设置过期时间,返回 0
     *
     * @param key
     */
    public Long persist(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.persist(key.getBytes()));
    }

    /**
     * 根据key 获取存储类型
     *
     * @param key
     * @return 返回 key 的数据类型,数据类型有:
     * none (key不存在)
     * string (字符串)
     * list (列表)
     * set (集合)
     * zset (有序集)
     * hash (哈希表)
     */
    public String getType(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.type(key.getBytes()));
    }

    /**
     * 根据key移除
     *
     * @param key
     */
    public void remove(String key) {
        validateKeyParam(key);
        if (exists(key)) {
            redisClient.invoke(jedisPool, (jedis) -> jedis.del(key.getBytes()));
        }
    }
    /**------------------字符串相关操作--------------------------------*/
    /**
     * 添加数据到redis
     * 设置默认过期时间  5 分钟
     *
     * @param key
     * @param value
     */
    public Boolean put(String key, Object value) {
        return put(key, value, FIVE, TimeUnit.MINUTES);
    }

    /**
     * 添加数据到redis
     * 自定义过期时间
     * 注:从 Redis 2.6.12 版本开始, SET 在设置操作成功完成时,返回 OK
     *
     * @param key
     * @param value
     * @param duration 时间量
     * @param timeUnit 时间单位枚举
     */
    public Boolean put(String key, Object value, int duration, TimeUnit timeUnit) {
        validateParam(key, value);
        String result = redisClient.invoke(jedisPool, (jedis) -> {
                    String srtResult = jedis.set(key.getBytes(), JsonSerializer.serialize(value));
                    if (duration <= ZERO) {
                        //默认5 分钟
                        jedis.pexpire(key.getBytes(), DEFAULT_CACHE_MILLIS);
                    } else {
                        //时间转换成毫秒
                        long millis = TimeUnitUtil.getMillis(timeUnit, duration);
                        jedis.pexpire(key.getBytes(), millis);
                    }
                    return srtResult;
                }

        );
        if (OK.equals(result)) {
            return true;
        }
        return false;
    }

    /**
     * 添加数据到redis
     * 并设置永不过期
     * 注:一般使用较少,数据过大时尽量不要使用
     * 从 Redis 2.6.12 版本开始, SET 在设置操作成功完成时,返回 OK
     *
     * @param key
     * @param value
     */
    public Boolean putNeverExpires(String key, Object value) {
        validateParam(key, value);
        String result = redisClient.invoke(jedisPool, (jedis) -> {
                    String srtResult = jedis.set(key.getBytes(), JsonSerializer.serialize(value));
                    return srtResult;
                }
        );
        if (OK.equals(result)) {
            return true;
        }
        return false;
    }


    /**
     * 根据key 获取值
     *
     * @param key
     * @param clazz 类class
     * @return 类对象
     */
    public <T> T get(String key, Class<T> clazz) {
        validateKeyParam(key);
        byte[] result = redisClient.invoke(jedisPool, (jedis) -> jedis.get(key.getBytes()));
        if (result == null) {
            return null;
        }
        return JsonSerializer.deserialize(result, clazz);
    }


    /**
     * 根据key 获取值
     * 返回 key 的值,如果 key 不存在时,返回 nil。
     * 如果 key 不是字符串类型,那么返回一个错误。
     *
     * @param key
     * @return String
     */
    public String get(String key) {
        return this.get(key, String.class);
    }

    /**
     * 根据key 获取值
     *
     * @param key
     * @param clazz 集合泛型对象
     * @return 集合对象
     */
    public <T> List<T> getList(String key, Class<T> clazz) {
        String str = this.get(key, String.class);
        return JSONArray.parseArray(str, clazz);
    }

    /**
     * 将key 的值设为 value ,当且仅当 key 不存在
     * more值是时间戳 默认有效期是 5 分钟
     *
     * @param key
     * @return 设置成功返回 true 失败返回false
     */
    public boolean setNx(String key) {
        return this.setNx(key, SystemClock.millisClock().now(), FIVE, TimeUnit.MINUTES);
    }

    /**
     * 将key 的值设为 value ,当且仅当 key 不存在
     * 默认有效期是 5 分钟
     *
     * @param key
     * @param value 自定义值
     * @return 设置成功返回 true 失败返回false
     */
    public boolean setNx(String key, Object value) {
        return this.setNx(key, value, FIVE, TimeUnit.MINUTES);
    }

    /**
     * 将key 的值设为 value ,当且仅当 key 不存在
     * more值是时间戳
     *
     * @param key
     * @param seconds 自定义过期时间秒数
     * @return 设置成功返回 true 失败返回false
     */
    public boolean setNx(String key, int seconds) {
        return this.setNx(key, SystemClock.millisClock().now(), seconds, TimeUnit.SECONDS);
    }

    /**
     * 将key 的值设为 value ,当且仅当 key 不存在
     * 默认时间单位是秒
     *
     * @param key
     * @param value   自定义 value
     * @param seconds 自定义过期时间秒数
     * @return 设置成功返回 true 失败返回false
     */
    public boolean setNx(String key, Object value, int seconds) {
        return this.setNx(key, value, seconds, TimeUnit.SECONDS);
    }

    /**
     * 将key 的值设为 value ,当且仅当 key 不存在
     * 注:常用与分布式锁
     *
     * @param key
     * @param value
     * @param duration 时间量
     * @param timeUnit 时间单位枚举
     * @return 设置成功返回 true 失败返回false
     */
    public boolean setNx(String key, Object value, int duration, TimeUnit timeUnit) {
        validateParam(key, value);
        return redisClient.invoke(jedisPool, (jedis) -> {
                    long result = jedis.setnx(key.getBytes(), JsonSerializer.serialize(value));
                    if (result >= 1) {
                        if (duration <= ZERO) {
                            //默认5 分钟
                            jedis.pexpire(key.getBytes(), DEFAULT_CACHE_MILLIS);
                            return true;
                        } else {
                            //时间转换成毫秒
                            long millis = TimeUnitUtil.getMillis(timeUnit, duration);
                            jedis.pexpire(key.getBytes(), millis);
                            return true;
                        }
                    } else {
                        return false;
                    }
                }
        );
    }

    /**
     * 设置指定 key 的值,并返回 key 的旧值
     * 返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时,返回 null
     * 注:默认有效期为 5分钟
     *
     * @param key
     * @return String
     */
    public String getSet(String key, String value) {
        return this.getSet(key, value, FIVE, TimeUnit.MINUTES);
    }

    /**
     * 设置指定 key 的值,并返回 key 的旧值
     * 返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时,返回 null
     *
     * @param key
     * @return string key 的旧值
     */
    public String getSet(String key, String value, int duration, TimeUnit timeUnit) {
        validateParam(key, value);
        return redisClient.invoke(jedisPool, (jedis) -> {
                    String result = jedis.getSet(key, value);
                    if (duration <= ZERO) {
                        //设置默认过期时间 5 分钟
                        jedis.pexpire(key.getBytes(), DEFAULT_CACHE_MILLIS);
                        return result;
                    } else {
                        //时间转换成毫秒
                        long millis = TimeUnitUtil.getMillis(timeUnit, duration);
                        jedis.pexpire(key.getBytes(), millis);
                        return result;
                    }
                }
        );
    }

    /**
     * 用于获取指定 key 所储存的字符串值的长度。
     * 当 key 储存的不是字符串值时,返回一个错误
     * 当 key 不存在时,返回 0
     *
     * @param key
     */
    public Long getStrLen(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.strlen(key.getBytes()));
    }

    /**
     * key 中储存的数字值增一 (默认增量+1)
     * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
     * 注:
     * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内
     *
     * @param key
     * @return 执行命令之后 key 的值。
     */
    public Long incr(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.incr(key.getBytes()));
    }

    /**
     * key 中储存的数字值增一 (自定义增量值 )
     * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
     * 注:
     * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内
     *
     * @param key
     * @param value 自定义增量值
     * @return 执行命令之后 key 的值。
     */
    public Long incr(String key, long value) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.incrBy(key.getBytes(), value));
    }

    /**
     * 为 key 中所储存的值加上指定的浮点数增量值
     * 如果 key 不存在,那么 incrbyfloat 会先将 key 的值设为 0 ,再执行加法操作。
     *
     * @param key
     * @param value 增量值
     * @return 执行命令之后 key 的值。
     */
    public Double incr(String key, Double value) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.incrByFloat(key.getBytes(), value));
    }

    /**
     * 将 key 中储存的数字值减一
     * <p>
     * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
     * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key
     * @return 执行命令之后 key 的值。
     */
    public Long decr(String key) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.decr(key.getBytes()));
    }

    /**
     * 将 key 中储存的数字值减一
     * <p>
     * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
     * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
     * 本操作的值限制在 64 位(bit)有符号数字表示之内。
     *
     * @param key
     * @param value 自定义减量值
     * @return 执行命令之后 key 的值。
     */
    public Long decr(String key, long value) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.decrBy(key.getBytes(), value));
    }

    /**
     * 用于为指定的 key 追加值。
     * <p>
     * 如果 key 已经存在并且是一个字符串, APPEND 命令将 value
     * 追加到 key 原来的值的末尾。
     * 如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,
     * 就像执行 SET key value 一样。
     *
     * @param key
     * @param value
     * @return 追加指定值之后, key 中字符串的长度。
     */
    public Long append(String key, Object value) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.append(key.getBytes(), JsonSerializer.serialize(value)));
    }

    /**------------------zSet相关操作--------------------------------*/

    /**
     * 添加元素到有序集合,有序集合是按照元素的score进行排列
     * 注意: 在 Redis 2.4 版本以前, ZADD 每次只能添加一个元素。
     * 当 key 存在但不是有序集类型时,返回一个错误。
     *
     * @param key
     * @param obj
     * @param score 分值
     */
    public void zAddByScore(String key, Object obj, double score) {
        validateParam(key, obj);
        redisClient.invoke(jedisPool, jedis -> {
            jedis.zadd(key.getBytes(), score, JsonSerializer.serialize(obj));
            return null;
        });
    }

    /**
     * 根据key 计算集合中元素的数量
     *
     * @param key
     * @return 当 key 存在且是有序集类型时,返回有序集的基数。 当 key 不存在时,返回 0
     */
    public long zCard(String key) {
        validateKeyParam(key);
        long count = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zcard(key.getBytes());
            return result;
        });
        return count;
    }

    /**
     * 根据key 计算在有序集合中指定区间分数的成员数
     *
     * @param key
     * @param minScore 最小排序分值
     * @param maxScore 最大排序分值
     * @return 分数值在 min 和 max 之间的成员的数量。
     */
    public long zCount(String key, double minScore, double maxScore) {
        validateKeyParam(key);
        long count = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zcount(key.getBytes(), minScore, maxScore);
            return result;
        });
        return count;
    }

    /**
     * 返回有序集中,指定区间内的成员 -> 从小到大
     * 其中成员的位置按分数值递增(从小到大)来排序
     * <p>
     * 具有相同分数值的成员按字典序来排列
     * 注意:下标参数0 为起始
     * 负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推
     *
     * @param key
     * @param clazz
     * @param start 开始位置
     * @param end   结束位置
     * @return 相应对象集合
     */
    public <T> List<T> zRange(String key, Class<T> clazz, int start, int end) {
        validateKeyParam(key);
        List<T> result = redisClient.invoke(jedisPool, jedis -> {
            Set<byte[]> set = jedis.zrange(key.getBytes(), start, end);
            List<T> list = new ArrayList<>(set.size());
            if (CollectionUtils.isEmpty(set)) {
                return new ArrayList<T>(ZERO);
            }
            for (byte[] bytes : set) {
                T t = JsonSerializer.deserialize(bytes, clazz);
                list.add(t);
            }
            return list;
        });
        return result;
    }

    /**
     * 返回有序集中,指定区间内的成员 -> 从大到小
     * 其中成员的位置按分数值递增(从大到小)来排序
     *
     * @param key
     * @param clazz
     * @param start 开始位置
     * @param end   结束位置
     * @return 指定区间内,带有分数值的有序集成员的列表。
     */
    public <T> List<T> zRevRange(String key, Class<T> clazz, int start, int end) {
        validateKeyParam(key);
        List<T> result = redisClient.invoke(jedisPool, jedis -> {
            Set<byte[]> set = jedis.zrevrange(key.getBytes(), start, end);
            List<T> list = new ArrayList<>(set.size());
            if (CollectionUtils.isEmpty(set)) {
                return new ArrayList<T>(ZERO);
            }
            for (byte[] bytes : set) {
                T t = JsonSerializer.deserialize(bytes, clazz);
                list.add(t);
            }
            return list;
        });
        return result;
    }

    /**
     * 通过分数返回有序集合指定区间内的成员 -> 从小到大
     * 有序集成员按分数值递增(从小到大)次序排列
     *
     * @param key
     * @param clazz
     * @param minScore 最小分数
     * @param maxScore 最大分数
     * @return 指定区间内,带有分数值(可选)的有序集成员的列表。
     */
    public <T> List<T> zRangeByScore(String key, Class<T> clazz, double minScore, double maxScore) {
        validateKeyParam(key);
        List<T> result = redisClient.invoke(jedisPool, jedis -> {
            Set<byte[]> set = jedis.zrangeByScore(key.getBytes(), minScore, maxScore);
            List<T> list = new ArrayList<>(set.size());
            if (CollectionUtils.isEmpty(set)) {
                return new ArrayList<T>(ZERO);
            }
            for (byte[] bytes : set) {
                T t = JsonSerializer.deserialize(bytes, clazz);
                list.add(t);
            }
            return list;
        });
        return result;
    }

    /**
     * 通过分数返回有序集合指定区间内的成员 -> 从大到小
     * 有序集成员按分数值递增(从大到小)次序排列
     *
     * @param key
     * @param clazz
     * @param minScore 最小分数
     * @param maxScore 最大分数
     * @return 指定区间内,带有分数值(可选)的有序集成员的列表。
     */
    public <T> List<T> zRevRangeByScore(String key, Class<T> clazz, double minScore, double maxScore) {
        validateKeyParam(key);
        List<T> result = redisClient.invoke(jedisPool, jedis -> {
            Set<byte[]> set = jedis.zrevrangeByScore(key.getBytes(), maxScore, minScore);
            List<T> list = new ArrayList<>(set.size());
            if (CollectionUtils.isEmpty(set)) {
                return new ArrayList<T>(ZERO);
            }
            for (byte[] bytes : set) {
                T t = JsonSerializer.deserialize(bytes, clazz);
                list.add(t);
            }
            return list;
        });
        return result;
    }


    /**
     * 返回有序集中指定成员的排名
     * 按分数值递增(从小到大)顺序排列
     * 排名以 0 为底,也就是说, 分数值最小的成员排名为 0
     *
     * @param key
     * @param obj 成员对象
     * @return 如果成员是有序集 key 的成员,返回 member 的排名。
     * 如果成员不是有序集 key 的成员,返回空
     */
    public long zRank(String key, Object obj) {
        validateParam(key, obj);
        long sortIndex = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zrank(key.getBytes(), JsonSerializer.serialize(obj));
            return result;
        });
        return sortIndex;
    }

    /**
     * 返回有序集中指定成员的排名
     * 分数值递减(从大到小)排序
     * 排名以 0 为底,也就是说, 分数值最大的成员排名为 0
     *
     * @param key
     * @param obj 成员对象
     * @return 如果成员是有序集 key 的成员,返回 member 的排名。
     * 如果成员不是有序集 key 的成员,返回空
     */
    public long zRevRank(String key, Object obj) {
        validateParam(key, obj);
        long sortIndex = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zrevrank(key.getBytes(), JsonSerializer.serialize(obj));
            return result;
        });
        return sortIndex;
    }

    /**
     * 移除有序集合中的个成员
     * 名称为key 的有序集合中的元素 obj
     *
     * @param key
     * @param obj 元素
     * @return 被成功移除的成员的数量,不包括被忽略的成员。
     */
    public long zRem(String key, Object obj) {
        validateParam(key, obj);
        long lRow = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zrem(key.getBytes(), JsonSerializer.serialize(obj));
            return result;
        });
        return lRow;
    }

    /**
     * 移除有序集合中给定的排名区间的所有成员
     * 从排序小的开始删除
     *
     * @param start 开始位置 下标 0 开始
     * @param end   结束位置
     * @return 被移除成员的数量。
     */
    public long zRemRangeByRank(String key, int start, int end) {
        validateKeyParam(key);
        long lRow = redisClient.invoke(jedisPool, jedis -> {
            long result = jedis.zremrangeByRank(key.getBytes(), start, end);
            return result;
        });
        return lRow;
    }

    /**
     * 返回有序集中,成员的分数值
     * 如果成员元素不是有序集 key 的成员,或 key 不存在,返回 null
     *
     * @param key
     * @param obj 成员对象
     * @return 如果成员是有序集 key 的成员,返回 member 的排名。
     * 如果成员不是有序集 key 的成员,返回空
     */
    public Double zScore(String key, Object obj) {
        validateParam(key, obj);
        Double score = redisClient.invoke(jedisPool, jedis -> {
            Double sResult = jedis.zscore(key.getBytes(), JsonSerializer.serialize(obj));
            return sResult;
        });
        return score;
    }


    /**
     * -------------------list相关操作---------------------
     */
    /**
     * 将一个或多个值插入到列表头部
     * 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
     * 当 key 存在但不是列表类型时,返回一个错误
     * 注意:在Redis 2.4版本以前的 lpush 命令,都只接受单个 value 值。
     *
     * @param key
     * @return 列表的长度。
     */
    public Long lpush(String key, Object... value) {
        validateParam(key, value);
        Long len = redisClient.invoke(jedisPool, jedis -> {
            return jedis.lpush(key.getBytes(), JsonSerializer.serialize(value));
        });
        return len;
    }

    /**
     * 用于返回列表的长度
     * 如果列表 key 不存在,则 key 被解释为一个空列表,
     * 返回 0 。 如果 key 不是列表类型,返回一个错误
     *
     * @param key
     * @return list 集大小
     */
    public long lLen(String key) {
        validateKeyParam(key);
        long count = redisClient.invoke(jedisPool, jedis -> {
            long countAll = jedis.llen(key.getBytes());
            return countAll;
        });
        return count;
    }


    /**
     * 通过索引获取列表中的元素
     * 如果指定索引值不在列表的区间范围内,返回 null
     * 使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
     *
     * @param key
     * @param index 集合索引
     * @return 元素信息
     */
    public <T> T lindex(String key, int index, Class<T> clazz) {
        validateParam(key, clazz);
        T obj = redisClient.invoke(jedisPool, jedis -> {
            byte[] result = jedis.lindex(key.getBytes(), index);
            if (result == null) {
                return null;
            }
            return JsonSerializer.deserialize(result, clazz);
        });
        return obj;
    }

    /**
     * 移除并返回列表的第一个元素
     *
     * @param key
     * @return 列表的第一个元素。 当列表 key 不存在时,返回 null。
     */
    public <T> T lpop(String key, Class<T> clazz) {
        validateParam(key, clazz);
        T obj = redisClient.invoke(jedisPool, jedis -> {
            byte[] result = jedis.lpop(key.getBytes());
            if (result == null) {
                return null;
            }
            return JsonSerializer.deserialize(result, clazz);
        });
        return obj;
    }

    /**
     * 移除并返回列表的最后一个元素
     *
     * @param key
     * @return 列表的最后一个元素。 当列表不存在时,返回 null
     */
    public <T> T rpop(String key, Class<T> clazz) {
        validateParam(key, clazz);
        T obj = redisClient.invoke(jedisPool, jedis -> {
            byte[] result = jedis.rpop(key.getBytes());
            if (result == null) {
                return null;
            }
            return JsonSerializer.deserialize(result, clazz);
        });
        return obj;
    }

    /**
     * -------------------Redis 发布订阅---------------------
     */
    public Long publish(String key, Object value) {
        validateParam(key, value);
        Long len = redisClient.invoke(jedisPool, jedis -> {
            return jedis.publish(key.getBytes(), JsonSerializer.serialize(value));
        });
        return len;
    }

    /**
     * -------------------Hash相关操作---------------------
     */

    /**
     * 用于为哈希表中的字段赋值
     * 如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作
     * 如果字段已经存在于哈希表中,旧值将被覆盖
     *
     * @param key
     * @param field
     * @param value 值
     */
    public Boolean setHash(String key, String field, Object value) {
        validateKeyParam(key);
        long result =
                redisClient.invoke(jedisPool, (jedis) -> jedis.hset(key.getBytes(), field.getBytes(),
                        JsonSerializer.serialize(value)));
        if (LONG_ZERO.equals(result)) {
            return false;
        }
        return true;
    }

    /**
     * 用于为哈希表中不存在的的字段赋值
     * 如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作
     * 如果字段已经存在于哈希表中,操作无效
     * 如果 key 不存在,一个新哈希表被创建并执行 HSETNX 命令
     * 设置成功,返回 1 。 如果给定字段已经存在且没有操作被执行,返回 0 。
     * 注意版本 >= 2.0.0
     *
     * @param key
     * @param field
     * @param value 值
     * @return 是否成功
     */
    public Boolean setNxHash(String key, String field, Object value) {
        validateKeyParam(key);
        long result =
                redisClient.invoke(jedisPool, (jedis) -> jedis.hsetnx(key.getBytes(), field.getBytes(),
                        JsonSerializer.serialize(value)));
        if (LONG_ZERO.equals(result)) {
            return false;
        }
        return true;
    }

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param field
     * @return 返回给定字段的值。如果给定的字段或 key 不存在时,返回 null
     */
    public <T> T getHash(String key, String field, Class<T> clazz) {
        validateKeyParam(key);
        byte[] value = redisClient.invoke(jedisPool, (jedis) -> jedis.hget(key.getBytes(), field.getBytes()));
        if (value != null) {
            return JsonSerializer.deserialize(value, clazz);
        }
        return null;
    }

    /**
     * 用于删除哈希表 key 中的个指定字段
     *
     * @param key
     * @param field
     */
    public void delHash(String key, String field) {
        validateParam(key, field);
        redisClient.invoke(jedisPool, (jedis) -> jedis.hdel(key.getBytes(), field.getBytes()));
    }

    /**
     * 用于查看哈希表的指定字段是否存在
     *
     * @param key
     * @param field
     */
    public Boolean hashKeyExists(String key, String field) {
        validateParam(key, field);
        return redisClient.invoke(jedisPool, (jedis) -> jedis.hexists(key.getBytes(), field.getBytes()));
    }

    /**
     * 获取在哈希表中指定 key 的所有字段和值
     * 在返回值里,紧跟每个字段名(field name)之后是字段的值(value),
     * 所以返回值的长度是哈希表大小的两倍。
     *
     * @param key
     * @return 以列表形式返回哈希表的字段及字段值。 若 key 不存在,返回空列表。
     */
    public <T> Map<String, T> getAllHash(String key, Class<T> clazz) {
        validateKeyParam(key);
        return redisClient.invoke(jedisPool, (jedis) -> {
            Map<byte[], byte[]> map = jedis.hgetAll(key.getBytes());
            Map<String, T> resultMap = new HashMap<>();
            if (map != null) {
                for (Map.Entry<byte[], byte[]> item : map.entrySet()) {
                    byte[] newKey = item.getKey();
                    T newValue = JsonSerializer.deserialize(item.getValue(), clazz);
                    resultMap.put(Arrays.toString(newKey), newValue);
                }
                return resultMap;
            }
            return null;
        });
    }


    /**
     * -------------------以下危险操作 谨慎使用 ---------------------
     */

    /**
     * 清空所有redis 数据
     * 谨慎使用
     */
    public void clearAll() {
        LOGGER.error("缓存的clear方法被调用,所有缓存数据都被清除!");
        redisClient.invoke(jedisPool, BinaryJedis::flushAll);
    }

    /**
     * 查找所有符合给定模式( pattern)的 key 。
     * 谨慎使用(存在性能问题)
     * 会引发Redis锁,并且增加Redis的CPU占用
     *
     * @param pattern
     * @return 符合给定模式的 key 列表 (Array)。
     */
    public List<String> findKeys(String pattern) {
        Assert.hasText(pattern, "查找规则不能为空");
        Charset charset = Charset.forName("UTF-8");
        return redisClient.invoke(jedisPool, jedis -> {
            Set<String> sets = jedis.keys(("*" + pattern + "*"));
            if (sets != null) {
                List<String> list = new ArrayList<>(sets.size());
                list.addAll(sets);
                return list;
            }
            return null;
        });
    }

    /**
     * 校验参数
     */
    private void validateParam(String key, Object value) {
        this.validateKeyParam(key);
        Assert.notNull(value, "value不能为空");
        Assert.isInstanceOf(Object.class, value, "value没有实现Object接口,无法序列化");
    }

    /**
     * 校验参数
     */
    private void validateKeyParam(String key) {
        Assert.hasText(key, "key不能为空");
        Assert.notNull(jedisPool, "jedis连接初始化失败");
    }

    public synchronized Jedis getRedis() {
        return jedisPool.getResource();
    }

    public void setJedisPool(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

}

测试
创建测试类

@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisSimplePutTest {
    private Logger LOGGER = LoggerFactory.getLogger(RedisSimplePutTest.class);
    @Autowired
    private RedisCache redisCache;
    /**
     * 存储字符串
     */
    @Test
    public void testPutString() {
        String key = "test_string_2";
        String value = "string_value";
        LOGGER.info("存储字符串开始 key={} value={}", key, value);
        boolean putFlag = redisCache.put(key, "string_value", 0, TimeUnit.MINUTES);
        LOGGER.info("存储字符串结束 putFlag={}", putFlag);
        String rvalue = redisCache.get(key);
        LOGGER.info("存储字符串-> 查询缓存value={}", rvalue);
    }
    /**
     * 存储自定义对象
     */
    @Test
    public void testPutObject() {
        String key = "test_object_article_2";
        TArticle tArticle =
                new TArticle("1111", "测试文章1", "作者1", 1.0, 1);
        LOGGER.info("存储对象开始 key={} value={}", key, tArticle);
        redisCache.put(key, tArticle);
        LOGGER.info("存储对象结束");
        TArticle value = redisCache.get(key, TArticle.class);
        LOGGER.info("存储对象-> 查询缓存value={}", value);
    }
}

输出结果

存储字符串开始 key=test_string_2 value=string_value
存储字符串结束 putFlag=true
存储字符串-> 查询缓存value=string_value

存储对象开始 key=test_object_article_2 value=测试文章1
存储对象结束
存储对象-> 查询缓存value=测试文章1

猜你喜欢

转载自blog.csdn.net/qq_38011415/article/details/82823778