前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot(十四)缓存还是要用起来

SpringBoot(十四)缓存还是要用起来

作者头像
全栈学习笔记
发布2022-03-31 11:49:26
4990
发布2022-03-31 11:49:26
举报
文章被收录于专栏:全栈学习笔记全栈学习笔记

一、缓存有什么用?

缓存应该是我们每一个系统都应该考虑使用的,这样可以加速系统的访问,提升系统的性能。比如我们经常需要访问的高频数据,将此类数据放在缓存中,可以大幅度提升系统的响应速度。原因就是一般来说我们的数据都是存在数据库中,但是高频的访问数据库不仅会对数据库造成压力,一定程度上还会影响响应速度,影响用户体验。如果引入了缓存,不仅能提升访问性能的同时降低数据库的压力。

二、JSR107规范

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

规范:

JCache规范定义了一种对Java对象临时在内存中进行缓存的方法,包括对象的创建、共享访问、假脱机(spooling)、失效、各JVM的一致性等,可被用于缓存JSP内最经常读取的数据,如产品目录和价格列表。利用JCACHE,多数查询的反应时间会因为有缓存的数据而加快(内部测试表明反应时间大约快15倍)。

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。其中CacheProvider就是缓存提供器,其中包含了多个缓存管理器(CacheManager),缓存管理器之下才是我们需要用到的缓存(Cache),缓存的形式就是键值对,即entry对象(Entry),每一个entry对象会对应一个Expiry,即可以对每一条缓存数据设置有效期。

上面都是抄过来的废话,看下面,实际操作一下。

三、Spring缓存抽象

Spring 3.1开始定义了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发。SpringBoot对缓存整合提供了很好的支持,支持不同的缓存规范(所谓的规范就是相当于面向接口编程,不同的缓存有不同的实现)。

其中Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;下面我们来看看,SpringBoot中默认的一些缓存配置与实现。默认的是SimpleCache

四、SpringBoot中的缓存-注解开发

新建SpringBoot web项目,加入依赖配置:SpringBoot版本为2.2.6.RELEASE 库存文章了,版本是去年的。

代码语言:javascript
复制
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
 </dependency>

然后我们逐个介绍一下缓存开发中用到的一些注解。

  • @EnableCaching
  • @Cacheable
  • @CachePut
  • @CacheEvict
  • @Caching
  • @CacheConfig

1.@EnableCaching

这个注解应该是最先了解的,把它放在启动类上面,表示开启基于注解的缓存,然后你用那些缓存相关的注解就能被Spring容器扫描到。这个注解不能缺,不然项目会报错。

代码语言:javascript
复制
 @EnableCaching
 @SpringBootApplication
 public class DemoCacheApplication {
 
     public static void main(String[] args) {
           SpringApplication.run(DemoCacheApplication.class, args);
     }
 }

2.@Cacheable

这个注解有很多的属性,可以定义一些缓存的规则。主要是用于方法之上,将方法的结果作为缓存的值,默认情况下将方法的参数作为缓存的键(key),也就是key-value的形式存储数据。首先介绍一下这个注解的运行机制:首先在方法运行之前,先根据注解配置的value或者是cacheNames到cacheManage中去查询缓存组件即Cache,当然如果第一次没有这个缓存就会自动创建。获取到缓存组件之后就用我们设置的key属性,或者是keyGenerator策略生成的key去查询键对应的缓存值,对应上面的entry对象。如果查到了值就返回给请求客户端,如果没有查到这个值就调用方法,然后将方法返回的结果缓存起来。

这里我们来说一说这个注解里面的一些属性的用法:

  • value/cacheNames:这两个属性是一样的,都是指定一个缓存组件的名字,当然这个可以指定多个,是一个数组。
  • key/keyGenerator:这两个有一定的区别,但是用的时候只需要用其中一个就行了。key是我们可以直接指定这个缓存结果对应的key是什么,到时候直接去Cache里面查就行了,keyGenerator就是可以自定义一个key的生成策略,然后生成key,两者从结果上来看都是生成了key,所以两者只需要用其中一个就OK了。
  • condition:在符合这个条件下才进行缓存
  • unless:当这个条件为假的时候才进行缓存,与condition相反

当然还有cacheManager(缓存管理器)以及cacheResolver(缓存解析器),这两个都可以自己定义。两个都差不多,二者用其中一个就OK。

下面我们来看看SpEl写法,因为我们的key或者是condition,或者是unless属性都会用到这个写法。

来自官方的spel 语法

Name

Location

Description

Example

methodName

Root object

被调用方法的名称

#root.methodName

method

Root object

被调用的方法

#root.method.name

target

Root object

被调用的目标对象

#root.target

targetClass

Root object

