首页 > 数据库 >Redis基于@Cacheable注解实现接口缓存

Redis基于@Cacheable注解实现接口缓存

时间:2023-03-31 17:48:02浏览次数:46  
标签:缓存 return Redis Cacheable template new 序列化 public

说明

@Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。

属性名称 属性描述 举例
value/cacheNames 指定缓存组件的名字 @Cacheable(value = "test")
key 缓存数据使用的 key,可以用它来指定。默认是使用方法参数的值 这个 key 你可以使用 spEL 表达式来编写,如: @Cacheable(value = "test", key = "#userId+'-'+#id")
keyGenerator key 的生成器,可以自己指定 key 的生成器的组件 id key 与 keyGenerator 二选一使用
cacheManager 指定缓存管理器;或者是 cacheResolver
condition 判断条件,指定符合条件的情况下才缓存 如 condition="#id!= null",就是在id不 为null的时候触发
unless 否定,unless 指定的条件为 true,方法的返回值就不会被缓存 如 unless="#id == null",就是在结果为null的 时候触发
sync 是否使用异步模式

引入Redis的Maven依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

在启动类上写上这个注解,不然缓存是不会得到支持的。


@SpringBootApplication
@EnableCaching
public class JyhInterfaceServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(JyhInterfaceServerApplication.class, args);
    }
}

使用

个人习惯,一般 @Cacheable 注解都使用在impl层的接口类上,这样其他任何Controller类调用次接口都会用到,岂不美哉。(根据自己业务需求判断)


    @Cacheable(key = "#id")
    public String get(Long id) {
        assert id != null;
        return "成功";
    }

实际业务中,往往我们的入参比较复杂,会遇到入参是实体类对象的情况,还有缓存的超时时间,这些都需要很灵活项目才方便使用。

方案一

批量设置接口超时时间、及生成key的序列化

创建Key的序列化方案


/**
 * Redis缓存时Key的序列化方案
 * 主要处理StringRedisSerializer是将Object强转为String
 * 这样当Object是Integer和Long进强转会报错
 */
public class CustomStringRedisSerializer implements RedisSerializer {

    @Override
    public byte[] serialize(Object o) throws SerializationException {
        if (o == null) {
            return null;
        }
        return String.valueOf(o).getBytes(Charset.forName("UTF-8"));
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null) {
            return null;
        }
        return new String(bytes, Charset.forName("UTF-8"));
    }
}

RedisConfig 配置中使用上面的序列化方案


@EnableCaching
@Configuration
public class RedisConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory))
                .cacheDefaults(this.redisCacheConfiguration(Duration.ofHours(1).getSeconds()))
                .withInitialCacheConfigurations(this.initialCacheConfigurations()).build();
    }

    /**
     * 这里设置过期时间,默认是1小时,@Cacheable(value = "jyhInterface") 匹配到的就是120秒
     */
    private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        redisCacheConfigurationMap.put("jyhInterface", this.redisCacheConfiguration(120));

        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    /**
     * 配置自定义redisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
        template.setConnectionFactory(connectionFactory);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(customStringRedisSerializer);
        template.setHashKeySerializer(customStringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer());
        template.setValueSerializer(jackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * json序列化
     */
    @Bean
    public RedisSerializer<Object> jackson2JsonRedisSerializer() {
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        serializer.setObjectMapper(mapper);
        return serializer;
    }


}

方案二

除了方案一批量设置超时时间之外,我们还可以在注解上自定义超时时间

添加扩展注解类


/**
 * @desc  扩展注解Cacheable 使用过期时间
 * @author  zhangl
 * @create  2023/3/30
 **/
public class CustomRedisCacheManager extends RedisCacheManager {

    public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }
    /**
     * 针对@Cacheable设置缓存过期时间
     * @param name
     * @param cacheConfig
     * @return
     */
    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        String[] array = StringUtils.delimitedListToStringArray(name, "#");
        name = array[0];
        // 解析TTL
        if (array.length > 1) {
            long ttl = Long.parseLong(array[1]);
            cacheConfig = cacheConfig.entryTtl(Duration.ofMinutes(ttl)); // 注意单位我此处用的是分钟
        }
        return super.createRedisCache(name, cacheConfig);
    }
}

