SpringBoot 缓存模块
默认的缓存配置
在诸多的缓存自动配置类中, SpringBoot默认装配的是SimpleCacheConfigguration
, 他使用的CacheManager
是 CurrentMapCacheManager
, 使用 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());
}
删除:
同样,先删除数据库中的数据, 再删除缓存
SpringBoot 缓存模块的更多相关文章
- springboot多模块开发以及整合dubbo\zookeeper进行服务管理
之前研究了springboot单工程的使用,参考git地址:https://github.com/qiao-zhi/springboot-ssm 下面研究springboot多模块开发的过程. 1.模 ...
- 使用spring EL表达式+自定义切面封装缓存模块
需求是这样的,业务代码需要使用到缓存功能以减少数据库压力,使用redis来实现,并且需要生成缓存的key由方法的传参拼接而成(貌似也只能这样才能保证同样的select查询可以使用缓存),简单的方式就是 ...
- 【Java EE 学习 78 上】【数据采集系统第十天】【Service使用Spring缓存模块】
一.需求分析 调查问卷中或许每一个单击动作都会引发大量的数据库访问,特别是在参与调查的过程中,只是单击“上一页”或者“下一页”的按钮就会引发大量的查询,必须对这种问题进行优化才行.使用缓存策略进行查询 ...
- .NET 缓存模块设计
上一篇谈了我对缓存的概念,框架上的理解和看法,这篇承接上篇讲讲我自己的缓存模块设计实践. 基本的缓存模块设计 最基础的缓存模块一定有一个统一的CacheHelper,如下: public interf ...
- IOS编程 图片缓存模块设计
手机客户端为什么会留存下来?而不是被一味的Wap替代掉?因为手机客户端有Wap无可替代的优势,就是自身较强的计算能力. 手机中不可避免的一环:图片缓存,在软件的整个运行过程中显得尤为重要. 先简单说一 ...
- SpringBoot缓存之redis--最简单的使用方式
第一步:配置redis 这里使用的是yml类型的配置文件 mybatis: mapper-locations: classpath:mapping/*.xml spring: datasource: ...
- SpringBoot多模块项目打包问题
项目结构图如下: 在SpringBoot多模块项目打包时遇见如下错误: 1.repackage failed: Unable to find main class -> [Help 1] 解决步 ...
- spring boot学习(十三)SpringBoot缓存(EhCache 2.x 篇)
SpringBoot 缓存(EhCache 2.x 篇) SpringBoot 缓存 在 Spring Boot中,通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManag ...
- 一步步实现一个基本的缓存模块·续, 添加Memcached调用实现
jusfr 原创,转载请注明来自博客园. 在之前的实现中,我们初步实现了一个缓存模块:包含一个基于Http请求的缓存实现,一个基于HttpRuntime.Cache进程级的缓存实现,但观察代码,会发现 ...
随机推荐
- 如何在vue中使用echart
1.安装echarts依赖 npm install echarts --save 2.在main.js中全局中引用 import echarts from 'echarts' Vue.protot ...
- druid一步到位
版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons) 在配置application.yml文件的时候,原本写的是MySQL的连 ...
- R035---偷个懒,用UiPath录制电脑操作过程,迅速实现流程自动化
一.缘起 UiPath可以录制你操作电脑的过程,从而实现自动化. 目前有点鸡肋,因为有些操作过程无法录制,例如: 键盘快捷键 修改键 右键点击 鼠标悬停 即便如此,录制功能有时候还是可以用一下,特别 ...
- Python入门基础(9)__面向对象编程_1
定义一个只包含方法的类 class 类名: def 方法1(self,参数列表): pass def 方法2(self,参数列表): pass 当一个类定义之后,要使用这个类来创键对象.语法如下: 对 ...
- 开源一个好用的nodejs访问mysql类库
一.背景问题 自nodejs诞生以来出现了一大批的web框架如express koa2 egg等等,前端可以不再依赖后端可以自己控制服务端的逻辑.原来的后端开发同学的阵地前端如今同样也写的风生水起,撸 ...
- 个人永久性免费-Excel催化剂功能第28波-工作薄瘦身,安全地减少非必要冗余
Excel催化剂在完善了数据分析场景的插件需求后,决定再补充一些日常绝大多数Excel用户同样可以使用到的小功能,欢迎小白入场,在不违背太多Excel最佳实践的前提下,Excel催化剂乐意为广大Exc ...
- SpringMVC面试题:什么是Servlet?
一.什么是servlet? servlet是一个Java编写的程序,此程序是基于http协议的,在服务器端(如Tomcat)运行的,是按照servlet规范编写的一个Java类.客户端发送请求至服务器 ...
- C#3.0新增功能05 分部方法
连载目录 [已更新最新开发文章,点击查看详细] 分部类或结构可以包含分部方法. 类的一个部分包含方法的签名. 可以在同一部分或另一个部分中定义可选实现. 如果未提供该实现,则会在编译时删除 ...
- [leetcode] 87. Scramble String (Hard)
题意: 判断两个字符串是否互为Scramble字符串,而互为Scramble字符串的定义: 字符串看作是父节点,从字符串某一处切开,生成的两个子串分别是父串的左右子树,再对切开生成的两个子串继续切开, ...
- 第一章jQuery基础
一.jQuert简介 1.什么是jQuery jQuery是javaScript的程序库之一,它是javaScript对象和实用函数的封装. jQuery是继Prototype之后又一个优秀的java ...