SpringBoot實現(xiàn)緩存組件配置動態(tài)切換的步驟詳解
一、需求背景
現(xiàn)在有多個springboot項目,但是不同的項目中使用的緩存組件是不一樣的,有的項目使用redis,有的項目使用ctgcache,現(xiàn)在需要用同一套代碼通過配置開關(guān),在不同的項目中切換這兩種緩存。
二、實現(xiàn)緩存組件的動態(tài)切換
1.第一步:配置文件新增切換開關(guān)
#緩存組件配置 #cache.type=ctgcache cache.type=redis
2.第二步:創(chuàng)建ctgcache 緩存條件類
package com.gstanzer.supervise.cache;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* ctgcache 緩存條件類
*
* @author: tangbingbing
* @date: 2024/7/22 14:31
*/
public class CtgCacheCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 從 Environment 中獲取屬性
Environment env = context.getEnvironment();
String cacheType = env.getProperty("cache.type");
// 檢查 cache.type 是否與 metadata 中的某個值匹配(這里簡單比較字符串)
// 注意:實際應(yīng)用中可能需要更復(fù)雜的邏輯來確定 metadata 中的值
// 這里我們假設(shè) metadata 中沒有特定值,僅根據(jù) cache.type 判斷
if ("ctgcache".equalsIgnoreCase(cacheType)) {
// 使用 ctgcache
return true;
}
// 如果沒有明確指定,或者指定了其他值,我們可以選擇默認(rèn)行為
// 這里假設(shè)默認(rèn)不使用這個 Bean
return false;
}
}3.第三步:創(chuàng)建redis 緩存條件類
package com.gstanzer.supervise.cache;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* redis 緩存條件類
*
* @author: tangbingbing
* @date: 2024/7/22 14:31
*/
public class RedisCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 從 Environment 中獲取屬性
Environment env = context.getEnvironment();
String cacheType = env.getProperty("cache.type");
// 檢查 cache.type 是否與 metadata 中的某個值匹配(這里簡單比較字符串)
// 注意:實際應(yīng)用中可能需要更復(fù)雜的邏輯來確定 metadata 中的值
// 這里我們假設(shè) metadata 中沒有特定值,僅根據(jù) cache.type 判斷
if ("redis".equalsIgnoreCase(cacheType)) {
// 使用 Redis
return true;
}
// 如果沒有明確指定,或者指定了其他值,我們可以選擇默認(rèn)行為
// 這里假設(shè)默認(rèn)不使用這個 Bean
return false;
}
}4.第四步:創(chuàng)建緩存切換配置類
package com.gstanzer.supervise.cache;
import com.ctg.itrdc.cache.pool.CtgJedisPool;
import com.ctg.itrdc.cache.pool.CtgJedisPoolConfig;
import com.ctg.itrdc.cache.vjedis.jedis.JedisPoolConfig;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
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.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.HostAndPort;
import java.util.ArrayList;
import java.util.List;
/**
* 緩存配置類
*
* @author: tangbingbing
* @date: 2024/7/22 14:28
*/
@Configuration
public class CacheConfig {
@Value("${access.cq.redis.host1}")
private String reidsHost1;
@Value("${access.cq.redis.host2}")
private String reidsHost2;
@Value("${access.cq.redis.port}")
private int port;
@Value("${access.cq.redis.password}")
private String password;
@Value("${access.cq.redis.group}")
private String group;
@Value("${access.cq.redis.max-total}")
private int maxTotal;
@Value("${access.cq.redis.max-idle}")
private int maxIdle;
@Value("${access.cq.redis.min-idle}")
private int minIdle;
@Value("${access.cq.redis.max-wait}")
private int maxWait;
@Value("${access.cq.redis.period}")
private int period;
@Value("${access.cq.redis.monitor-timeout}")
private int monitorTimeout;
@Bean
@Conditional(RedisCondition.class)
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 創(chuàng)建并返回RedisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
// 設(shè)置value的序列化器
//使用Jackson 2,將對象序列化為JSON
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//json轉(zhuǎn)對象類,不設(shè)置默認(rèn)的會將json轉(zhuǎn)成hashmap
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// json中會顯示類型
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
@Conditional(RedisCondition.class)
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
// 創(chuàng)建并返回RedisMessageListenerContainer
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
// 監(jiān)聽所有庫的key過期事件
container.setConnectionFactory(connectionFactory);
return container;
}
@Bean
@Conditional(CtgCacheCondition.class)
public CtgJedisPool ctgJedisPool() {
// 創(chuàng)建并返回CtgJedisPool
List<HostAndPort> hostAndPortList = new ArrayList();
HostAndPort host1 = new HostAndPort(reidsHost1, port);
HostAndPort host2 = new HostAndPort(reidsHost2, port);
hostAndPortList.add(host1);
hostAndPortList.add(host2);
GenericObjectPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(maxTotal); // 最大連接數(shù)(空閑+使用中)
poolConfig.setMaxIdle(maxIdle); //最大空閑連接數(shù)
poolConfig.setMinIdle(minIdle); //保持的最小空閑連接數(shù)
poolConfig.setMaxWaitMillis(maxWait); //借出連接時最大的等待時間
CtgJedisPoolConfig config = new CtgJedisPoolConfig(hostAndPortList);
config.setDatabase(group)
.setPassword(password)
.setPoolConfig(poolConfig)
.setPeriod(period)
.setMonitorTimeout(monitorTimeout);
CtgJedisPool pool = new CtgJedisPool(config);
return pool;
}
}5.第五步:創(chuàng)建緩存服務(wù)接口
package com.gstanzer.supervise.cache;
/**
* 緩存服務(wù)接口
*
* @author: tangbingbing
* @date: 2024/7/22 14:46
*/
public interface CacheService {
/**
* 檢查緩存中是否存在某個key
*
* @param key
* @return
*/
public boolean exists(final String key);
/**
* 獲取緩存中對應(yīng)key的value值
*
* @param key
* @return
*/
public String get(final String key);
/**
* 存入值到緩存,并設(shè)置有效期
*
* @param key
* @param value
* @param expireTime 有效期,單位s
* @return
*/
public boolean set(final String key, String value, int expireTime);
/**
* 存入值到緩存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, String value);
/**
* 刪除緩存對應(yīng)的key值
*
* @param key
* @return
*/
public boolean del(final String key);
}6.第六步:創(chuàng)建ctgcache實現(xiàn)類實現(xiàn)緩存服務(wù)接口
package com.gstanzer.supervise.cache;
import com.ctg.itrdc.cache.pool.CtgJedisPool;
import com.ctg.itrdc.cache.pool.ProxyJedis;
import com.gstanzer.supervise.ctgcache.CtgRedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* ctgcache 緩存方法實現(xiàn)
*
* @author: tangbingbing
* @date: 2024/7/22 14:48
*/
@Service
@Conditional(CtgCacheCondition.class)
public class CtgCacheService implements CacheService {
private static Logger logger = LoggerFactory.getLogger(CtgRedisUtil.class);
@Resource
private CtgJedisPool ctgJedisPool;
/**
* 判斷緩存中是否有對應(yīng)的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
Boolean exists = false;
ProxyJedis jedis = new ProxyJedis();
try {
jedis = ctgJedisPool.getResource();
exists = jedis.exists(key);
jedis.close();
} catch (Throwable e) {
logger.error(e.getMessage());
jedis.close();
} finally {
// finally內(nèi)執(zhí)行,確保連接歸還
try {
jedis.close();
} catch (Throwable ignored) {
}
}
return exists;
}
/**
* 讀取緩存
*
* @param key
* @return
*/
public String get(final String key) {
String value = null;
ProxyJedis jedis = new ProxyJedis();
try {
jedis = ctgJedisPool.getResource();
value = jedis.get(key);
jedis.close();
} catch (Throwable e) {
logger.error(e.getMessage());
jedis.close();
} finally {
// finally內(nèi)執(zhí)行,確保連接歸還
try {
jedis.close();
} catch (Throwable ignored) {
}
}
return value;
}
/**
* 寫入緩存設(shè)置時效時間
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, String value, int expireTime) {
Boolean result = false;
ProxyJedis jedis = new ProxyJedis();
try {
jedis = ctgJedisPool.getResource();
jedis.setex(key, expireTime, value);
result = true;
jedis.close();
} catch (Throwable e) {
logger.error(e.getMessage());
jedis.close();
} finally {
// finally內(nèi)執(zhí)行,確保連接歸還
try {
jedis.close();
} catch (Throwable ignored) {
}
}
return result;
}
/**
* 寫入緩存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, String value) {
Boolean result = false;
ProxyJedis jedis = new ProxyJedis();
try {
jedis = ctgJedisPool.getResource();
jedis.set(key, value);
result = true;
jedis.close();
} catch (Throwable e) {
logger.error(e.getMessage());
jedis.close();
} finally {
// finally內(nèi)執(zhí)行,確保連接歸還
try {
jedis.close();
} catch (Throwable ignored) {
}
}
return result;
}
/**
* 刪除緩存
*
* @param key
* @return
*/
public boolean del(final String key) {
Boolean result = false;
ProxyJedis jedis = new ProxyJedis();
try {
jedis = ctgJedisPool.getResource();
jedis.del(key);
result = true;
jedis.close();
} catch (Throwable e) {
logger.error(e.getMessage());
jedis.close();
} finally {
// finally內(nèi)執(zhí)行,確保連接歸還
try {
jedis.close();
} catch (Throwable ignored) {
}
}
return result;
}
}7.第七步:創(chuàng)建redis實現(xiàn)類實現(xiàn)緩存服務(wù)接口
package com.gstanzer.supervise.cache;
import com.gstanzer.supervise.redis.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
/**
* reids 緩存方法實現(xiàn)
*
* @author: tangbingbing
* @date: 2024/7/22 14:48
*/
@Service
@Conditional(RedisCondition.class)
public class RedisCacheService implements CacheService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);
/**
* 檢查緩存中是否存在某個key
*
* @param key
* @return
*/
@Override
public boolean exists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 獲取緩存中對應(yīng)key的value值
*
* @param key
* @return
*/
@Override
public String get(String key) {
String result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key).toString();
return result;
}
/**
* 存入值到緩存,并設(shè)置有效期
*
* @param key
* @param value
* @param expireTime 有效期,單位s
* @return
*/
@Override
public boolean set(String key, String value, int expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 存入值到緩存
*
* @param key
* @param value
* @return
*/
@Override
public boolean set(String key, String value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 刪除緩存對應(yīng)的key值
*
* @param key
* @return
*/
@Override
public boolean del(String key) {
Boolean result = false;
try {
if (exists(key)) {
redisTemplate.delete(key);
}
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}8.第八步:在程序中注入緩存服務(wù)接口使用
package com.gstanzer.supervise.controller;
import com.gstanzer.supervise.cache.CacheService;
import com.gstanzer.supervise.jwt.PassToken;
import com.gstanzer.supervise.swagger.ApiForBackEndInIAM;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;
/**
* 緩存測試 Controller
*
* @author: tangbingbing
* @date: 2023/10/23 16:25
*/
@Api(tags = "緩存測試")
@Slf4j
@Validated
@RestController
@RequestMapping(value = "/redis")
public class RedisController {
@Resource
private CacheService cacheService;
@PassToken
@ApiForBackEndInIAM
@ApiOperation(value = "redis測試")
@PostMapping("/test")
public String test(
@RequestParam() @ApiParam(value = "redis鍵") @NotEmpty(message = "{validator.RedisController.test.key.NotEmpty}") String key
) {
String res = "獲取到redis-value為:空";
if (cacheService.exists(key)){
String value = cacheService.get(key);
res = "獲取到redis-value為:" + value;
} else {
cacheService.set(key,"test",60);
res = "未獲取到value,重新設(shè)置值有效期為60s";
}
return res;
}
}三、總結(jié)
其實整體實現(xiàn)是一個比較簡單的過程,核心是需要了解Springboot中@Conditional注解的應(yīng)用,希望對大家有所幫助。
到此這篇關(guān)于SpringBoot實現(xiàn)緩存組件配置動態(tài)切換的步驟詳解的文章就介紹到這了,更多相關(guān)SpringBoot緩存組件動態(tài)切換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot /tmp 臨時目錄的具體實現(xiàn)
springboot應(yīng)用服務(wù)再啟動的時候,會在操作系統(tǒng)的/tmp目錄,本文主要介紹了springboot /tmp 臨時目錄的具體實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2024-06-06
擴(kuò)展tk.mybatis的流式查詢功能實現(xiàn)
mybatis查詢默認(rèn)是一次獲取全部,如果數(shù)據(jù)過于龐大,就會導(dǎo)致OOM問題,本文就介紹了tk.mybatis 流式查詢,具有一定的參考價值,感興趣的可以了解一下2021-12-12
Java注解之超越Javadoc的元數(shù)據(jù)利器詳解
本文將深入探討 Java 注解的定義、類型、內(nèi)置注解、自定義注解、保留策略、實際應(yīng)用場景及最佳實踐,無論是初學(xué)者還是資深開發(fā)者,都能通過本文了解如何利用注解提升代碼質(zhì)量和開發(fā)效率2025-05-05
sentinel?整合spring?cloud限流的過程解析
這篇文章主要介紹了sentinel?整合spring?cloud限流,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
Java中double數(shù)值保留兩位小數(shù)的4種實現(xiàn)方式舉例
在Java編程中,我們經(jīng)常遇到需要對double類型的浮點數(shù)進(jìn)行精確截斷或四舍五入保留兩位小數(shù)的需求,這篇文章主要給大家介紹了關(guān)于Java中double數(shù)值保留兩位小數(shù)的4種實現(xiàn)方式,需要的朋友可以參考下2024-07-07
SpringBoot集成itextpdf實現(xiàn)根據(jù)模板動態(tài)生成PDF
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何集成itextpdf實現(xiàn)根據(jù)模板動態(tài)生成PDF,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2024-03-03

