SpringBoot实现缓存组件配置动态切换的步骤详解
作者:Mr Tang
现在有多个springboot项目,但是不同的项目中使用的缓存组件是不一样的,有的项目使用redis,有的项目使用ctgcache,现在需要用同一套代码通过配置开关,在不同的项目中切换这两种缓存,本文介绍了SpringBoot实现缓存组件配置动态切换的步骤,需要的朋友可以参考下
一、需求背景
现在有多个springboot项目,但是不同的项目中使用的缓存组件是不一样的,有的项目使用redis,有的项目使用ctgcache,现在需要用同一套代码通过配置开关,在不同的项目中切换这两种缓存。
二、实现缓存组件的动态切换
1.第一步:配置文件新增切换开关
#缓存组件配置 #cache.type=ctgcache cache.type=redis
2.第二步:创建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 中的某个值匹配(这里简单比较字符串) // 注意:实际应用中可能需要更复杂的逻辑来确定 metadata 中的值 // 这里我们假设 metadata 中没有特定值,仅根据 cache.type 判断 if ("ctgcache".equalsIgnoreCase(cacheType)) { // 使用 ctgcache return true; } // 如果没有明确指定,或者指定了其他值,我们可以选择默认行为 // 这里假设默认不使用这个 Bean return false; } }
3.第三步:创建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 中的某个值匹配(这里简单比较字符串) // 注意:实际应用中可能需要更复杂的逻辑来确定 metadata 中的值 // 这里我们假设 metadata 中没有特定值,仅根据 cache.type 判断 if ("redis".equalsIgnoreCase(cacheType)) { // 使用 Redis return true; } // 如果没有明确指定,或者指定了其他值,我们可以选择默认行为 // 这里假设默认不使用这个 Bean return false; } }
4.第四步:创建缓存切换配置类
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) { // 创建并返回RedisTemplate RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(redisSerializer); redisTemplate.setHashKeySerializer(redisSerializer); // 设置value的序列化器 //使用Jackson 2,将对象序列化为JSON Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //json转对象类,不设置默认的会将json转成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) { // 创建并返回RedisMessageListenerContainer RedisMessageListenerContainer container = new RedisMessageListenerContainer(); // 监听所有库的key过期事件 container.setConnectionFactory(connectionFactory); return container; } @Bean @Conditional(CtgCacheCondition.class) public CtgJedisPool ctgJedisPool() { // 创建并返回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); // 最大连接数(空闲+使用中) poolConfig.setMaxIdle(maxIdle); //最大空闲连接数 poolConfig.setMinIdle(minIdle); //保持的最小空闲连接数 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.第五步:创建缓存服务接口
package com.gstanzer.supervise.cache; /** * 缓存服务接口 * * @author: tangbingbing * @date: 2024/7/22 14:46 */ public interface CacheService { /** * 检查缓存中是否存在某个key * * @param key * @return */ public boolean exists(final String key); /** * 获取缓存中对应key的value值 * * @param key * @return */ public String get(final String key); /** * 存入值到缓存,并设置有效期 * * @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); /** * 删除缓存对应的key值 * * @param key * @return */ public boolean del(final String key); }
6.第六步:创建ctgcache实现类实现缓存服务接口
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 缓存方法实现 * * @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; /** * 判断缓存中是否有对应的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内执行,确保连接归还 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内执行,确保连接归还 try { jedis.close(); } catch (Throwable ignored) { } } return value; } /** * 写入缓存设置时效时间 * * @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内执行,确保连接归还 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内执行,确保连接归还 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内执行,确保连接归还 try { jedis.close(); } catch (Throwable ignored) { } } return result; } }
7.第七步:创建redis实现类实现缓存服务接口
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 缓存方法实现 * * @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); } /** * 获取缓存中对应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; } /** * 存入值到缓存,并设置有效期 * * @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; } /** * 删除缓存对应的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.第八步:在程序中注入缓存服务接口使用
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,重新设置值有效期为60s"; } return res; } }
三、总结
其实整体实现是一个比较简单的过程,核心是需要了解Springboot中@Conditional注解的应用,希望对大家有所帮助。
到此这篇关于SpringBoot实现缓存组件配置动态切换的步骤详解的文章就介绍到这了,更多相关SpringBoot缓存组件动态切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!