From b4894f5b4facae97acb9d06b51fb599ae1bdc5b4 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Wed, 22 Nov 2023 21:56:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Redisson=20?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../continew-starter-cache-redisson/pom.xml | 25 +++ .../RedissonAutoConfiguration.java | 119 ++++++++++++ .../autoconfigure/RedissonProperties.java | 81 +++++++++ .../cache/redisson/util/RedisUtils.java | 171 ++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + continew-starter-cache/pom.xml | 29 +++ continew-starter-dependencies/pom.xml | 15 ++ .../continew-starter-json-jackson/pom.xml | 7 + .../JacksonAutoConfiguration.java | 3 - pom.xml | 1 + 10 files changed, 449 insertions(+), 3 deletions(-) create mode 100644 continew-starter-cache/continew-starter-cache-redisson/pom.xml create mode 100644 continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonAutoConfiguration.java create mode 100644 continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonProperties.java create mode 100644 continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/util/RedisUtils.java create mode 100644 continew-starter-cache/continew-starter-cache-redisson/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 continew-starter-cache/pom.xml diff --git a/continew-starter-cache/continew-starter-cache-redisson/pom.xml b/continew-starter-cache/continew-starter-cache-redisson/pom.xml new file mode 100644 index 00000000..d0801804 --- /dev/null +++ b/continew-starter-cache/continew-starter-cache-redisson/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + top.charles7c.continew + continew-starter-cache + ${revision} + + + continew-starter-cache-redisson + jar + + ${project.artifactId} + ContiNew Starter 缓存 - Redisson 模块 + + + + + org.redisson + redisson-spring-boot-starter + + + \ No newline at end of file diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonAutoConfiguration.java b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonAutoConfiguration.java new file mode 100644 index 00000000..41849e10 --- /dev/null +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonAutoConfiguration.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.continew.starter.cache.redisson.autoconfigure; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.redisson.codec.JsonJacksonCodec; +import org.redisson.config.ClusterServersConfig; +import org.redisson.config.SentinelServersConfig; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import top.charles7c.continew.starter.core.constant.StringConsts; + +import java.util.List; + +/** + * Redisson 自动配置 + * + * @author gengwei.zheng(Dante Engine) + * @author Charles7c + * @since 1.0.0 + */ +@Slf4j +@AutoConfiguration +@RequiredArgsConstructor +@ConditionalOnProperty(prefix = "spring.data.redisson", name = "enabled", havingValue = "true") +@EnableConfigurationProperties(RedissonProperties.class) +public class RedissonAutoConfiguration { + + private final RedissonProperties properties; + private final RedisProperties redisProperties; + private final ObjectMapper objectMapper; + + @Bean + public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() { + return config -> { + RedissonProperties.Mode mode = properties.getMode(); + String protocol = redisProperties.getSsl().isEnabled() ? "rediss://" : "redis://"; + switch (mode) { + case CLUSTER -> { + ClusterServersConfig clusterServersConfig = config.useClusterServers(); + ClusterServersConfig customClusterServersConfig = properties.getClusterServersConfig(); + if (null != customClusterServersConfig) { + BeanUtil.copyProperties(customClusterServersConfig, clusterServersConfig); + clusterServersConfig.setNodeAddresses(customClusterServersConfig.getNodeAddresses()); + } + // 下方配置如果为空,则使用 Redis 的配置 + if (CollUtil.isEmpty(clusterServersConfig.getNodeAddresses())) { + List nodeList = redisProperties.getCluster().getNodes(); + nodeList.stream().map(node -> protocol + node).forEach(clusterServersConfig::addNodeAddress); + } + if (StrUtil.isBlank(clusterServersConfig.getPassword())) { + clusterServersConfig.setPassword(redisProperties.getPassword()); + } + } + case SENTINEL -> { + SentinelServersConfig sentinelServersConfig = config.useSentinelServers(); + SentinelServersConfig customSentinelServersConfig = properties.getSentinelServersConfig(); + if (null != customSentinelServersConfig) { + BeanUtil.copyProperties(customSentinelServersConfig, sentinelServersConfig); + sentinelServersConfig.setSentinelAddresses(customSentinelServersConfig.getSentinelAddresses()); + } + // 下方配置如果为空,则使用 Redis 的配置 + if (CollUtil.isEmpty(sentinelServersConfig.getSentinelAddresses())) { + List nodeList = redisProperties.getSentinel().getNodes(); + nodeList.stream().map(node -> protocol + node).forEach(sentinelServersConfig::addSentinelAddress); + } + if (StrUtil.isBlank(sentinelServersConfig.getPassword())) { + sentinelServersConfig.setPassword(redisProperties.getPassword()); + } + if (StrUtil.isBlank(sentinelServersConfig.getMasterName())) { + sentinelServersConfig.setMasterName(redisProperties.getSentinel().getMaster()); + } + } + default -> { + SingleServerConfig singleServerConfig = config.useSingleServer(); + SingleServerConfig customSingleServerConfig = properties.getSingleServerConfig(); + if (null != customSingleServerConfig) { + BeanUtil.copyProperties(properties.getSingleServerConfig(), singleServerConfig); + } + // 下方配置如果为空,则使用 Redis 的配置 + singleServerConfig.setDatabase(redisProperties.getDatabase()); + if (StrUtil.isBlank(singleServerConfig.getPassword())) { + singleServerConfig.setPassword(redisProperties.getPassword()); + } + if (StrUtil.isBlank(singleServerConfig.getAddress())) { + singleServerConfig.setAddress(protocol + redisProperties.getHost() + StringConsts.COLON + redisProperties.getPort()); + } + } + } + // Jackson 处理 + config.setCodec(new JsonJacksonCodec(objectMapper)); + log.info("[ContiNew Starter] - Auto Configuration 'Redisson' completed initialization."); + }; + } +} diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonProperties.java b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonProperties.java new file mode 100644 index 00000000..8c6aa3cd --- /dev/null +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/autoconfigure/RedissonProperties.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.continew.starter.cache.redisson.autoconfigure; + +import lombok.Data; +import org.redisson.config.ClusterServersConfig; +import org.redisson.config.SentinelServersConfig; +import org.redisson.config.SingleServerConfig; +import org.springframework.boot.context.properties.ConfigurationProperties; + + +/** + * Redisson 配置属性 + * + * @author gengwei.zheng(Dante Engine) + * @author Charles7c + * @since 1.0.0 + */ +@Data +@ConfigurationProperties(prefix = "spring.data.redisson") +public class RedissonProperties { + + /** + * 是否启用 Redisson + */ + private boolean enabled = false; + + /** + * Redis 模式 + */ + private Mode mode = Mode.SINGLE; + + /** + * 单机服务配置 + */ + private SingleServerConfig singleServerConfig; + + /** + * 集群服务配置 + */ + private ClusterServersConfig clusterServersConfig; + + /** + * 哨兵服务配置 + */ + private SentinelServersConfig sentinelServersConfig; + + /** + * Redis 模式 + */ + public enum Mode { + /** + * 单机 + */ + SINGLE, + + /** + * 集群 + */ + CLUSTER, + + /** + * 哨兵 + */ + SENTINEL + } +} diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/util/RedisUtils.java b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/util/RedisUtils.java new file mode 100644 index 00000000..50475c96 --- /dev/null +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/java/top/charles7c/continew/starter/cache/redisson/util/RedisUtils.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.continew.starter.cache.redisson.util; + +import cn.hutool.extra.spring.SpringUtil; +import lombok.AccessLevel; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.redisson.api.*; +import org.redisson.config.Config; +import top.charles7c.continew.starter.core.constant.StringConsts; + +import java.time.Duration; +import java.util.Collection; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Redis 工具类 + * + * @author Charles7c + * @since 1.0.0 + */ +@Data +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RedisUtils { + + private static final RedissonClient CLIENT = SpringUtil.getBean(RedissonClient.class); + + /** + * 设置缓存 + * + * @param key 键 + * @param value 值 + */ + public static void set(final String key, final T value) { + CLIENT.getBucket(key).set(value); + } + + /** + * 设置缓存 + * + * @param key 键 + * @param value 值 + * @param duration 过期时间 + */ + public static void set(final String key, final T value, final Duration duration) { + RBatch batch = CLIENT.createBatch(); + RBucketAsync bucket = batch.getBucket(key); + bucket.setAsync(value); + bucket.expireAsync(duration); + batch.execute(); + } + + /** + * 查询指定缓存 + * + * @param key 键 + * @return 值 + */ + public static T get(final String key) { + RBucket bucket = CLIENT.getBucket(key); + return bucket.get(); + } + + /** + * 删除缓存 + * + * @param key 键 + * @return true 设置成功;false 设置失败 + */ + public static boolean delete(final String key) { + return CLIENT.getBucket(key).delete(); + } + + /** + * 设置缓存过期时间 + * + * @param key 键 + * @param timeout 过期时间(单位:秒) + * @return true 设置成功;false 设置失败 + */ + public static boolean expire(final String key, final long timeout) { + return expire(key, Duration.ofSeconds(timeout)); + } + + /** + * 设置缓存过期时间 + * + * @param key 键 + * @param duration 过期时间 + * @return true 设置成功;false 设置失败 + */ + public static boolean expire(final String key, final Duration duration) { + return CLIENT.getBucket(key).expire(duration); + } + + /** + * 查询缓存剩余过期时间 + * + * @param key + * 键 + * @return 缓存剩余过期时间(单位:毫秒) + */ + public static long getTimeToLive(final String key) { + return CLIENT.getBucket(key).remainTimeToLive(); + } + + /** + * 是否存在指定缓存 + * + * @param key 键 + * @return true 存在;false 不存在 + */ + public static boolean hasKey(String key) { + RKeys keys = CLIENT.getKeys(); + return keys.countExists(getNameMapper().map(key)) > 0; + } + + /** + * 查询缓存列表 + * + * @param keyPattern + * 键表达式 + * @return 缓存列表 + */ + public static Collection keys(final String keyPattern) { + Stream stream = CLIENT.getKeys().getKeysStreamByPattern(getNameMapper().map(keyPattern)); + return stream.map(key -> getNameMapper().unmap(key)).collect(Collectors.toList()); + } + + /** + * 格式化键,将各子键用 : 拼接起来 + * + * @param subKeys 子键列表 + * @return 键 + */ + public static String formatKey(String... subKeys) { + return String.join(StringConsts.COLON, subKeys); + } + + /** + * 根据 Redisson 配置,获取名称映射器 + * + * @return 名称映射器 + */ + private static NameMapper getNameMapper() { + Config config = CLIENT.getConfig(); + if (config.isClusterConfig()) { + return config.useClusterServers().getNameMapper(); + } + if (config.isSentinelConfig()) { + return config.useSentinelServers().getNameMapper(); + } + return config.useSingleServer().getNameMapper(); + } +} diff --git a/continew-starter-cache/continew-starter-cache-redisson/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/continew-starter-cache/continew-starter-cache-redisson/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..2937003e --- /dev/null +++ b/continew-starter-cache/continew-starter-cache-redisson/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +top.charles7c.continew.starter.cache.redisson.autoconfigure.RedissonAutoConfiguration \ No newline at end of file diff --git a/continew-starter-cache/pom.xml b/continew-starter-cache/pom.xml new file mode 100644 index 00000000..3585c03c --- /dev/null +++ b/continew-starter-cache/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + top.charles7c.continew + continew-starter + ${revision} + + + continew-starter-cache + pom + + ${project.artifactId} + ContiNew Starter 缓存模块 + + + continew-starter-cache-redisson + + + + + + top.charles7c.continew + continew-starter-core + + + \ No newline at end of file diff --git a/continew-starter-dependencies/pom.xml b/continew-starter-dependencies/pom.xml index 0953eb3b..498f65dd 100644 --- a/continew-starter-dependencies/pom.xml +++ b/continew-starter-dependencies/pom.xml @@ -55,12 +55,20 @@ 1.0.0-SNAPSHOT + 3.24.3 4.3.0 5.8.23 + + + org.redisson + redisson-spring-boot-starter + ${redisson.version} + + com.github.xiaoymin @@ -77,6 +85,13 @@ ${hutool.version} + + + top.charles7c.continew + continew-starter-cache-redisson + ${revision} + + top.charles7c.continew diff --git a/continew-starter-json/continew-starter-json-jackson/pom.xml b/continew-starter-json/continew-starter-json-jackson/pom.xml index 820bdc89..6573863f 100644 --- a/continew-starter-json/continew-starter-json-jackson/pom.xml +++ b/continew-starter-json/continew-starter-json-jackson/pom.xml @@ -14,4 +14,11 @@ ${project.artifactId} ContiNew Starter JSON - Jackson 模块 + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + \ No newline at end of file diff --git a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/charles7c/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/charles7c/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java index 8e540acf..3516f2fb 100644 --- a/continew-starter-json/continew-starter-json-jackson/src/main/java/top/charles7c/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java +++ b/continew-starter-json/continew-starter-json-jackson/src/main/java/top/charles7c/continew/starter/json/jackson/autoconfigure/JacksonAutoConfiguration.java @@ -17,7 +17,6 @@ package top.charles7c.continew.starter.json.jackson.autoconfigure; import cn.hutool.core.date.DatePattern; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -31,7 +30,6 @@ import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilde import org.springframework.context.annotation.Bean; import top.charles7c.continew.starter.json.jackson.serializer.BigNumberSerializer; -import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; @@ -57,7 +55,6 @@ public class JacksonAutoConfiguration { javaTimeModule.addSerializer(Long.class, BigNumberSerializer.SERIALIZER_INSTANCE); javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.SERIALIZER_INSTANCE); javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.SERIALIZER_INSTANCE); - javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); // 针对时间类型:LocalDateTime 的序列化和反序列化处理 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter)); diff --git a/pom.xml b/pom.xml index 7476ca9b..887ddcb4 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ continew-starter-core continew-starter-json continew-starter-api-doc + continew-starter-cache