SpringBoot实现缓存组件配置动态切换的步骤详解

 更新时间:2024年07月26日 11:51:28   作者: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缓存组件动态切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java反射拼接方法名动态执行方法实例

    java反射拼接方法名动态执行方法实例

    下面小编就为大家带来一篇java反射拼接方法名动态执行方法实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • Java加解密工具类源码示例

    Java加解密工具类源码示例

    最近在项目中接触到了数据加解密的业务,数据加密技术是网络中最基本的安全技术,这篇文章主要给大家介绍了关于Java加解密工具类源码的相关资料,需要的朋友可以参考下
    2023-11-11
  • Java如何Mock FileInputStream问题

    Java如何Mock FileInputStream问题

    这篇文章主要介绍了Java如何Mock FileInputStream问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • 解读线程池-Executors的newSingleThreadExecutor和newFixedThreadPool(1)区别

    解读线程池-Executors的newSingleThreadExecutor和newFixedThreadPool(1

    这篇文章主要介绍了解读线程池-Executors的newSingleThreadExecutor和newFixedThreadPool(1)区别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • 浅谈Java代理(jdk静态代理、动态代理和cglib动态代理)

    浅谈Java代理(jdk静态代理、动态代理和cglib动态代理)

    下面小编就为大家带来一篇浅谈Java代理(jdk静态代理、动态代理和cglib动态代理)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • 深入剖析理解AsyncGetCallTrace源码底层原理

    深入剖析理解AsyncGetCallTrace源码底层原理

    这篇文章主要为大家介绍了AsyncGetCallTrace源码的深层原理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • springbean的加载过程及应用场景分析

    springbean的加载过程及应用场景分析

    Bean已经被实例化、属性注入、初始化,并且注册到容器中,可以被其他Bean或应用程序使用,这篇文章主要介绍了springbean的加载过程以及应用场景,需要的朋友可以参考下
    2024-04-04
  • Java 反射(Reflect)详解

    Java 反射(Reflect)详解

    这篇文章主要介绍了JAVA 反射机制的相关知识,文中讲解的非常细致,代码帮助大家更好的理解学习,感兴趣的朋友可以了解下
    2021-09-09
  • 关于Hadoop中Spark Streaming的基本概念

    关于Hadoop中Spark Streaming的基本概念

    这篇文章主要介绍了关于Hadoop中Spark Streaming的基本概念,Spark Streaming是构建在Spark上的实时计算框架,它扩展了Spark处理大规模流式数据的能力,Spark Streaming可结合批处理和交互式查询,需要的朋友可以参考下
    2023-07-07
  • 详解spring cloud hystrix 请求合并collapsing

    详解spring cloud hystrix 请求合并collapsing

    这篇文章主要介绍了详解spring cloud hystrix 请求合并collapsing,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05

最新评论