• SpringBoot 缓存模块


    默认的缓存配置

    在诸多的缓存自动配置类中, SpringBoot默认装配的是SimpleCacheConfigguration, 他使用的CacheManagerCurrentMapCacheManager, 使用 CurrentMap当底层的数据结构,按照Cache的名字查询出Cache, 每一个Cache中存在多个k-v键值对,缓存值

    几个主要的概念&常用缓存注解

    名称 解释
    Cache 缓存接口,主要实现由 RedisChache, EhCacheCachem , ConcurrentMapCache
    CacheManager 缓存管理器,管理存放着不同类型的缓存 Cache 组件
    @Cacheable 加在方法上,根据方法的请求参数对结果进行缓存
    @CacheEvict 清空缓存
    @CachePut 保证方法被调用,又希望对方法的结果进行缓存
    @EnableCaching 添加在启动类上,表示开始缓存
    @keyGenerator 缓存数据时key生成策略
    serialize 混存数据时,value的序列化策略

    @Cacheable

    上手使用

    第一步:
    开启基于注解的缓存 @EnableCaching

    第二步:
    使用缓存注解@Cacheable

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Cacheable {
        @AliasFor("cacheNames")
        String[] value() default {};  
    
        @AliasFor("value")
        String[] cacheNames() default {};
    
        String key() default "";
    
        String keyGenerator() default "";
    
        String cacheManager() default "";
    
        String cacheResolver() default "";
    
        String condition() default "";
    
        String unless() default "";
    
        boolean sync() default false;
    }
    
    • value 和 cacheNames 作用一样,都是在指定缓存的名字, 接收一个数组,可以指定多个缓存
    • key, 指定当前这条数据在缓存中的唯一标识,支持SPEL表达式,默认是方法的参数值

    最好都提前确定好使用哪个key

    • keyGenerator, 指定key的生成策略
    // 自定义key的生成器
    
    @Configuration
    public class MyCacheConfig {
        @Bean("myKeyGenerator")
        public KeyGenerator keyGenerator() {
            return new KeyGenerator() {
                @Override
                public Object generate(Object o, Method method, Object... objects) {
                    return method.getName() + "[" + Arrays.asList(objects).toString() + "]";
                }
            };
        }
    }
    
    // 使用
    @Cacheable(cacheNames = "dept",key = "#id",keyGenerator = "myKeyGenerator")
    

    一般key和keyGenerator二选一就行

    • cacheManager, 指定缓存管理器
    • cacheResolver, 指定缓存解析器
    • condition, 当条件为ture时, 进行缓存支持SPEL表达式
    当id>0时,缓存
    @Cacheable(cacheNames = "dept",key = "#id",condition = "#id>0")
    使用and添加更多的条件
    @Cacheable(cacheNames = "dept",key = "#id",condition = "#id>0 and #id<10")
    
    • unless, 当条件为true时, 不进行缓存支持SPEL表达式
    当结果为空时,不缓存
    @Cacheable(cacheNames = "dept",key = "#id",unless="#result == null")
    
    • sync, 异步缓存

    异步模式下,不支持 unless

    执行流程&时机

    @Cacheable标注的方法在执行之前,会先去检查缓存中有没有这个数据, 根据这种对应关系查询 key->value, key是使用keyGenerator生成的: 它的默认实现是SimpleKeyGenerate

    参数个数 key
    没有参数 new SimpleKey()
    有一个参数 参数值
    多个参数 new SimpleKey(多个参数)

    常用的SPEL表达式

    描述 示例
    当前被调用的方法名 #root.mathodName
    当前被调用的方法 #root.mathod
    当前被调用的目标对象 #root.target
    当前被调用的目标对象类 #root.targetClass
    当前被调用的方法的参数列表 #root.args[0] 第一个参数, #root.args[1] 第二个参数...
    根据参数名字取出值 #参数名, 也可以使用 #p0 #a0 0是参数的下标索引
    当前方法的返回值 #result

    @CachePut

    调用时机:

    目标方法执行完之后生效, @Cache被使用于修改操作比较多,哪怕缓存中已经存在目标值了,但是这个注解保证这个方法依然会执行,执行之后的结果被保存在缓存中

    常用更新操作,前端会把id+实体传递到后端使用,我们就直接指定方法的返回值从新存进缓存时的key="#id", 如果前端只是给了实体,我们就使用key="#实体.id" 获取key. 同时,他的执行时机是目标方法结束后执行, 所以也可以使用 key="#result.id", 拿出返回值的id

    @CacheEvict 缓存清除

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface CacheEvict {
        @AliasFor("cacheNames")
        String[] value() default {};
    
        @AliasFor("value")
        String[] cacheNames() default {};
    
        String key() default "";
    
        String keyGenerator() default "";
    
        String cacheManager() default "";
    
        String cacheResolver() default "";
    
        String condition() default "";
    
        boolean allEntries() default false;
    
        boolean beforeInvocation() default false;
    }
    
    • 同样,key的默认值就是参数的id的值,也可以手动指定
    • condition, 指定条件
    • value锁定Cache组件
    • allEntries 指定是否删除当前缓存组件中的全部值,默认是flase不全部删除
    • beforeInvocation, 缓存的清除,是否在方法之前执行, 默认是flase, 表示在方法执行之后执行

    如果是在方法执行之前就清空缓存了, 然后方法执行过程中出现异常被中断,缓存依然会被清除

    @CacheEvict(value="",key="")
    public void deleteById(Integer id){
        // todo
    }
    

    @Caching

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Caching {
        Cacheable[] cacheable() default {};
    
        CachePut[] put() default {};
    
        CacheEvict[] evict() default {};
    }
    

    这是个组合注解,适用于更复杂的情况, 添加在方法上,使用:

    @Caching(
            cacheable={@Cacheable(...),@Cacheable(...)  }
            put={@CachePut(...),@CachePut(...)  }
        )
    public void xxx(XXX){...}
    

    @CacheConfig

    使用场景, 比如,在一个部门的控制器中, 所有的缓存使用的value都是一样的,所有的方法又不能不写,于是使用@CacheConfig简化开发

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CacheConfig {
        String[] cacheNames() default {};
    
        String keyGenerator() default "";
    
        String cacheManager() default "";
    
        String cacheResolver() default "";
    }
    

    添加在类头上

    • cacheNames , 指定缓存使用的公共的名字, 使用在标注在类头上, 类中方法上的缓存相关的注解都可以不写value="XXX"

    整和其他混存中间件

    整合Redis当缓存中间件

    引入启动器

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

    当我们添加进redis相关的启动器之后, SpringBoot会使用RedisCacheConfigratioin当做生效的自动配置类进行缓存相关的自动装配,容器中使用的缓存管理器是
    RedisCacheManager, 这个缓存管理器创建的Cache为 RedisCache, 进而操控redis进行数据的缓存

    使用RedisTemplate,默认保存进去的数据 k-v 全是Obeject, 被保存的对象需要实现序列化接口, 虽然可以达到缓存的目的,但是对象被序列化成一堆看不懂的乱码, 需要我们自定义Redis 的 Template, 以及自定义CacheManager, 但是这样的话相对于它默认的配置就变的异常的麻烦;


    使用redisTemplate做缓存的常用方式

    查询:

    • 首先去redis中尝试获取,如果有redis中有值,直接返回redis中的结果 , 如果没有,去数据库中查询,把结果存入redis
    // todo 使用redis做缓存,减少和数据库的接触次数
    public Label findById(Long labelId) {
    
        // 先尝试从缓存中查询当前对象
        Label label = (Label) redisTemplate.opsForValue().get("label_id" + labelId);
    
        if (label==null){
            // 从数据库中查询 
    
            // 将查出的结果存进缓存中
            redisTemplate.opsForValue().set("label_id"+label.getId(),label);
        }
        return label;
    

    修改:

    先更新数据库, 然后删除redis中对应的缓存

    public void update(Long labelId, Label label) {
    label.setId(labelId);
    Label save = labelRepository.save(label);
    
    // todo 数据库修改成功后, 将缓存删除
    redisTemplate.delete("label_id"+save.getId());
    }
    

    删除:

    同样,先删除数据库中的数据, 再删除缓存

  • 相关阅读:
    .NET Core HttpClient调用腾讯云对象存储Web API的"ERROR_CGI_PARAM_NO_SUCH_OP"问题
    .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
    腾讯云短信服务使用记录与.NET Core C#代码分享
    .net core中使用Type.GetType()从字符串获取类型遇到的问题
    阿里云不同账号之间相同地域的VPC网络互访
    ASP.NET Core 实现用户登录验证的最低配置
    体验 ASP.NET Core 中的多语言支持(Localization)
    .NET Core 2.0 单元测试中初识 IOptionsMonitor<T>
    体验 PHP under .NET Core
    docker swarm:执行 service update 过程中服务短暂不能访问的问题
  • 原文地址:https://www.cnblogs.com/ZhuChangwu/p/11379521.html
Copyright © 2020-2023  润新知