Spring Cache简化缓存开发

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Spring Cache简化缓存开发

1 背景介绍

官方入门文档:https://spring.io/guides/gs/caching/


Spring 从3.1 开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发;Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache 接口下Spring 提供了各种xxxCache 的实现;如RedisCache , EhCacheCache , ConcurrentMapCache 等;

每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已 经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。


如果我们的程序想要使用缓存,就要与这些框架耦合。聪明的架构师已经在利用接口来降低耦合了,利用面向对象的抽象和多态的特性,做到业务代码与具体的框架分离。

但我们仍然需要显式地在代码中去调用与缓存有关的接口和方法,在合适的时候插入数据到缓存里,在合适的时候从缓存中读取数据。

想一想AOP的适用场景,这不就是天生就应该AOP去做的吗?

是的,Spring Cache就是一个这个框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以3秒钟就使用上一个很不错的缓存功能。

既然有这么好的轮子,干嘛不用呢?


2 使用方式

Spring cache提供了开箱即用的接入方式,只需要若干注解和缓存管理类即可接入.

1.开启缓存能力

引入缓存依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

在应用启动类添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.使用缓存

在业务方法添加@Cacheable注解:

@Cacheable(cacheNames = {"task"})
public TaskInfoDTO getTask(String taskId) {
    log.info("TestBuzz.getTask mock query from DB......");
    TaskInfoDTO taskInfoDTO = new TaskInfoDTO();
    taskInfoDTO.setTaskId(taskId);
    taskInfoDTO.setApplicantId("system");
    taskInfoDTO.setDescription("test");
    return taskInfoDTO;
}

模拟请求:

@GetMapping("/test_cache")
public IResp<TaskInfoDTO> testCache() {
    TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");
    return IResp.getSuccessResult(taskInfoDTO);
}

连续发送两次查询请求:

curl http://localhost:port/test_cache

从日志中看到只打印了一次DB调用,也就是说明第二次走了缓存。就这么简单我们就开启并使用了spring的缓存能力。

3 常用注解

原理:基于Proxy/AspectJ动态代理技术的AOP思想(面向切面编程)

使用:


SpringCache包含两个顶级接口,Cache(缓存)和CacheManager(缓存管理器),顾名思义,用CacheManager去管理一堆Cache。

spring cache实现有基于XML/注解实现AOP;

CacheManager负责对缓存的增删改查, CacheManager的缓存的介质可配置, 如:ConcurrentMap/EhCache/Redis等。当没有加入EhCache或者Redis依赖时默认采用concurrentMap实现的缓存,是存在内存中,重启服务器则清空缓存

pring Cache中的注解主要有如下五个:


@Cacheable:缓存数据或者获取缓存数据

@CachePut:修改缓存数据

@CacheEvict: 清空缓存

@CacheConfig:统一配置@Cacheable中的value值

@Caching:组合多个Cache注解

3.1 @Cacheable

先从value中获取为key的缓存,如果存在直接返回;如果不存在则执行方法并返回,且把返回输出存入缓存。(注意:保存的数据是return返回的数据)

主要有三个参数:


