• SpringCache -- Redis --- 配置与缓存使用--配置过期时间


    写在前面:
    学redis,还是得搭配SpringCache来玩一玩。
    前置内容

    导入依赖

            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redisartifactId>
            dependency>
            
            <dependency>
                <groupId>org.apache.commonsgroupId>
                <artifactId>commons-pool2artifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-cacheartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    配置cache

    在yml下

    spring:
    	cache:
    		type: redis
    		redis:
    			key-prefix: Client_ #key前缀,如果配置了cacheManager则失效
         		use-key-prefix: true #是否开启key前缀
          		cache-null-values: true #是否缓存空值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在配置类上加上@EnableCaching开启缓存
    配置序列化
    RedisConfig implements CachingConfigurer
    redis的序列化器配置见前文,

        /**
         * 对缓存进行序列化和反序列化的配置
         * @param factory redis连接工厂
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory){
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofDays(1)) // 配置缓存时长
                    // 注意0为永久用于 Duration.ZERO 声明永久缓存。
                    .prefixCacheNameWith("Client:") // 前缀
                    .computePrefixWith(cacheName -> "caching:" + cacheName); //前缀动态
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
            return RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用

    在这里配置就差不多了,在需要进行缓存操作的地方加注解就可以了

    @Cacheable

    该注解为缓存获取注解:

    如果缓存中没有则进行查询在添加到缓存,如果缓存有则直接走缓存

    属性

    • value/cacheName
      他们互为别名
      确定目标高速缓存(或高速缓存),与特定 Bean 定义的限定符值或 Bean 名匹配
      最终缓存的key为prefix+cacheName:: key
    • key
      指定缓存的key,可以使用Spring Expression Language (SpEL) 表达式来进行动态设置。

    SpEL 表达式:

    1. 可以使用 #root.method, #root.target, and #root.caches 来指定 方法, 目标对象和受影响的缓存的method引用
      @cacheable(value =“phone”,key="#root.methodName”)
      指定方法名来作为
    2. 可以使用#形参名字来使用,下面是用phone的id来
      @Cacheable(value = “Phone”,key=“#phone.id”)
      public Result getAll(Phone phone)
    3. 方法参数可以通过索引访问。例如,可以通过 或 #p1 #a1访问#root.args[1]第二个参数。如果该信息可用,也可以按名称访问参数。
    • cacheManager
      指定管理器
    • condition
      缓存的条件,可以为空,使用SPEL表达式编写,返回true或者false,true表示存入缓存
    • KeyGenerator
      指定key生成对策
      如果是自定义对象类型,判断不了可以自定义KeyGenerator
    @Component
    public class MyGenerator implements KeyGenerator{
    	@override
    	public Object generate(object o, Method method, object... objects){
     		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • unless
      用于否决缓存放置操作的 Spring 表达式语言 (SpEL) 表达式。如果条件的计算结果为 true。
      默认值为 “”,表示缓存永远不会被否决。
    • cacheResolver
      要使用的自定义 org.springframework.cache.interceptor.CacheResolver 的 Bean 名称。

    @CachePut

    更新缓存
    每次都会调用方法,把返回值缓存起来,每次都会更新/新增
    注解和前面一样

    @CacheEvict

    删除缓存
    调用后删除指定缓存
    注解相同,多了几个

    • allEntries
      是否删除缓存中的所有条目。
    • beforeInvocation
      是否应在调用该方法之前进行逐出。
      将此属性设置为 true,会导致逐出发生,而不考虑方法结果(即,是否引发异常)。

    配置过期时间

    我是2种方法都配置了的

    依据cacheName设置

    主要在与对于每一个cacheName设置不同的RedisCacheConfiguration
    多个cacheName就加多个withCacheConfiguration就可以了

        /**
         * 对缓存进行序列化和反序列化的配置
         *
         * @param factory redis连接工厂
         * @return
         */
        @Bean
        public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) {
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .entryTtl(Duration.ofDays(1)) // 配置缓存时长
                    .prefixCacheNameWith("Client:") // 前缀
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) // key序列化方式
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); // value序列化方式
                    // 如果不需要第二种就把CustomRedisCacheManager换成RedisCacheManager
            return CustomRedisCacheManager.RedisCacheManagerBuilder
                    .fromConnectionFactory(factory)
                    .cacheDefaults(config)
                    .withCacheConfiguration("contact:relation", getCacheConfigurationWithTtl(redisTemplate, Duration.ofMinutes(30)))
                    .transactionAware()
                    .build();
        }
        RedisCacheConfiguration getCacheConfigurationWithTtl(RedisTemplate<String, Object> template, Duration time) {
            return RedisCacheConfiguration
                    .defaultCacheConfig()
                    .prefixCacheNameWith("Client:") // 前缀
                    // 设置key为String
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getStringSerializer()))
                    // 设置value 为自动转Json的Object
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template.getValueSerializer()))
                    // 不缓存null
                    .disableCachingNullValues()
                    // 缓存数据保存1小时
                    .entryTtl(time);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在注解上截取过期时间

    在cacheName上面加上#时间就可以了

    
    /**
     * 自定义缓存管理器
     */
    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, "#");
            // 解析TTL
            if (array.length > 2) {
                char c = array[1].charAt(array.length - 1);
                long ttl = Long.parseLong(StrUtil.sub(array[1], 0, array[1].length() - 1));
                cacheConfig = switch (c){
                    case 's','S' -> cacheConfig.entryTtl(Duration.ofSeconds(ttl));
                    case 'm','M' -> cacheConfig.entryTtl(Duration.ofMinutes(ttl));
                    case 'h','H' -> cacheConfig.entryTtl(Duration.ofHours(ttl));
                    case 'd','D' -> cacheConfig.entryTtl(Duration.ofDays(ttl));
                    default -> cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(array[1])));
                }
                cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl)); // 注意单位我此处用的是秒,而非毫秒
            }
            return super.createRedisCache(array[0], cacheConfig);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    如果redis宕机的解决方案。

    redis如果连接不上,springboot执行报错,从而导致正常业务造成错误。

    所以我们需要重写异常处理的方法
    重写继承CachingConfigurer的errorHandler方法,直接打印就可以了。

     /**
         * 配置当redis连接不上时被缓存注解标注的方法绕过Redis
         */
        @Bean
        @Override
        public CacheErrorHandler errorHandler() {
            return new CacheErrorHandler() {
                @Override
                public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                    log.error("redis异常:key=[{}]", key, exception);
                }
    
                @Override
                public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                    log.error("redis异常:key=[{}]", key, exception);
                }
    
                @Override
                public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                    log.error("redis异常:key=[{}]", key, exception);
                }
    
                @Override
                public void handleCacheClearError(RuntimeException exception, Cache cache) {
                    log.error("redis异常:", exception);
                }
            };
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    问题:很久才有响应结果。

    缩小redis的读取超时timeout时间。

    但是我发现需要2个timeout时间才会执行正常的逻辑,所以我选择700ms

      redis:
        database: 5
        host: localhost
        port: 6379
        timeout: 700ms
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    java 大厂面试指南:性能优化 + 微服务 + 并发编程 + 开源框架 + 分布式
    安卓面经_安卓基础面全解析<3/30>之广播全解析
    2022-09-18 mysql-subselect相关执行流程记录
    公司一般怎样筛选简历
    Dubbo-Activate实现原理
    day53--动态规划12
    计算机视觉+人工智能面试笔试总结——CNN模型总结
    【21天学习挑战赛—经典算法】希尔排序
    入门力扣自学笔记109 C++ (题目编号1161)
    如果手机被偷了,里面的微信和支付宝绑定了银行卡,该怎么办?
  • 原文地址:https://blog.csdn.net/qq_56717234/article/details/132984319