SpringBoot 整合Redisson重寫(xiě)cacheName支持多參數(shù)的案例代碼
依賴
<!--常用工具類(lèi) --> <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ù)庫(kù)索引 database: 0 # 密碼(如沒(méi)有密碼請(qǐng)注釋掉) # password: ****** # 連接超時(shí)時(shí)間 timeout: 10s # 是否開(kāi)啟ssl ssl: enabled: false redisson: # redis key前綴 keyPrefix: # 線程池?cái)?shù)量 threads: 4 # Netty線程池?cái)?shù)量 nettyThreads: 8 # 單節(jié)點(diǎn)配置 singleServerConfig: # 客戶端名稱(chēng) clientName: ${ruoyi.name} # 最小空閑連接數(shù) connectionMinimumIdleSize: 8 # 連接池大小 connectionPoolSize: 32 # 連接空閑超時(shí),單位:毫秒 idleConnectionTimeout: 10000 # 命令等待超時(shí),單位:毫秒 timeout: 3000 # 發(fā)布和訂閱連接池大小 subscriptionConnectionPoolSize: 50
Properties
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; /** * 線程池?cái)?shù)量,默認(rèn)值 = 當(dāng)前處理核數(shù)量 * 2 */ private int threads; /** * Netty線程池?cái)?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 { /** * 客戶端名稱(chēng) */ private String clientName; /** * 最小空閑連接數(shù) */ private int connectionMinimumIdleSize; /** * 連接池大小 */ private int connectionPoolSize; /** * 連接空閑超時(shí),單位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超時(shí),單位:毫秒 */ private int timeout; /** * 發(fā)布和訂閱連接池大小 */ private int subscriptionConnectionPoolSize; } @Data @NoArgsConstructor public static class ClusterServersConfig { /** * 客戶端名稱(chēng) */ private String clientName; /** * master最小空閑連接數(shù) */ private int masterConnectionMinimumIdleSize; /** * master連接池大小 */ private int masterConnectionPoolSize; /** * slave最小空閑連接數(shù) */ private int slaveConnectionMinimumIdleSize; /** * slave連接池大小 */ private int slaveConnectionPoolSize; /** * 連接空閑超時(shí),單位:毫秒 */ private int idleConnectionTimeout; /** * 命令等待超時(shí),單位:毫秒 */ 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); // 指定序列化輸入的類(lèi)型,類(lèi)必須是非final修飾的。序列化時(shí)將對(duì)象全類(lèi)名一起保存下來(lái) 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 腳本實(shí)現(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ī)與集群只能開(kāi)啟一個(gè)另一個(gè)需要注釋掉) * spring.data: * redis: * cluster: * nodes: * - 192.168.0.100:6379 * - 192.168.0.101:6379 * - 192.168.0.102:6379 * # 密碼 * password: * # 連接超時(shí)時(shí)間 * timeout: 10s * # 是否開(kāi)啟ssl * ssl.enabled: false * * redisson: * # 線程池?cái)?shù)量 * threads: 16 * # Netty線程池?cái)?shù)量 * nettyThreads: 32 * # 集群配置 * clusterServersConfig: * # 客戶端名稱(chēng) * clientName: ${ruoyi.name} * # master最小空閑連接數(shù) * masterConnectionMinimumIdleSize: 32 * # master連接池大小 * masterConnectionPoolSize: 64 * # slave最小空閑連接數(shù) * slaveConnectionMinimumIdleSize: 32 * # slave連接池大小 * slaveConnectionPoolSize: 64 * # 連接空閑超時(shí),單位:毫秒 * idleConnectionTimeout: 10000 * # 命令等待超時(shí),單位:毫秒 * timeout: 3000 * # 發(fā)布和訂閱連接池大小 * subscriptionConnectionPoolSize: 50 * # 讀取模式 * readMode: "SLAVE" * # 訂閱模式 * subscriptionMode: "MASTER" */ }
重寫(xiě)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 源碼 * 重寫(xiě) 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) { // 重寫(xiě) 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 過(guò)期時(shí)間 如果設(shè)置為0則不過(guò)期 默認(rèn)為0
maxIdleTime 最大空閑時(shí)間 根據(jù)LRU算法清理空閑數(shù)據(jù) 如果設(shè)置為0則不檢測(cè) 默認(rèn)為0
maxSize 組最大長(zhǎng)度 根據(jù)LRU算法清理溢出數(shù)據(jù) 如果設(shè)置為0則無(wú)限長(zhǎng) 默認(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"; /** * 用戶名稱(chēng) */ String SYS_NICKNAME = "sys_nickname#30d"; /** * 部門(mén) */ 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
會(huì)先判斷有沒(méi)有緩存,如果有則不執(zhí)行方法體@CachePut
無(wú)論如何都會(huì)先執(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 */ // 類(lèi)級(jí)別 緩存統(tǒng)一配置 //@CacheConfig(cacheNames = CacheNames.DEMO_CACHE) @RequiredArgsConstructor @RestController @RequestMapping("/demo/cache") public class RedisCacheController { /** * 測(cè)試 @Cacheable * <p> * 表示這個(gè)方法有了緩存的功能,方法的返回值會(huì)被緩存下來(lái) * 下一次調(diào)用該方法前,會(huì)去檢查是否緩存中已經(jīng)有值 * 如果有就直接返回,不調(diào)用方法 * 如果沒(méi)有,就調(diào)用方法,然后把結(jié)果緩存起來(lái) * 這個(gè)注解「一般用在查詢方法上」 * <p> * 重點(diǎn)說(shuō)明: 緩存注解嚴(yán)謹(jǐn)與其他篩選數(shù)據(jù)功能一起使用 * 例如: 數(shù)據(jù)權(quán)限注解 會(huì)造成 緩存擊穿 與 數(shù)據(jù)不一致問(wèn)題 * <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); } /** * 測(cè)試 @CachePut * <p> * 加了@CachePut注解的方法,會(huì)把方法的返回值put到緩存里面緩存起來(lái),供其它地方使用 * 它「通常用在新增或者實(shí)時(shí)更新方法上」 * <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); } /** * 測(cè)試 @CacheEvict * <p> * 使用了CacheEvict注解的方法,會(huì)清空指定緩存 * 「一般用在刪除的方法上」 * <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); } /** * 測(cè)試設(shè)置過(guò)期時(shí)間 * 手動(dòng)設(shè)置過(guò)期時(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重寫(xiě)cacheName支持多參數(shù)的文章就介紹到這了,更多相關(guān)SpringBoot 整合Redisson內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Security自定義登錄原理及實(shí)現(xiàn)詳解
這篇文章主要介紹了Spring Security自定義登錄原理及實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09java實(shí)現(xiàn)區(qū)域內(nèi)屏幕截圖示例
這篇文章主要介紹了java截圖示例,需要的朋友可以參考下2014-04-04java連接SQL?Server數(shù)據(jù)庫(kù)圖文教程(自用)
在Java應(yīng)用程序中,我們經(jīng)常需要與數(shù)據(jù)庫(kù)進(jìn)行交互,下面這篇文章主要給大家介紹了關(guān)于java連接SQL?Server數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解
這篇文章主要介紹了Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情
這篇文章主要介紹了SpringBoot整合SpringSession實(shí)現(xiàn)分布式登錄詳情,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08Lombok的詳細(xì)使用及優(yōu)缺點(diǎn)總結(jié)
最近在學(xué)Mybatis,接觸到了Lombok的使用,所以寫(xiě)一篇文章記錄一下,包括lombok的安裝及使用優(yōu)缺點(diǎn),感興趣的朋友跟隨小編一起看看吧2021-07-07