value:缓存的名称,可以多个 (必填,也可以用@CacheConfig替代)

 @Cacheable(value="testcache")
 @Cacheable(value={"testcache1","testcache2"}
  • key:缓存的 key,按照 SpEL 表达式编写,为空时默认为方法的入参(value相当于缓存空间的名称,而key相当于是一个缓存值的名字)
@Cacheable(value="testcache",key="#id")
  • condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false
@Cacheable(value="testcache",condition="#id.length()>2")

3.2 @CachePut

根据value中获取为key的缓存,如果存在则修改;不存在则新增

主要是三个参数

value,key,condition如上所示。

注意:保存的数据是return返回的数据,如下返回的有user对象和null,第一个缓存的数据是实体类,第二个缓存的数据是空

3.3 @CacheEvict

根据对应的value和key删除缓存,没有key值则删除value中所有的缓存

主要有五个参数value,key,condition,allEntries,beforeInvocation

  • allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
@CachEvict(value="testcache",allEntries=true)

图一、会清空getData下面所有缓存(allEntries=true则删除所有)

图二、只会清空getData下面key值为id的缓存(没有key默认取入参)

图三、不会清空任何缓存

  • beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为
    true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(注:作用只有一个,就是先清空缓存再执行方法
@CachEvict(value="testcache",beforeInvocation=true)

3.4 @CacheConfig

相当于把类下面所有方法@Cacheable中的value值放到@CacheConfig注解中,

如果@Cacheable中没有value值则用@Cacheable中的值;如果@Cacheable中有value值则以value值为准。

@CacheConfig("testcache")
public class UserServiceImpl implements  UserService{
    @Cacheable
    public Result findById(Long id) {
    }
  @Cacheable
    public Result findByIdAndName(Long id,String name) {
    }

3.5 @Caching

组合注解,可以组合多个注解

@Caching(put = {
  @CachePut(value = "user", key = "#user.id"),
  @CachePut(value = "user", key = "#user.username"),
  @CachePut(value = "user", key = "#user.email")
})
public User save(User user) {
}

4 实战:Spring Cache整合redis缓存

4.1 CacheManager 缓存管理器

public interface CacheManager {
  //根据缓存名字获取缓存
  @Nullable
  Cache getCache(String name);
  //获取管理的所有缓存的名字
  Collection<String> getCacheNames();
}

支持多种类型的缓存

这里我们使用redis作为缓存

<!--    引入redis    -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--   jedis不写版本springboot控制     -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>

4.2 配置spring-boot-starter-cache

1.缓存的自动配置

CacheProperties封装了配置文件中可以配置的属性

@ConfigurationProperties(prefix = "spring.cache")
org.springframework.boot.autoconfigure.cache.CacheProperties
CacheConfiguration`会根据缓存类型导入`RedisCacheConfiguration

RedisCacheConfiguration自动配好了缓存管理器

@Bean
  public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
      ResourceLoader resourceLoader) {
    RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
        .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
    List<String> cacheNames = this.cacheProperties.getCacheNames();
    if (!cacheNames.isEmpty()) {
      builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
    }
    return this.customizerInvoker.customize(builder.build());
  }

4.3 手动配置

配置使用Redis作为缓存

spring:
    cache:
      #cache-names: 可以自动配置
      type: redis

4.4 开启缓存功能 @Cacheable({“…”})

在启动类添加@EnableCaching 注解,不需要重复配Redis

@EnableCaching 注解

2.使用注解 就能完成缓存操作

//每一个需要缓存的数据都需要指定要放到哪个名字的缓存,缓存的分区,按照业务类型分 
 @Cacheable({"categroy"})    //代表方法的结果需要缓存 ,如果缓存中有 方法都不用调用,如果缓存中没有,调用方法,将结果放入缓存
 @Override
 public List<CategoryEntity> getLevel1Categorys() {
     long l = System.currentTimeMillis();
     List<CategoryEntity> categoryEntityList = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
     System.out.println("消耗时间:" + (System.currentTimeMillis() - l));
     return categoryEntityList;
 }

使用@Cacheable 默认行为

如果缓存中有缓存 方法都不用调用

key值 自动生成

缓存的Value值默认使用JDK序列化机制,将序列化后的数据存到Redis

默认过期时间TTL -1 永不过去

应该要指定缓存存活时间 在配置文件中修改

  cache:
    #cache-names: 可以自动配置
    type: redis
    redis:
      time-to-live: 3600000  #设置存活时间毫秒
      key-prefix: CACHE_    #key前缀 如果制定了前缀就用指定的前缀,如果没有就默认使用缓存的名字作为前缀
      use-key-prefix: true  # 是否使用前缀
      cache-null-values: true # 是否缓存控制 解决缓存穿透


相关实践学习
基于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
目录
相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
4天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
15 2
|
16天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
56 2
|
25天前
|
XML Java 数据格式
提升效率!Spring Boot 开发中的常见失误轻松规避
本文深入探讨了在 Spring Boot 开发中常见的失误,包括不当使用注解、不良异常处理、低效日志记录等,提供了有效的规避策略,帮助开发者提升代码质量和系统性能,构建更健壮、高效的应用程序。
|
9天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
22 0
|
1月前
|
开发框架 Java API
「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架
「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架
52 0
|
1月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
46 0
|
1月前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
79 0
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
6天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题