一、缓存有什么用?

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

二、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 库存文章了,版本是去年的。

 <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容器扫描到。这个注解不能缺,不然项目会报错。

@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 表达式(何时beforeInvocationfalse)中可用。对于支持的包装器(例如 Optional),#result指的是实际对象,而不是包装器。 #result

具体使用如下:

@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

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

@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

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

@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注释。实际上就是,各个缓存注解可以配合使用,操作不同的缓存空间。

 @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),这些配置可以直接配置到类上面,这样方法对应的就能少些一些重复代码。

//注解的四个属性
String[] cacheNames() default {}; String keyGenerator() default ""; String cacheManager() default ""; String cacheResolver() default "";

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

@CacheConfig(cacheNames = "chache")
public class CacheController {}

确实,我们的缓存还能从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都可以自己配置,具体的下一篇文章会介绍。

SpringBoot系列(十五)整合缓存,项目会用得到的技术的更多相关文章

  1. springboot系列十五、springboot集成PageHelper

    一.介绍 项目中经常会遇到分页,PageHelper为我们解决了这个问题.本质上实现了Mybatis的拦截器,作了分页处理. 二.配置PageHelper 1.引入依赖 pagehelper-spri ...

  2. SpringBoot进阶教程(二十五)整合Redis之@Cacheable、@CachePut、@CacheEvict的应用

    在上一篇文章(<SpringBoot(二十四)整合Redis>)中,已经实现了Spring Boot对Redis的整合,既然已经讲到Cache了,今天就介绍介绍缓存注解.各家互联网产品现在 ...

  3. SpringBoot系列(五)Mybatis整合完整详细版

    SpringBoot系列(五)Mybatis整合 目录 mybatis简介 项目创建 entity dao service serviceImpl mapper controller 1. Mybat ...

  4. SpringBoot系列(十二)过滤器配置详解

    SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...

  5. CRL快速开发框架系列教程五(使用缓存)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  6. 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

    学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...

  7. SpringBoot笔记十二:缓存

    目录 非缓存项目 缓存 JSR-107 Spring缓存抽象 @Cacheable @CachePut @CacheEvict @Caching @CacheConfig 整合Redis 先在Dock ...

  8. SpringBoot | 第二十五章:日志管理之自定义Appender

    前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...

  9. SpringBoot | 第十五章:基于Postman的RESTful接口测试

    前言 从上一章节开始,接下来的几个章节会讲解一些开发过程中配套工具的使用.俗话说的好,工欲善其事,必先利其器.对于开发人员而言,有个好用的工具,也是一件事半功倍的事,而且开发起来也很爽,效率也会提升很 ...

  10. 聊聊MySQL的加锁规则《死磕MySQL系列 十五》

    大家好,我是咔咔 不期速成,日拱一卒 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升MySQ ...

随机推荐

  1. JMeter四种参数化方式

    JMeter参数化是指把固定的数据动态化,这样更贴合实际的模拟用户请求,比如模拟多个不同账号.JMeter一共有四种参数化方式,分别是: CSV Data Set Config Function He ...

  2. 【转载】复制文件到已存在的Jar

    复制文件到已存在的Jar 技术标签: Ant  OSGI  Eclipse  脚本  配置管理   问题 这两天在写一个小东西.这个小东西是一个大东西的一部分.其实也就是其中的一两个类.而这个大东西需 ...

  3. Linux PATH环境变量及作用(初学者必读)

    Linux PATH环境变量及作用(初学者必读) < 什么是环境变量,Linux环境变量有哪些?Linux打包(归档)和压缩 > <Linux就该这么学>是一本基于最新Linu ...

  4. 064.Python开发虚拟环境

    在使用 Python 开发的过程中,工程一多,难免会碰到不同的工程依赖不同版本的库的问题:亦或者是在开发过程中不想让物理环境里充斥各种各样的库,引发未来的依赖灾难.此时,我们需要对于不同的工程使用不同 ...

  5. Java EnumMap 实现类

    EnumMap 实现类 因为 HashMap 是一种通过对 key 计算 hashCode(),通过空间换时间的方式,直接定位到 value 所在的内部数组的索引,因此,查找效率非常高. 如果作为 k ...

  6. Linux中级之lvs三个模式的图像补充(nat,dr,tun)

    负载均衡(Load Balance)集群提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的负载.带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. (1)单台计算机无法承受大规 ...

  7. HEX与ASCII之间装换

    static const char bcd_ascll[20]={0x30,0x31,0x32,0x33,0x34, 0x35,0x36,0x37,0x38,0x39,                 ...

  8. Linux 查看实时网卡流量的方法 网速 nload sar iftop dstat

    1.使用nload yum install -y gcc gcc-c++ ncurses-devel make wgetwget http://www.roland-riegel.de/nload/n ...

  9. SQLZOO

    一.SELECT basics/zh 以顯示德國 Germany 的人口. select population from world where name = 'Germany'; 查詢面積為 5,0 ...

  10. node.js学习(4)事件

    1 导入事件库