SpringBoot 缓存模块

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 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());
}


删除:#


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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
缓存 NoSQL Java
SpringBoot实现缓存预热的几种常用方案
SpringBoot实现缓存预热的几种常用方案
|
6月前
|
缓存 Java
java开发常用模块——缓存模块
java开发常用模块——缓存模块
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
5月前
|
缓存 NoSQL Java
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
在 Spring Boot 应用中使用 Spring Cache 和 Redis 实现数据查询的缓存功能
278 0
|
4月前
|
缓存 Python
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
在Python中,`functools`模块提供了一个非常有用的装饰器`lru_cache()`,它实现了最近最少使用(Least Recently Used, LRU)缓存策略。
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
3月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
|
5月前
|
缓存 Java 数据库
springboot数据库及缓存常用依赖及配置
springboot数据库及缓存常用依赖及配置
|
5月前
|
缓存 NoSQL Java
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
案例 采用Springboot默认的缓存方案Simple在三层架构中完成一个手机验证码生成校验的程序
109 5
|
5月前
|
存储 缓存 NoSQL
SpringBoot配置第三方专业缓存技术Redis
SpringBoot配置第三方专业缓存技术Redis
46 4