这编文章主要介绍了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