修改RedisConfig

注意这里时间单位是用的分钟,方案一是用的秒

@EnableCaching
@Configuration
public class RedisConfig {

    /**
     * 实例化自定义的缓存管理器
     * @param redisTemplate
     * @return
     */
    @Bean
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        return new CustomRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

    private RedisCacheConfiguration redisCacheConfiguration(long seconds) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        return redisCacheConfiguration.entryTtl(Duration.ofSeconds(seconds))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    /**
     * 配置自定义redisTemplate
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        CustomStringRedisSerializer customStringRedisSerializer = new CustomStringRedisSerializer();
        template.setConnectionFactory(connectionFactory);
        //使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(customStringRedisSerializer);
        template.setHashKeySerializer(customStringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer());
        template.setValueSerializer(jackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * json序列化
     */
    @Bean
    public RedisSerializer<Object> jackson2JsonRedisSerializer() {
        //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        serializer.setObjectMapper(mapper);
        return serializer;
    }
}

使用

这个时候我们就可以在使用注解的时候默认带上超时时间就可以拉

    // 表示超时时间为5分钟
    @Cacheable(value = "jyhInterface#5")
    public String get(Long id) {
        assert id != null;
        return "成功";
    }

标签:缓存,return,Redis,Cacheable,template,new,序列化,public
From: https://www.cnblogs.com/zlnp/p/17276662.html

相关文章

  • SpringBoot 使用RedisTemplate
    1.导入Maven依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>2.配置连接信息spring:redis:host:127.0.0.1......
  • Redis数据库高可用
    一、Redis高可用在web服务器中,高可用是指服务器可以正常访问的时问,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%99.998等等)。但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢......
  • 一个机器开启多个redis
    一个机器开启多个redis复制redis.conf(可以先查一下redis.conf路径在哪里,find/-nameredis.conf)cpredis.confredis3.conf修改port、pidfile(命令行下用/port可以快速查询,按n查找下一个)port6381​pidfile/var/run/redis_6381.pid注意路径是否写对src/redis-s......
  • Redis和kafka比较,两者的区别和各自的优势。
    1.Redis,它首先是一个内存数据库,其提供的PUB/SUB功能把消息保存在内存中(基于channel),因此如果你的消息的持久性需求并不高且后端应用的消费能力超强的话,使用RedisPUB/SUB是比较合适的使用场景。比如官网说提供的一个网络聊天室的例子:模拟IRC,因为channel就是IRC中的服务器。用户......
  • RedisInsight的使用(Redis图形化工具)
    建议连接redis-cluster需求的,并且没有设置密码的,安装redisinsight-win-1.13.1.msi,但是官网说1.X版本在23/4/30后就不支持了。在浏览器中输入http://IP:8001就能看到主界面   勾选IhavereadandunderstoodtheRedisInsightLicenseTerms,r然后点CONFIRM   ......
  • NoSQL之Redis配置与优化
    一、数据库类型1.关系型数据库关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。SQL语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作。主流的关系型数据库包括Oracle、MySQL、SQLSe......
  • 缓存行与伪共享问题
    局部性原理时间局部性:如果数据正在被访问,那么在近期它很可能还会被再次访问。比如循环、方法的反复调用等。空间局部性:如果存储器的位置被引用,那么将来他附近的位置也会被引用。比如顺序结构、数组。CPU缓存执行程序是靠CPU执行主存中代码,但是CPU和主存的速度差异是非常大的,为......
  • 微信小程序开发:前端中的"Redis"
    wx.setStorageSync('key','value')wx.getStorageSync("key")   参考链接https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html ......
  • redis cluster 故障后,主从位于不同节点的修复【转】
    今天机房有一台物理机宕机了,有一个虚拟机192.168.1.122,其上有两个redis节点也不能用了。    redis没有备份,丢失的192.168.1.122节点只能重建。    我找运维的......
  • 缓存更新策略
      TRANSLATEwithxEnglishArabicHebrewPolishBulgarianHindiPortugueseCatalanHmongDawRomanianChineseSimplifiedHungarianRuss......