From 28cb7b5857f8eb80e2e1709fcf9932ffb572ef1b Mon Sep 17 00:00:00 2001 From: liyuxin <1579178744@qq.com> Date: Mon, 5 Aug 2024 19:34:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 + pom.xml | 33 +++ .../FastJson2JsonRedisSerializer.java | 47 ++++ .../common/redis/configure/RedisConfig.java | 41 +++ .../common/redis/service/RedisService.java | 258 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../FastJson2JsonRedisSerializer.class | Bin 0 -> 2778 bytes .../common/redis/configure/RedisConfig.class | Bin 0 -> 2191 bytes .../common/redis/service/RedisService.class | Bin 0 -> 8267 bytes target/cloud-common-redis-3.6.3.jar | Bin 0 -> 7816 bytes target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 3 + .../compile/default-compile/inputFiles.lst | 3 + 14 files changed, 402 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java create mode 100644 src/main/java/com/muyu/common/redis/configure/RedisConfig.java create mode 100644 src/main/java/com/muyu/common/redis/service/RedisService.java create mode 100644 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 target/classes/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.class create mode 100644 target/classes/com/muyu/common/redis/configure/RedisConfig.class create mode 100644 target/classes/com/muyu/common/redis/service/RedisService.class create mode 100644 target/cloud-common-redis-3.6.3.jar create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4a2d5ea --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + + com.muyu + cloud-common + 3.6.3 + + 4.0.0 + + cloud-common-redis + + + cloud-common-redis缓存服务 + + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.muyu + cloud-common-core + + + + diff --git a/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java b/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..5959aad --- /dev/null +++ b/src/main/java/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.java @@ -0,0 +1,47 @@ +package com.muyu.common.redis.configure; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.muyu.common.core.constant.Constants; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +import java.nio.charset.Charset; + +/** + * Redis使用FastJson序列化 + * + * @author muyu + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer { + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer (Class clazz) { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize (T t) throws SerializationException { + if (t == null) { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize (byte[] bytes) throws SerializationException { + if (bytes == null || bytes.length <= 0) { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/src/main/java/com/muyu/common/redis/configure/RedisConfig.java b/src/main/java/com/muyu/common/redis/configure/RedisConfig.java new file mode 100644 index 0000000..ba8760e --- /dev/null +++ b/src/main/java/com/muyu/common/redis/configure/RedisConfig.java @@ -0,0 +1,41 @@ +package com.muyu.common.redis.configure; + +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +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.StringRedisSerializer; + +/** + * redis配置 + * + * @author muyu + */ +@Configuration +@EnableCaching +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig extends CachingConfigurerSupport { + @Bean + @SuppressWarnings(value = {"unchecked", "rawtypes"}) + public RedisTemplate redisTemplate (RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } +} diff --git a/src/main/java/com/muyu/common/redis/service/RedisService.java b/src/main/java/com/muyu/common/redis/service/RedisService.java new file mode 100644 index 0000000..db90c1e --- /dev/null +++ b/src/main/java/com/muyu/common/redis/service/RedisService.java @@ -0,0 +1,258 @@ +package com.muyu.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * spring redis 工具类 + * + * @author muyu + **/ +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +@Component +public class RedisService { + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject (final String key, final T value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject (final String key, final T value, final Long timeout, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * + * @return true=设置成功;false=设置失败 + */ + public boolean expire (final String key, final long timeout) { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * + * @return true=设置成功;false=设置失败 + */ + public boolean expire (final String key, final long timeout, final TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * + * @return 有效时间 + */ + public long getExpire (final String key) { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * + * @return true 存在 false不存在 + */ + public Boolean hasKey (String key) { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * + * @return 缓存键值对应的数据 + */ + public T getCacheObject (final String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject (final String key) { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * + * @return + */ + public boolean deleteObject (final Collection collection) { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * + * @return 缓存的对象 + */ + public long setCacheList (final String key, final List dataList) { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * + * @return 缓存键值对应的数据 + */ + public List getCacheList (final String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet (final String key, final Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * + * @return + */ + public Set getCacheSet (final String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap (final String key, final Map dataMap) { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * + * @return + */ + public Map getCacheMap (final String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue (final String key, final String hKey, final T value) { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * + * @return Hash中的对象 + */ + public T getCacheMapValue (final String key, final String hKey) { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * + * @return Hash对象集合 + */ + public List getMultiCacheMapValue (final String key, final Collection hKeys) { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * + * @return 是否成功 + */ + public boolean deleteCacheMapValue (final String key, final String hKey) { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * + * @return 对象列表 + */ + public Collection keys (final String pattern) { + return redisTemplate.keys(pattern); + } +} diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..27b030e --- /dev/null +++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.redis.configure.RedisConfig +com.muyu.common.redis.service.RedisService diff --git a/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..27b030e --- /dev/null +++ b/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,2 @@ +com.muyu.common.redis.configure.RedisConfig +com.muyu.common.redis.service.RedisService diff --git a/target/classes/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.class b/target/classes/com/muyu/common/redis/configure/FastJson2JsonRedisSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..a22af8ade3d65912cc0de0c0ec53a5bb0a134625 GIT binary patch literal 2778 zcmbtW-&Y$&6#j+;HVMm*hC+b~rIwb26jrO&wxI~2NdW`2WC0NsC&>~P!Y*gCQQ^T? zpZpX2A1GDJQQJp7$3M#Bo!uoY5)h9sJ3BKw_kQ=g-<`XE{`Jcn0FSW~K?4F3f-)M> z#L&LQzv4=n8zp5SzoivyhNdyy(Cu-CKrFr-MhIaE5gE;p8O93cjS1L&ftqd_? zoxaWUJXbcz(w4BK%;py6S1jGutig=NZE_Pv3)*E|L6o7@*>bGR^Bs+0ID1($$!1(d zr-UvU*U-(-d;UQN*)|1`oNehwiQ#$77lTV(a_#b-R{*TbXjlWCZ=VAz?tqO}u?7rN>}HCCyIl*;<96-@{$c zP<$;VYz+!++-A5DTT7kq)z!w3j2PnNbaF{uSXWn{rPnhvSv9@LaQhrKHuN&3Qpq?2 zB|dRs;ukeuJW0Gr*ocf#C=AWJf`((&e&^4J$Ec5ES7nz%bWs*hqe=R+=xHE?cS+|< zYG&j?1n=Q}2_ML~j}IBT9nII_ok7|#Emypwb;9tx)Hz9-d5A|6k}}5dm|;i=*k7In zlWLNxvto0D1mnGs45C8I>nqbUYC1cUQ`d7UWvpY($C$@q1Rr5S!laB8rWgj!FAp7VEUzU$! z?Ku;xyCVV)*O(+2{_i}w)0M%%sB1)hOnQ6NL5NL;mYmHC+jD%^Lmf$fQ_yy8ozy5{ zm0{xY$vkc*T%4|%p#qALCkIP&Q9A{BEmfyTsY3Jab=V;A2P!r-Cw~N<_=RCI;6xD` zE0n#)xH+aiIveiz*`@5|Bm0L;k8=~rnN_PGN{K{v^P=uLDy|o5Q!|F4S&m{gqIdJc zl6AW(Md|fb3EK?!|ElP+MXWeybN+J5^V0eN6dPIApACYd+HE3)QzZJBXao(RI zNbil9ru7+G$z$`smX#{%d#C3$p=EpSaa=%6vb2OuKp7r@t zdkeL_r!~*d=7`VQ!WpbyFb z%|xiTd{FvoQ2LOAN={Coh&>mI*ju8#WwICm3t`1E&G3oG%cr!thR-~7-;*N|`%vO{ zgc5IlKqEW&75%FT!u1Nb=I##v2sjgxMLeoH)p%iM|$8jyk7+X!M;U+rN9v6#j++g}NwIF5(qIkxOB{qf$_)5Q7yBB_zaO!!ne`cBh%0Rq} z#mnM@RPStuJKMtMo+mZeuJr62ZIDJzdRuNWP(l@gN<}Ko(3*8USI;seMn>0?Xk)n3 z2yv5ZZY#0r`nDsLuvg5VtD;;faV=6vB4L&@7A$lybo)Y+FHbne`yzL=RJ#ldBl%eD zL!-#UDUfMH@*G3fk!8Ca>;^=qT&4&Qxm5$S7rC#OeCgdaOUSk$l*>!*C!vz)V(8rA z@3~##UeSKNu|rH#IE(Y! zZwR%@H%hdV&P#`v);K*L$<@tfz3uu86ZwDeG0o7S>eH6tL+nL6M_K&agpAMWll4MO zx@y2jj}9KPWCzmv<>)nrt% z)2DZ4$!UAREqYv2pJVv^f1dcSx%RIFo_5P(&GlW%@42I%9j14qWqkO#EVI!2OB zUq;`Sv3;0F@x2F#rGn literal 0 HcmV?d00001 diff --git a/target/classes/com/muyu/common/redis/service/RedisService.class b/target/classes/com/muyu/common/redis/service/RedisService.class new file mode 100644 index 0000000000000000000000000000000000000000..7d150d7a374ab665f863ae707b3932b426f5efd9 GIT binary patch literal 8267 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 L@X$lUH`o3Td9;