feat: 新增 Redisson 自动配置

This commit is contained in:
2023-11-22 21:56:02 +08:00
parent 422affe1c1
commit b4894f5b4f
10 changed files with 449 additions and 3 deletions

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.charles7c.continew</groupId>
<artifactId>continew-starter-cache</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-cache-redisson</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>ContiNew Starter 缓存 - Redisson 模块</description>
<dependencies>
<!-- Redisson不仅仅是一个 Redis Java 客户端) -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* 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
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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<a href="https://gitee.com/herodotus/dante-engine">Dante Engine</a>
* @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<String> 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<String> 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.");
};
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* 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
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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<a href="https://gitee.com/herodotus/dante-engine">Dante Engine</a>
* @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
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* 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
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* 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 <T> void set(final String key, final T value) {
CLIENT.getBucket(key).set(value);
}
/**
* 设置缓存
*
* @param key 键
* @param value 值
* @param duration 过期时间
*/
public static <T> void set(final String key, final T value, final Duration duration) {
RBatch batch = CLIENT.createBatch();
RBucketAsync<T> bucket = batch.getBucket(key);
bucket.setAsync(value);
bucket.expireAsync(duration);
batch.execute();
}
/**
* 查询指定缓存
*
* @param key 键
* @return 值
*/
public static <T> T get(final String key) {
RBucket<T> 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<String> keys(final String keyPattern) {
Stream<String> 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();
}
}

View File

@@ -0,0 +1 @@
top.charles7c.continew.starter.cache.redisson.autoconfigure.RedissonAutoConfiguration

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>top.charles7c.continew</groupId>
<artifactId>continew-starter</artifactId>
<version>${revision}</version>
</parent>
<artifactId>continew-starter-cache</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>ContiNew Starter 缓存模块</description>
<modules>
<module>continew-starter-cache-redisson</module>
</modules>
<dependencies>
<!-- 核心模块 -->
<dependency>
<groupId>top.charles7c.continew</groupId>
<artifactId>continew-starter-core</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -55,12 +55,20 @@
<properties>
<revision>1.0.0-SNAPSHOT</revision>
<redisson.version>3.24.3</redisson.version>
<knife4j.version>4.3.0</knife4j.version>
<hutool.version>5.8.23</hutool.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Redisson不仅仅是一个 Redis Java 客户端) -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
<!-- Knife4j前身是 swagger-bootstrap-ui集 Swagger2 和 OpenAPI3 为一体的增强解决方案) -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
@@ -77,6 +85,13 @@
<version>${hutool.version}</version>
</dependency>
<!-- 缓存 - Redisson 模块 -->
<dependency>
<groupId>top.charles7c.continew</groupId>
<artifactId>continew-starter-cache-redisson</artifactId>
<version>${revision}</version>
</dependency>
<!-- API 文档模块 -->
<dependency>
<groupId>top.charles7c.continew</groupId>

View File

@@ -14,4 +14,11 @@
<name>${project.artifactId}</name>
<description>ContiNew Starter JSON - Jackson 模块</description>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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));

View File

@@ -71,6 +71,7 @@
<module>continew-starter-core</module>
<module>continew-starter-json</module>
<module>continew-starter-api-doc</module>
<module>continew-starter-cache</module>
</modules>
<dependencies>