被调用目标的类

#root.targetClass

args

Root object

用于调用目标的参数(作为数组)

#root.args[0]

caches

Root object

运行当前方法的缓存集合

#root.caches[0].name

Argument name

Evaluation context

任何方法参数的名称。如果名称不可用(可能是由于没有调试信息),则参数名称也可在代表参数索引的#a<#arg> where下#arg(从 开始0)。

#iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias).

result

Evaluation context

方法调用的结果(要缓存的值)。仅在unless 表达式、cache put表达式(用于计算key)或cache evict 表达式(何时beforeInvocation是false)中可用。对于支持的包装器(例如 Optional),#result指的是实际对象,而不是包装器。

#result

具体使用如下:

代码语言:javascript
复制
 @GetMapping("/cacheAble")
 @Cacheable(value = "cache",key = "#root.args[0]",condition = "#id>20")
 public  Student cacheAble(@RequestParam Integer id) {
     Student student = (Student) hashMap.get(id);
     System.out.println("第一次调用了方法,添加缓存");
     return student;
 }

上面就相当于是将第一个方法参数id作为缓存的key ,返回值Student 作为值。缓存的名称就是cache,设置在参数id大于20的时候才进行缓存,unless 则是相反的。

3.@CachePut

这个注解是用来更新缓存的,一般情况下都是用在更新数据的接口上,示例如下:

代码语言:javascript
复制
 @PostMapping("/putCache")
 @CachePut(value = "cache",key = "#result.id")
 public Student putCache(@RequestBody Student student) {
     hashMap.replace(student.getId(),student);
     System.out.println("调用了方法,也更新了缓存");
     return student;
 }

4.@CacheEvict

删除缓存的注解,可以设置删除全部缓存还是删除部分缓存数据。如下

代码语言:javascript
复制
 @CacheEvict(value = "cache",key = "#id",beforeInvocation = true,allEntries = false)
 @GetMapping("/cacheEvict")
 public void cacheEvict(@RequestParam Integer id) {
     hashMap.remove(id);
     System.out.println("缓存删除");
 }

beforeInvocation 设置为true ,表示在方法执行之前进行缓存删除,默认为false,`allEntries设置为true ,表示删除这个缓存名称下面的所有缓存。

5.@Caching

借助官方的话说,有时,需要指定多个相同类型的注解(例如@CacheEvict@CachePut)——例如,因为不同缓存之间的条件或键表达式不同。@Caching允许在同一方法上使用多个嵌套的 @Cacheable@CachePut@CacheEvict注释。实际上就是,各个缓存注解可以配合使用,操作不同的缓存空间。

代码语言:javascript
复制
  @Caching(
      cacheable = {
          @Cacheable(value = "cache",key = "#id"),
          @Cacheable(value = "cache1",key = "#id")
      },
      evict = {@CacheEvict(value = "cache2",key = "#id")}
  )
 @GetMapping("/caching")
 public Student caching(@RequestParam Integer id) {
     Student student = (Student)hashMap.get(id);
     System.out.println("新增缓存了,也删除了缓存,但是操作的cache不一样");
     return student;
 }

代码实现的就是将数据缓存到cache,cache1,再将cache2的缓存删除掉。

6.@CacheConfig

这是一个类级别的缓存配置注解,像缓存名称,缓存处理器(cacheResolver),缓存管理器(cacheManager),这些配置可以直接配置到类上面,这样方法对应的就能少些一些重复代码。

代码语言:javascript
复制
 //注解的四个属性
 String[] cacheNames() default {};
 
 String keyGenerator() default "";
 
 String cacheManager() default "";
  
 String cacheResolver() default "";

示例: 在类上面,加上注解,配置好对应的缓存名称,key生成策略等,keyGenerator,cacheManager,cacheResolver这三个东西留着下篇文章将redis缓存的时候讲,主要是比较多。

代码语言:javascript
复制
 @CacheConfig(cacheNames = "chache")
 public class CacheController {}

确实,我们的缓存还能从cacheManager中获取,这很好理解,cacheManager是管理缓存的,当然也能获取缓存了。

代码语言:javascript
复制
@Autowired
private CacheManager cacheManager;

@GetMapping("/get") public Student getCache(@RequestParam Integer id){
     Cache cache = cacheManager.getCache("cache");
     Cache.ValueWrapper student = cache.get(id);
     Object o = student.get();
     return (Student) o;
}

由于SpringBoot的良好扩展性,CacheManager,CacheResolver,KeyGenerator都可以自己配置,具体的下一篇文章会介绍。

end

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈学习笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、缓存有什么用?
  • 二、JSR107规范
  • 三、Spring缓存抽象
  • 四、SpringBoot中的缓存-注解开发
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档