SpringBoot系列(十五)整合缓存,项目会用得到的技术
一、缓存有什么用?
缓存应该是我们每一个系统都应该考虑使用的,这样可以加速系统的访问,提升系统的性能。比如我们经常需要访问的高频数据,将此类数据放在缓存中,可以大幅度提升系统的响应速度。原因就是一般来说我们的数据都是存在数据库中,但是高频的访问数据库不仅会对数据库造成压力,一定程度上还会影响响应速度,影响用户体验。如果引入了缓存,不仅能提升访问性能的同时降低数据库的压力。
二、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 表达式(何时beforeInvocation是false)中可用。对于支持的包装器(例如 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系列(十五)整合缓存,项目会用得到的技术的更多相关文章
- springboot系列十五、springboot集成PageHelper
一.介绍 项目中经常会遇到分页,PageHelper为我们解决了这个问题.本质上实现了Mybatis的拦截器,作了分页处理. 二.配置PageHelper 1.引入依赖 pagehelper-spri ...
- SpringBoot进阶教程(二十五)整合Redis之@Cacheable、@CachePut、@CacheEvict的应用
在上一篇文章(<SpringBoot(二十四)整合Redis>)中,已经实现了Spring Boot对Redis的整合,既然已经讲到Cache了,今天就介绍介绍缓存注解.各家互联网产品现在 ...
- SpringBoot系列(五)Mybatis整合完整详细版
SpringBoot系列(五)Mybatis整合 目录 mybatis简介 项目创建 entity dao service serviceImpl mapper controller 1. Mybat ...
- SpringBoot系列(十二)过滤器配置详解
SpringBoot(十二)过滤器详解 往期精彩推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列(三)配置文件 ...
- CRL快速开发框架系列教程五(使用缓存)
本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...
- 学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)
学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP.NET Core Razor 编程系列二——添加一个实体 学习ASP.NET ...
- SpringBoot笔记十二:缓存
目录 非缓存项目 缓存 JSR-107 Spring缓存抽象 @Cacheable @CachePut @CacheEvict @Caching @CacheConfig 整合Redis 先在Dock ...
- SpringBoot | 第二十五章:日志管理之自定义Appender
前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...
- SpringBoot | 第十五章:基于Postman的RESTful接口测试
前言 从上一章节开始,接下来的几个章节会讲解一些开发过程中配套工具的使用.俗话说的好,工欲善其事,必先利其器.对于开发人员而言,有个好用的工具,也是一件事半功倍的事,而且开发起来也很爽,效率也会提升很 ...
- 聊聊MySQL的加锁规则《死磕MySQL系列 十五》
大家好,我是咔咔 不期速成,日拱一卒 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升MySQ ...
随机推荐
- JMeter四种参数化方式
JMeter参数化是指把固定的数据动态化,这样更贴合实际的模拟用户请求,比如模拟多个不同账号.JMeter一共有四种参数化方式,分别是: CSV Data Set Config Function He ...
- 【转载】复制文件到已存在的Jar
复制文件到已存在的Jar 技术标签: Ant OSGI Eclipse 脚本 配置管理 问题 这两天在写一个小东西.这个小东西是一个大东西的一部分.其实也就是其中的一两个类.而这个大东西需 ...
- Linux PATH环境变量及作用(初学者必读)
Linux PATH环境变量及作用(初学者必读) < 什么是环境变量,Linux环境变量有哪些?Linux打包(归档)和压缩 > <Linux就该这么学>是一本基于最新Linu ...
- 064.Python开发虚拟环境
在使用 Python 开发的过程中,工程一多,难免会碰到不同的工程依赖不同版本的库的问题:亦或者是在开发过程中不想让物理环境里充斥各种各样的库,引发未来的依赖灾难.此时,我们需要对于不同的工程使用不同 ...
- Java EnumMap 实现类
EnumMap 实现类 因为 HashMap 是一种通过对 key 计算 hashCode(),通过空间换时间的方式,直接定位到 value 所在的内部数组的索引,因此,查找效率非常高. 如果作为 k ...
- Linux中级之lvs三个模式的图像补充(nat,dr,tun)
负载均衡(Load Balance)集群提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的负载.带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. (1)单台计算机无法承受大规 ...
- HEX与ASCII之间装换
static const char bcd_ascll[20]={0x30,0x31,0x32,0x33,0x34, 0x35,0x36,0x37,0x38,0x39, ...
- Linux 查看实时网卡流量的方法 网速 nload sar iftop dstat
1.使用nload yum install -y gcc gcc-c++ ncurses-devel make wgetwget http://www.roland-riegel.de/nload/n ...
- SQLZOO
一.SELECT basics/zh 以顯示德國 Germany 的人口. select population from world where name = 'Germany'; 查詢面積為 5,0 ...
- node.js学习(4)事件
1 导入事件库