SpringBoot 整合Redisson重寫cacheName支持多參數(shù)的案例代碼
依賴
<!--常用工具類 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
</dependency>
<!--redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>配置文件
spring:
data:
redis:
# 地址
host: localhost
# 端口,默認(rèn)為6379
port: 6379
# 數(shù)據(jù)庫索引
database: 0
# 密碼(如沒有密碼請注釋掉)
# password: ******
# 連接超時時間
timeout: 10s
# 是否開啟ssl
ssl:
enabled: false
redisson:
# redis key前綴
keyPrefix:
# 線程池數(shù)量
threads: 4
# Netty線程池數(shù)量
nettyThreads: 8
# 單節(jié)點配置
singleServerConfig:
# 客戶端名稱
clientName: ${ruoyi.name}
# 最小空閑連接數(shù)
connectionMinimumIdleSize: 8
# 連接池大小
connectionPoolSize: 32
# 連接空閑超時,單位:毫秒
idleConnectionTimeout: 10000
# 命令等待超時,單位:毫秒
timeout: 3000
# 發(fā)布和訂閱連接池大小
subscriptionConnectionPoolSize: 50Properties
package com.example.redisson.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Redisson 配置屬性
*
* @author Lion Li
*/
@Data
@Component
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
/**
* redis緩存key前綴
*/
private String keyPrefix;
/**
* 線程池數(shù)量,默認(rèn)值 = 當(dāng)前處理核數(shù)量 * 2
*/
private int threads;
/**
* Netty線程池數(shù)量,默認(rèn)值 = 當(dāng)前處理核數(shù)量 * 2
*/
private int nettyThreads;
/**
* 單機(jī)服務(wù)配置
*/
private SingleServerConfig singleServerConfig;
/**
* 集群服務(wù)配置
*/
private ClusterServersConfig clusterServersConfig;
@Data
@NoArgsConstructor
public static class SingleServerConfig {
/**
* 客戶端名稱
*/
private String clientName;
/**
* 最小空閑連接數(shù)
*/
private int connectionMinimumIdleSize;
/**
* 連接池大小
*/
private int connectionPoolSize;
/**
* 連接空閑超時,單位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超時,單位:毫秒
*/
private int timeout;
/**
* 發(fā)布和訂閱連接池大小
*/
private int subscriptionConnectionPoolSize;
}
@Data
@NoArgsConstructor
public static class ClusterServersConfig {
/**
* 客戶端名稱
*/
private String clientName;
/**
* master最小空閑連接數(shù)
*/
private int masterConnectionMinimumIdleSize;
/**
* master連接池大小
*/
private int masterConnectionPoolSize;
/**
* slave最小空閑連接數(shù)
*/
private int slaveConnectionMinimumIdleSize;
/**
* slave連接池大小
*/
private int slaveConnectionPoolSize;
/**
* 連接空閑超時,單位:毫秒
*/
private int idleConnectionTimeout;
/**
* 命令等待超時,單位:毫秒
*/
private int timeout;
/**
* 發(fā)布和訂閱連接池大小
*/
private int subscriptionConnectionPoolSize;
/**
* 讀取模式
*/
private ReadMode readMode;
/**
* 訂閱模式
*/
private SubscriptionMode subscriptionMode;
}
}Config
注入自定義PlusSpringCacheManager支持多參數(shù)
package org.dromara.common.redis.config;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.redis.config.properties.RedissonProperties;
import org.dromara.common.redis.handler.KeyPrefixHandler;
import org.dromara.common.redis.manager.PlusSpringCacheManager;
import org.redisson.client.codec.StringCodec;
import org.redisson.codec.CompositeCodec;
import org.redisson.codec.TypedJsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
/**
* redis配置
*
* @author Lion Li
*/
@Slf4j
@AutoConfiguration
@EnableCaching
@EnableConfigurationProperties(RedissonProperties.class)
public class RedisConfig {
@Autowired
private RedissonProperties redissonProperties;
@Autowired
private ObjectMapper objectMapper;
@Bean
public RedissonAutoConfigurationCustomizer redissonCustomizer() {
return config -> {
ObjectMapper om = objectMapper.copy();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化輸入的類型,類必須是非final修飾的。序列化時將對象全類名一起保存下來
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
TypedJsonJacksonCodec jsonCodec = new TypedJsonJacksonCodec(Object.class, om);
// 組合序列化 key 使用 String 內(nèi)容使用通用 json 格式
CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
config.setThreads(redissonProperties.getThreads())
.setNettyThreads(redissonProperties.getNettyThreads())
// 緩存 Lua 腳本 減少網(wǎng)絡(luò)傳輸(redisson 大部分的功能都是基于 Lua 腳本實現(xiàn))
.setUseScriptCache(true)
.setCodec(codec);
RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig();
if (ObjectUtil.isNotNull(singleServerConfig)) {
// 使用單機(jī)模式
config.useSingleServer()
//設(shè)置redis key前綴
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(singleServerConfig.getTimeout())
.setClientName(singleServerConfig.getClientName())
.setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize())
.setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize())
.setConnectionPoolSize(singleServerConfig.getConnectionPoolSize());
}
// 集群配置方式 參考下方注釋
RedissonProperties.ClusterServersConfig clusterServersConfig = redissonProperties.getClusterServersConfig();
if (ObjectUtil.isNotNull(clusterServersConfig)) {
config.useClusterServers()
//設(shè)置redis key前綴
.setNameMapper(new KeyPrefixHandler(redissonProperties.getKeyPrefix()))
.setTimeout(clusterServersConfig.getTimeout())
.setClientName(clusterServersConfig.getClientName())
.setIdleConnectionTimeout(clusterServersConfig.getIdleConnectionTimeout())
.setSubscriptionConnectionPoolSize(clusterServersConfig.getSubscriptionConnectionPoolSize())
.setMasterConnectionMinimumIdleSize(clusterServersConfig.getMasterConnectionMinimumIdleSize())
.setMasterConnectionPoolSize(clusterServersConfig.getMasterConnectionPoolSize())
.setSlaveConnectionMinimumIdleSize(clusterServersConfig.getSlaveConnectionMinimumIdleSize())
.setSlaveConnectionPoolSize(clusterServersConfig.getSlaveConnectionPoolSize())
.setReadMode(clusterServersConfig.getReadMode())
.setSubscriptionMode(clusterServersConfig.getSubscriptionMode());
}
log.info("初始化 redis 配置");
};
}
/**
* 自定義緩存管理器 整合spring-cache
*/
@Bean
public CacheManager cacheManager() {
return new PlusSpringCacheManager();
}
/**
* redis集群配置 yml
*
* --- # redis 集群配置(單機(jī)與集群只能開啟一個另一個需要注釋掉)
* spring.data:
* redis:
* cluster:
* nodes:
* - 192.168.0.100:6379
* - 192.168.0.101:6379
* - 192.168.0.102:6379
* # 密碼
* password:
* # 連接超時時間
* timeout: 10s
* # 是否開啟ssl
* ssl.enabled: false
*
* redisson:
* # 線程池數(shù)量
* threads: 16
* # Netty線程池數(shù)量
* nettyThreads: 32
* # 集群配置
* clusterServersConfig:
* # 客戶端名稱
* clientName: ${ruoyi.name}
* # master最小空閑連接數(shù)
* masterConnectionMinimumIdleSize: 32
* # master連接池大小
* masterConnectionPoolSize: 64
* # slave最小空閑連接數(shù)
* slaveConnectionMinimumIdleSize: 32
* # slave連接池大小
* slaveConnectionPoolSize: 64
* # 連接空閑超時,單位:毫秒
* idleConnectionTimeout: 10000
* # 命令等待超時,單位:毫秒
* timeout: 3000
* # 發(fā)布和訂閱連接池大小
* subscriptionConnectionPoolSize: 50
* # 讀取模式
* readMode: "SLAVE"
* # 訂閱模式
* subscriptionMode: "MASTER"
*/
}重寫cacheName方法
/**
* Copyright (c) 2013-2021 Nikita Koksharov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 org.dromara.common.redis.manager;
import org.dromara.common.redis.utils.RedisUtils;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A {@link org.springframework.cache.CacheManager} implementation
* backed by Redisson instance.
* <p>
* 修改 RedissonSpringCacheManager 源碼
* 重寫 cacheName 處理方法 支持多參數(shù)
*
* @author Nikita Koksharov
*
*/
@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
private boolean dynamic = true;
private boolean allowNullValues = true;
private boolean transactionAware = true;
Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
/**
* Creates CacheManager supplied by Redisson instance
*/
public PlusSpringCacheManager() {
}
/**
* Defines possibility of storing {@code null} values.
* <p>
* Default is <code>true</code>
*
* @param allowNullValues stores if <code>true</code>
*/
public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
}
/**
* Defines if cache aware of Spring-managed transactions.
* If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
* <p>
* Default is <code>false</code>
*
* @param transactionAware cache is transaction aware if <code>true</code>
*/
public void setTransactionAware(boolean transactionAware) {
this.transactionAware = transactionAware;
}
/**
* Defines 'fixed' cache names.
* A new cache instance will not be created in dynamic for non-defined names.
* <p>
* `null` parameter setups dynamic mode
*
* @param names of caches
*/
public void setCacheNames(Collection<String> names) {
if (names != null) {
for (String name : names) {
getCache(name);
}
dynamic = false;
} else {
dynamic = true;
}
}
/**
* Set cache config mapped by cache name
*
* @param config object
*/
public void setConfig(Map<String, ? extends CacheConfig> config) {
this.configMap = (Map<String, CacheConfig>) config;
}
protected CacheConfig createDefaultConfig() {
return new CacheConfig();
}
@Override
public Cache getCache(String name) {
// 重寫 cacheName 支持多參數(shù)
String[] array = StringUtils.delimitedListToStringArray(name, "#");
name = array[0];
Cache cache = instanceMap.get(name);
if (cache != null) {
return cache;
}
if (!dynamic) {
return cache;
}
CacheConfig config = configMap.get(name);
if (config == null) {
config = createDefaultConfig();
configMap.put(name, config);
}
if (array.length > 1) {
config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
}
if (array.length > 2) {
config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
}
if (array.length > 3) {
config.setMaxSize(Integer.parseInt(array[3]));
}
if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
return createMap(name, config);
}
return createMapCache(name, config);
}
private Cache createMap(String name, CacheConfig config) {
RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
Cache cache = new RedissonCache(map, allowNullValues);
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
}
return cache;
}
private Cache createMapCache(String name, CacheConfig config) {
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
Cache cache = new RedissonCache(map, config, allowNullValues);
if (transactionAware) {
cache = new TransactionAwareCacheDecorator(cache);
}
Cache oldCache = instanceMap.putIfAbsent(name, cache);
if (oldCache != null) {
cache = oldCache;
} else {
map.setMaxSize(config.getMaxSize());
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet());
}
}支持多參數(shù)
key 格式為 cacheNames#ttl#maxIdleTime#maxSize
ttl 過期時間 如果設(shè)置為0則不過期 默認(rèn)為0
maxIdleTime 最大空閑時間 根據(jù)LRU算法清理空閑數(shù)據(jù) 如果設(shè)置為0則不檢測 默認(rèn)為0
maxSize 組最大長度 根據(jù)LRU算法清理溢出數(shù)據(jù) 如果設(shè)置為0則無限長 默認(rèn)為0
例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
package com.example.redisson.core;
public interface CacheNames {
/**
* 演示案例
*/
String DEMO_CACHE = "demo:cache#60s#10m#20";
/**
* 系統(tǒng)配置
*/
String SYS_CONFIG = "sys_config";
/**
* 數(shù)據(jù)字典
*/
String SYS_DICT = "sys_dict";
/**
* 租戶
*/
String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
/**
* 用戶賬戶
*/
String SYS_USER_NAME = "sys_user_name#30d";
/**
* 用戶名稱
*/
String SYS_NICKNAME = "sys_nickname#30d";
/**
* 部門
*/
String SYS_DEPT = "sys_dept#30d";
/**
* OSS內(nèi)容
*/
String SYS_OSS = "sys_oss#30d";
/**
* OSS配置
*/
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
/**
* 在線用戶
*/
String ONLINE_TOKEN = "online_tokens";
}使用
@Cacheable會先判斷有沒有緩存,如果有則不執(zhí)行方法體@CachePut無論如何都會先執(zhí)行方法體然后將緩存保存供其他地方使用
package com.example.redisson.controller;
import com.example.redisson.core.CacheNames;
import com.example.redisson.core.R;
import com.example.redisson.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
/**
* spring-cache 演示案例
*
* @author Lion Li
*/
// 類級別 緩存統(tǒng)一配置
//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/cache")
public class RedisCacheController {
/**
* 測試 @Cacheable
* <p>
* 表示這個方法有了緩存的功能,方法的返回值會被緩存下來
* 下一次調(diào)用該方法前,會去檢查是否緩存中已經(jīng)有值
* 如果有就直接返回,不調(diào)用方法
* 如果沒有,就調(diào)用方法,然后把結(jié)果緩存起來
* 這個注解「一般用在查詢方法上」
* <p>
* 重點說明: 緩存注解嚴(yán)謹(jǐn)與其他篩選數(shù)據(jù)功能一起使用
* 例如: 數(shù)據(jù)權(quán)限注解 會造成 緩存擊穿 與 數(shù)據(jù)不一致問題
* <p>
* cacheNames 命名規(guī)則 查看 {@link CacheNames} 注釋 支持多參數(shù)
*/
@Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
@GetMapping("/test1")
public R<String> test1(String key, String value) {
System.out.println("test1-->調(diào)用方法體");
return R.ok("操作成功", value);
}
/**
* 測試 @CachePut
* <p>
* 加了@CachePut注解的方法,會把方法的返回值put到緩存里面緩存起來,供其它地方使用
* 它「通常用在新增或者實時更新方法上」
* <p>
* cacheNames 命名規(guī)則 查看 {@link CacheNames} 注釋 支持多參數(shù)
*/
@CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
@GetMapping("/test2")
public R<String> test2(String key, String value) {
System.out.println("test2-->調(diào)用方法體");
return R.ok("操作成功", value);
}
/**
* 測試 @CacheEvict
* <p>
* 使用了CacheEvict注解的方法,會清空指定緩存
* 「一般用在刪除的方法上」
* <p>
* cacheNames 命名規(guī)則 查看 {@link CacheNames} 注釋 支持多參數(shù)
*/
@CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
@GetMapping("/test3")
public R<String> test3(String key, String value) {
return R.ok("操作成功", value);
}
/**
* 測試設(shè)置過期時間
* 手動設(shè)置過期時間10秒
* 11秒后獲取 判斷是否相等
*/
@GetMapping("/test6")
public R<Boolean> test6(String key, String value) {
RedisUtils.setCacheObject(key, value);
boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
System.out.println("***********" + flag);
try {
Thread.sleep(11 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object obj = RedisUtils.getCacheObject(key);
return R.ok(value.equals(obj));
}
}到此這篇關(guān)于SpringBoot 整合Redisson重寫cacheName支持多參數(shù)的文章就介紹到這了,更多相關(guān)SpringBoot 整合Redisson內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Security自定義登錄原理及實現(xiàn)詳解
這篇文章主要介紹了Spring Security自定義登錄原理及實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09
java實現(xiàn)區(qū)域內(nèi)屏幕截圖示例
這篇文章主要介紹了java截圖示例,需要的朋友可以參考下2014-04-04
java連接SQL?Server數(shù)據(jù)庫圖文教程(自用)
在Java應(yīng)用程序中,我們經(jīng)常需要與數(shù)據(jù)庫進(jìn)行交互,下面這篇文章主要給大家介紹了關(guān)于java連接SQL?Server數(shù)據(jù)庫的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06
Java多線程 ReentrantReadWriteLock原理及實例詳解
這篇文章主要介紹了Java多線程 ReentrantReadWriteLock原理及實例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-09-09
SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實現(xiàn)分布式登錄詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08
Lombok的詳細(xì)使用及優(yōu)缺點總結(jié)
最近在學(xué)Mybatis,接觸到了Lombok的使用,所以寫一篇文章記錄一下,包括lombok的安裝及使用優(yōu)缺點,感興趣的朋友跟隨小編一起看看吧2021-07-07

