网站制作 网站建设,郑州 网站建设 东区,论文中参考文献对不上,网站建设考察报告1. 背景
在新公司的脚手架中开发#xff0c;需要用到redis#xff0c;发现没有封装好一套能集成各种常用命令、包括Lua脚本的方便使用的RedisTemplateClient#xff0c;于是自己来实现下#xff1b;
springboot整合redis之后#xff0c;提供了操作redis的简便方式#…1. 背景
在新公司的脚手架中开发需要用到redis发现没有封装好一套能集成各种常用命令、包括Lua脚本的方便使用的RedisTemplateClient于是自己来实现下
springboot整合redis之后提供了操作redis的简便方式可以通过通用对象RedisTemplate方式操作String,Hash,List,Set SortSet五大数据类型
本次的实现就是基于Spring的RedisTemplate做的二次封装
2. 代码
1RedisTemplate配置类
配置的关键是序列化器的配置未配置好序列化器会导致例如时间类、Json格式放入缓存中时实际存放的值与预期不符或是在获取值时出现预期之外的错误
推荐下方的配置写法
/*** Redis配置类*/
Configuration
ConditionalOnClass({RedisOperations.class})
EnableConfigurationProperties(MyRedisCacheProperties.class)
AutoConfigureBefore(RedisAutoConfiguration.class)
ConditionalOnProperty(prefix my.project.cache, name enabled, havingValue true, matchIfMissing true)
public class RedisCacheAutoConfiguration {/*** 配置RedisTemplate** param factory Redis连接工厂* return RedisTemplateString, Object*/BeanConditionalOnMissingBeanConditionalOnProperty(name spring.cache.type, havingValue redis)public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) {// 创建一个模板类RedisTemplateString, Object template new RedisTemplate();// 将刚才的redis连接工厂设置到模板类中template.setConnectionFactory(factory);// 设置key的序列化器template.setKeySerializer(new StringRedisSerializer());// 设置value的序列化器// 使用Jackson 2将对象序列化为JSONJackson2JsonRedisSerializerObject jackson2JsonRedisSerializer this.getJackson2JsonRedisSerializer();template.setValueSerializer(jackson2JsonRedisSerializer);template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}/*** 获取Jackson2Json序列器** return Jackson2JsonRedisSerializer*/protected Jackson2JsonRedisSerializerObject getJackson2JsonRedisSerializer() {// 使用Jackson 2将对象序列化为JSONJackson2JsonRedisSerializerObject jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);jackson2JsonRedisSerializer.setObjectMapper(this.getObjectMapper());return jackson2JsonRedisSerializer;}/*** 获取JSON对象映射器** return ObjectMapper*/protected ObjectMapper getObjectMapper() {// json转对象类不设置默认的会将json转HashMapObjectMapper objectMapper JacksonUtils.newObjectMapper();// 设置序列化时保留对象的全类名这样反序列化时就不用指定类型了objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.WRAPPER_ARRAY);return objectMapper;}}2RedisClient的二次封装
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;/*** redisTemplate工具类 提供安全的redis操作命令*/
Slf4j
Service
public class RedisService {private static final RedisSerializerString stringRedisSerializer new StringRedisSerializer();private static final RedisSerializerObject fastJsonRedisSerializer new FastJsonRedisSerializer(Object.class);Resourceprivate RedisTemplateString, String redisTemplate;// common/*** 指定缓存失效时间** param key 键* param seconds 时间(秒)*/public void expire(String key, long seconds) {if (seconds 0) {redisTemplate.expire(key, seconds, TimeUnit.SECONDS);} else {throw new IllegalArgumentException(设置的redis过期时间应大于0! [badParam seconds ]);}}/*** 根据key 获取过期时间** param key 键 不能为null* return 时间(秒) 返回0代表为永久有效*/public long ttl(String key) {return Objects.requireNonNull(redisTemplate.getExpire(key, TimeUnit.SECONDS));}/*** 判断key是否存在** param key 键* return true 存在 false不存在*/public boolean hasKey(String key) {return Boolean.TRUE.equals(redisTemplate.hasKey(key));}/*** 删除key** param key 可以传一个值 或多个*/public void del(String... key) {if (key ! null key.length 0) {if (key.length 1) {redisTemplate.delete(key[0]);log.warn(删除redisKey成功 [key{}], key[0]);} else {ListString keys Arrays.asList(key);redisTemplate.delete(keys);log.warn(批量删除redisKey成功 [keys{}], JSON.toJSONString(keys));}}}// Lua/*** 执行lua脚本 脚本返回值必须为number** param luaScript* param keys* param args 为空时传Collections.emptyList()* return*/SuppressWarnings({rawtypes, unchecked})public Long evalLua(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, Long.class);Object execute redisTemplate.execute(script, stringRedisSerializer, fastJsonRedisSerializer, keys, args.toArray());return (Long) execute;}/*** 执行lua脚本 脚本返回值必须为jsonString** param luaScript* param keys* param args 为空时传Collections.emptyList()* return 返回类型*/SuppressWarnings({rawtypes, unchecked})public String evalLuaForJsonVal(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, String.class);Object execute redisTemplate.execute(script, stringRedisSerializer, fastJsonRedisSerializer, keys, args.toArray());return String.valueOf(execute);}/*** 执行lua脚本 脚本返回值必须为string 注意使用tostring函数或数值常量加单引号如const** param luaScript* param keys* param args 为空时传Collections.emptyList()* return 返回类型*/SuppressWarnings({rawtypes, unchecked})public String evalLuaForStringVal(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, String.class);Object execute redisTemplate.execute(script, stringRedisSerializer, stringRedisSerializer, keys, args.toArray());return String.valueOf(execute);}// String/*** 普通缓存获取** param key 键* return 值*/public String get(String key) {return key null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** param key 键* param value 值* return true成功 false失败*/public void set(String key, String value) {redisTemplate.opsForValue().set(key, value);}/*** 普通缓存放入并设置时间** param key 键* param value 值* param seconds 时间(秒) time要大于0 如果time小于等于0 将设置无限期* return true成功 false 失败*/public void setExpire(String key, String value, long seconds) {if (seconds 0) {redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);} else {set(key, value);}}/*** 递增** param key 键* param delta 要增加几(大于0)* return 递增后的结果*/public long incr(String key, long delta) {if (delta 0) {throw new IllegalArgumentException(incr递增因子必须大于0);}return Objects.requireNonNull(redisTemplate.opsForValue().increment(key, delta));}/*** 递减** param key 键* param delta 要减少几(小于0)* return 递减后的结果*/public long decr(String key, long delta) {if (delta 0) {throw new IllegalArgumentException(decr递减因子必须大于0);}return Objects.requireNonNull(redisTemplate.opsForValue().increment(key, -delta));}// Hash/*** HashGet** param key 键 不能为null* param hashKey 项 不能为null*/public Object hget(String key, String hashKey) {return redisTemplate.opsForHash().get(key, hashKey);}/*** 获取hashKey对应的所有键值** param key 键* return 对应的多个键值*/public MapObject, Object hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** param key 键* param map 对应多个键值*/public void hmset(String key, MapString, Object map) {redisTemplate.opsForHash().putAll(key, map);}/*** HashSet 并设置时间 不保证原子性** param key 键* param map 对应多个键值* param seconds 时间(秒)*/public void hmsetExpire(String key, MapString, Object map, long seconds) {redisTemplate.opsForHash().putAll(key, map);if (seconds 0) {expire(key, seconds);}}/*** 向一张hash表中放入数据,hash如果不存在将创建hash** param key 键* param item 项* param value 值*/public void hset(String key, String item, Object value) {redisTemplate.opsForHash().put(key, item, value);}/*** 向一张hash表中放入数据,hash如果不存在将创建hash 不保证原子性 原子性使用Lua脚本** param key 键* param item 项* param value 值* param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间*/public void hsetExpire(String key, String item, Object value, long time) {redisTemplate.opsForHash().put(key, item, value);if (time 0) {expire(key, time);}}/*** 删除hash表中的值** param key 键 不能为null* param hashKey 项 可以使多个 不能为null*/public void hdel(String key, Object... hashKey) {redisTemplate.opsForHash().delete(key, hashKey);}/*** 判断hash表中是否有该项的值** param key 键 不能为null* param hashKey 项 不能为null* return true 存在 false不存在*/public boolean hHasKey(String key, String hashKey) {return redisTemplate.opsForHash().hasKey(key, hashKey);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** param key 键* param item 项* param delta 要增加几(大于0)*/public double hincr(String key, String item, long delta) {if (delta 0) {throw new IllegalArgumentException(hincr递增因子必须大于0);}return redisTemplate.opsForHash().increment(key, item, delta);}/*** hash递减** param key 键* param item 项* param delta 要减少记(小于0)*/public double hdecr(String key, String item, long delta) {if (delta 0) {throw new IllegalArgumentException(hdecr递减因子必须大于0);}return redisTemplate.opsForHash().increment(key, item, -delta);}// set/*** 根据key获取Set中的所有值** param key 键*/public SetString sGet(String key) {return redisTemplate.opsForSet().members(key);}/*** 根据value从一个set中查询,是否存在** param key 键* param value 值* return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, value));}private String[] toStringArray(ListObject values) {ListString collect values.stream().map(String::valueOf).collect(Collectors.toList());String[] valStrArray new String[collect.size()];for (int i 0; i valStrArray.length; i) {valStrArray[i] collect.get(i);}return valStrArray;}/*** 将数据放入set缓存** param key 键* param values 值 可以是多个* return 成功个数*/public long sSet(String key, ListObject values) {return Objects.requireNonNull(redisTemplate.opsForSet().add(key, toStringArray(values)));}/*** 将set数据放入缓存** param key 键* param seconds 时间(秒)* param values 值 可以是多个* return 成功个数*/public long sSetAndTime(String key, long seconds, ListObject values) {long count Objects.requireNonNull(redisTemplate.opsForSet().add(key, toStringArray(values)));if (seconds 0)expire(key, seconds);return count;}/*** 获取set缓存的长度** param key 键*/public long sGetSetSize(String key) {return Objects.requireNonNull(redisTemplate.opsForSet().size(key));}/*** 移除值为value的** param key 键* param values 值 可以是多个* return 移除的个数*/public long setRemove(String key, ListObject values) {return Objects.requireNonNull(redisTemplate.opsForSet().remove(key, values));}// list/*** 获取list缓存的内容** param key 键* param start 开始* param end 结束 0 到 -1代表所有值*/public ListString lrange(String key, long start, long end) {return redisTemplate.opsForList().range(key, start, end);}/*** 获取list缓存的长度** param key 键*/public long lsize(String key) {return Objects.requireNonNull(redisTemplate.opsForList().size(key));}/*** 通过索引 获取list中的值** param key 键* param index 索引 index0时 0 表头1 第二个元素依次类推index0时-1表尾-2倒数第二个元素依次类推*/public String lGetIndex(String key, long index) {return redisTemplate.opsForList().index(key, index);}/*** item放入list缓存** param key 键* param value 值*/public void ladd(String key, String value) {redisTemplate.opsForList().rightPush(key, value);}/*** 将list放入缓存** param key 键* param value 值* return*/public long lpush(String key, ListString value) {return Objects.requireNonNull(redisTemplate.opsForList().rightPushAll(key, value));}/*** 根据索引修改list中的某条数据** param key 键* param index 索引* param updateVal 值* return*/public void lUpdateIndex(String key, long index, String updateVal) {redisTemplate.opsForList().set(key, index, updateVal);}}注意操作Lua脚本时一般脚本预期的返回值是数值型如果需要返回字符串或Json字符串需要特殊指定返回类型和序列化器如下 // Lua/*** 执行lua脚本 脚本返回值必须为number** param luaScript* param keys* param args 为空时传Collections.emptyList()* return*/SuppressWarnings({rawtypes, unchecked})public Long evalLua(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, Long.class);Object execute redisTemplate.execute(script, stringRedisSerializer, fastJsonRedisSerializer, keys, args.toArray());return (Long) execute;}/*** 执行lua脚本 脚本返回值必须为jsonString** param luaScript* param keys* param args 为空时传Collections.emptyList()* return 返回类型*/SuppressWarnings({rawtypes, unchecked})public String evalLuaForJsonVal(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, String.class);Object execute redisTemplate.execute(script, stringRedisSerializer, fastJsonRedisSerializer, keys, args.toArray());return String.valueOf(execute);}/*** 执行lua脚本 脚本返回值必须为string 注意使用tostring函数或数值常量加单引号如const** param luaScript* param keys* param args 为空时传Collections.emptyList()* return 返回类型*/SuppressWarnings({rawtypes, unchecked})public String evalLuaForStringVal(String luaScript, ListString keys, ListString args) {// 利用泛型擦除RedisScript script new DefaultRedisScript(luaScript, String.class);Object execute redisTemplate.execute(script, stringRedisSerializer, stringRedisSerializer, keys, args.toArray());return String.valueOf(execute);}
3Lua脚本相关
可参考之前的文章编码技巧——Lua脚本的应用这里再次附上代码
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Properties;/*** 文件加载类*/
Slf4j
public class FileLoaderUtils {/*** 读文件文本到String 根目录为resource** param fileName* return*/public static String loadFile2String(String fileName) {ClassLoader loader Thread.currentThread().getContextClassLoader();URL url loader.getResource(fileName);if (url null) {log.error(Cannot_locate_ fileName _as_a_classpath_resource.);throw new RuntimeException(Cannot_locate_ fileName _as_a_classpath_resource.);}try {return IOUtils.toString(url, StandardCharsets.UTF_8);} catch (IOException e) {log.error(读取文件内容为String失败! [fileName{}], fileName, e);throw new RuntimeException(读取文件内容为String失败! [fileName fileName ]);}}/*** 读文件文本到Property 根目录为resource** param fileName* return*/public static Properties loadFile2Property(String fileName) {ClassLoader loader Thread.currentThread().getContextClassLoader();InputStream in loader.getResourceAsStream(fileName);Properties properties new Properties();try {properties.load(in);if (in ! null) {in.close();}} catch (IOException e) {log.error(读取文件内容到Properties失败! [fileName{}], fileName, e);throw new RuntimeException(读取文件内容到Properties失败! [fileName fileName ]);}return properties;}}/*** LuaScript注册类*/
public class LuaScript {// 常用原子操作private static final String INCR_EXPIRE;private static final String DECR_EXPIRE;private static final String SETNX_EXPIRE;private static final String HSET_EXPIRE;private static final String INCRBY_EXPIRE;private static final String SET_EXPIRE;static {DECR_EXPIRE FileLoaderUtils.loadFile2String(lua/decr_expire.lua);INCR_EXPIRE FileLoaderUtils.loadFile2String(lua/incr_expire.lua);SETNX_EXPIRE FileLoaderUtils.loadFile2String(lua/setnx_expire.lua);HSET_EXPIRE FileLoaderUtils.loadFile2String(lua/hset_expire.lua);INCRBY_EXPIRE FileLoaderUtils.loadFile2String(lua/incrby_expire.lua);SET_EXPIRE FileLoaderUtils.loadFile2String(lua/set_expire.lua);}public static String incrExpireScript() {return INCR_EXPIRE;}public static String decrExpireScript() {return DECR_EXPIRE;}public static String setnxExpireScript() {return SETNX_EXPIRE;}public static String hsetExpireScript() {return HSET_EXPIRE;}public static String incrbyExpireScript() {return INCRBY_EXPIRE;}public static String setExpireScript() {return SET_EXPIRE;}}以上希望能帮到你 参考
Springboot整合Redis使用RedisTemplate操作redis五大类型用法详解
RedisTemplate执行lua脚本抛出异常IllegalStateException - 简书