SpringBoot 整合缓存Cacheable实战详细使用
前言
我知道在接口api项目中,频繁的调用接口获取数据,查询数据库是非常耗费资源的,于是就有了缓存技术,可以把一些不常更新,或者经常使用的数据,缓存起来,然后下次再请求时候,就直接从缓存中获取,不需要再去查询数据,这样可以提供程序性能,增加用户体验,也节省服务资源浪费开销,
在springboot帮你我们做好了整合,有对应的场景启动器start,我们之间引入使用就好了,帮我们整合了各种缓存
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
</dependencies>
简介
缓存介绍
Spring 从 3.1 开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术。并支持使用 JCache(JSR-107)注解简化我们的开发。
其使用方法和原理都类似于 Spring 对事务管理的支持。Spring Cache 是作用在方法上的,其核心思想是,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存在缓存中。
Cache 和 CacheManager 接口说明
Cache 接口包含缓存的各种操作集合,你操作缓存就是通过这个接口来操作的。
Cache 接口下 Spring 提供了各种 xxxCache 的实现,比如:RedisCache、EhCache、ConcurrentMapCache
CacheManager 定义了创建、配置、获取、管理和控制多个唯一命名的 Cache。这些 Cache 存在于 CacheManager 的上下文中。
小结
每次调用需要缓存功能的方法时,Spring 会检查指定参数的指定目标方法是否已经被调用过,如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点;
- 确定方法需要被缓存以及他们的缓存策略
- 从缓存中读取之前缓存存储的数据
快速开始
- 使用缓存我们需要开启基于注解的缓存,使用
@EnableCaching标注在 springboot 主启动类上或者配置类上
@SpringBootApplication
@MapperScan(value = {"cn.soboys.kmall.mapper","cn.soboys.kmall.sys.mapper","cn.soboys.kmall.security.mapper"},nameGenerator = UniqueNameGenerator.class)
@ComponentScan(value = {"cn.soboys.kmall"},nameGenerator = UniqueNameGenerator.class)
@EnableCaching //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class WebApplication {
private static ApplicationContext applicationContext;
public static void main(String[] args) {
applicationContext =
SpringApplication.run(WebApplication.class, args);
//displayAllBeans();
}
/**
* 打印所以装载的bean
*/
public static void displayAllBeans() {
String[] allBeanNames = applicationContext.getBeanDefinitionNames();
for (String beanName : allBeanNames) {
System.out.println(beanName);
}
}
}
或者配置类
/**
* @author kenx
* @version 1.0
* @date 2021/8/17 15:05
* @webSite https://www.soboys.cn/
* 自定义缓存配置
*/
@Configuration
@Slf4j
@EnableCaching //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class CacheConfig {
//自定义配置类配置keyGenerator
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString() +"]";
}
};
}
}
- 标注缓存注解在需要缓存的地方使用
@Cacheable注解
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService<Menu> {
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
@Cacheable(key = "#username")
List<Menu> getUserMenus(String username);
}
@Cacheable注解有如下一些参数我们可以看到他源码
@Target({ElementType.TYPE, ElementType.METHOD})
@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;
}
下面介绍一下 @Cacheable 这个注解常用的几个属性:
cacheNames/value :指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定 多个缓存;
key :缓存数据时使用的 key,可以用它来指定。默认是使用方法参数的值。(这个 key 你可以使用 spEL 表达式来编写如
#i d;参数id的值 #a0 #p0 #root.args[0])keyGenerator :key的生成器;可以自己指定key的生成器的组件id 然后key 和 keyGenerator 二选一使用
cacheManager :可以用来指定缓存管理器。从哪个缓存管理器里面获取缓存。或者cacheResolver指定获取解析器
condition :可以用来指定符合条件的情况下才缓存
condition = "#id>0"
condition = "#a0>1":第一个参数的值》1的时候才进行缓存
- unless :否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。当然你也可以获取到结果进行判断。(通过 #result 获取方法结果)
unless = "#result == null"
unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
- sync :是否使用异步模式。异步模式的情况下
unless不支持 默认是方法执行完,以同步的方式将方法返回的结果存在缓存中
cacheNames/value属性
用来指定缓存组件的名字,将方法的返回结果放在哪个缓存中,可以是数组的方式,支持指定多个缓存
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
@Cacheable(cacheNames = "menuCache") 或者// @Cacheable(cacheNames = {"menuCache","neCacge"})
List<Menu> getUserMenus(String username);
如果只有一个属性,cacheNames可忽略,直接是value属性默认
key
缓存数据时使用的 key。默认使用的是方法参数的值。可以使用 spEL 表达式去编写。
Cache SpEL available metadata
| 名称 | 位置 | 描述 | 示例 |
|---|---|---|---|
| methodName | root对象 | 当前被调用的方法名 | #root.methodname |
| method | root对象 | 当前被调用的方法 | #root.method.name |
| target | root对象 | 当前被调用的目标对象实例 | #root.target |
| targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
| args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
| caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
| argumentName | 执行上下文(avaluation context) | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
| result | 执行上下文(evaluation context) | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
//key = "#username" 就是参数username
@Cacheable(key = "#username" ,cacheNames = "menuCache")
List<Menu> getUserMenus(String username);
keyGenerator
key 的生成器,可以自己指定 key 的生成器,通过这个生成器来生成 key
定义一个@Bean类,将KeyGenerator添加到Spring容器
@Configuration
@Slf4j
@EnableCaching //开启缓存注解驱动,否则后面使用的缓存都是无效的
public class CacheConfig {
//自定义配置类配置keyGenerator
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString() +"]";
}
};
}
}
在使用指定自己的@Cacheable(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
注意这样放入缓存中的 key 的生成规则就按照你自定义的 keyGenerator 来生成。不过需要注意的是:@Cacheable 的属性,key 和 keyGenerator 使用的时候,一般二选一。
condition
符合条件的情况下才缓存。方法返回的数据要不要缓存,可以做一个动态判断。
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
//判断username 用户名是kenx开头才会被缓存
@Cacheable(key = "#username" ,condition = "#username.startsWith('kenx')")
List<Menu> getUserMenus(String username);
unless
否定缓存。当 unless 指定的条件为 true ,方法的返回值就不会被缓存。
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
//判断username 用户名是kenx开头不会被缓存
@Cacheable(key = "#username" ,condition = "#username.startsWith('kenx')")
List<Menu> getUserMenus(String username);
spEL 编写 key

当然我们可以全局去配置,cacheNames,keyGenerator属性通过@CacheConfig注解可以用于抽取缓存的公共配置,然后在类加上就可以,eg:如
//全局配置,下面用到缓存方法,不配置默认使用全局的
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService<Menu> {
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
@Cacheabl
List<Menu> getUserMenus(String username);
}
深入使用
@CachePut
@CachePut注解也是一个用来缓存的注解,不过缓存和@Cacheable有明显的区别是即调用方法,又更新缓存数据,也就是执行方法操作之后再来同步更新缓存,所以这个主键常用于更新操作,也可以用于查询,主键属性和@Cacheable有很多类似的参看 @CachePut源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
@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 "";
}
/**
* @CachePut:既调用方法,又更新缓存数据;同步更新缓存
* 修改了数据,同时更新缓存
*/
@CachePut(value = {"emp"}, key = "#result.id")
public Employee updateEmp(Employee employee){
employeeMapper.updateEmp(employee);
LOG.info("更新{}号员工数据",employee.getId());
return employee;
}
@CacheEvict
清空缓存
主要属性:
- key:指定要清除的数据
- allEntries = true:指定清除这个缓存中所有的数据
- beforeInvocation = false:默认代表缓存清除操作是在方法执行之后执行
- beforeInvocation = true:代表清除缓存操作是在方法运行之前执行
@CacheEvict(value = {"emp"}, beforeInvocation = true,key="#id")
public void deleteEmp(Integer id){
employeeMapper.deleteEmpById(id);
//int i = 10/0;
}
@Caching
@Caching 用于定义复杂的缓存规则,可以集成@Cacheable和 @CachePut
// @Caching 定义复杂的缓存规则
@Caching(
cacheable = {
@Cacheable(/*value={"emp"},*/key = "#lastName")
},
put = {
@CachePut(/*value={"emp"},*/key = "#result.id"),
@CachePut(/*value={"emp"},*/key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName){
return employeeMapper.getEmpByLastName(lastName);
}
@CacheConfig
@CacheConfig注解可以用于抽取缓存的公共配置,然后在类加上就可以
//全局配置,下面用到缓存方法,不配置默认使用全局的
@CacheConfig(cacheNames = "menuCache",keyGenerator ="myKeyGenerator" )
public interface IMenuService extends IService<Menu> {
/**
* 获取用户菜单信息
*
* @param username 用户名
* @return
*/
@Cacheable(key = "#username" )
List<Menu> getUserMenus(String username);
}
参考
SpringBoot 整合缓存Cacheable实战详细使用的更多相关文章
- springBoot 整合 mybatis 项目实战
二.springBoot 整合 mybatis 项目实战 前言 上一篇文章开始了我们的springboot序篇,我们配置了mysql数据库,但是我们sql语句直接写在controller中并且使用 ...
- 😊SpringBoot 整合 Elasticsearch (超详细).md
SpringBoot 整合 Elasticsearch (超详细) 注意: 1.环境搭建 安装es Elasticsearch 6.4.3 下载链接 为了方便,环境使用Windows 配置 解压后配置 ...
- springboot整合dubbo+zookeeper最新详细
引入 最近和小伙伴做一个比赛,处于开发阶段,因为涉及的服务比较多,且服务需要分开部署在不同的服务器上,讨论之后,打算采用分布式来做,之前学习springboot的时候,部分章节涉及到了springbo ...
- SpringBoot Redis缓存 @Cacheable、@CacheEvict、@CachePut
文章来源 https://blog.csdn.net/u010588262/article/details/81003493 1. pom.xml <dependency> <gro ...
- springboot 整合缓存(Ehcache或者reids)
这里介绍Spring Boot结合JPA,MySQL和Ehcache实现缓存功能,提高程序访问效率. 一.Maven依赖 <!-- caching --> <dependency&g ...
- 二、springBoot 整合 mybatis 项目实战
前言 上一篇文章开始了我们的springboot序篇,我们配置了mysql数据库,但是我们sql语句直接写在controller中并且使用的是jdbcTemplate.项目中肯定不会这样使用,上篇文章 ...
- Springboot整合MybatisPlus(超详细)完整教程~
新建springboot项目 开发工具:idea2019.2,maven3 pom.xml <dependency> <groupId>org.springframework. ...
- Springboot整合https原来这么简单
1 简介 HTTP是不安全的,我们需要给它套上SSL,让它变成HTTPS.本文章将用实例介绍Springboot整合HTTPS. 2 密码学基础 要谈https就要谈Security,自然就要谈安全: ...
- Canal 实战 | 第一篇:SpringBoot 整合 Canal + RabbitMQ 实现监听 MySQL 数据库同步更新 Redis 缓存
一. Canal 简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同 ...
随机推荐
- 痞子衡嵌入式:对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是对比i.MXRT与LPC在RTC外设GPREG寄存器使用上的异同. 本篇是 <在SBL项目实战中妙用i.MXRT1xxx里Syst ...
- CentOS-常用命令(版本:7.x)
常用命令 注:centos命令不定期持续更新,希望能够帮到你~ 修改主机名 $ hostnamectl set-hostname xxx 查看IP $ vim /etc/sysconfig/netwo ...
- CentOS-配置JDK(压缩包)
卸载openjdk $ rpm -qa | grep jdk 以上命令用来检查linux上是否安装openjdk,如果安装需要将其全部卸载掉,卸载命令: $ rpm -e --nodeps java- ...
- buu [MRCTF2020]keyboard
密文: ooo yyy ii w uuu ee uuuu yyy uuuu y w uuu i i rr w i i rr rrr uuuu rrr uuuu t ii uuuu i w u rrr ...
- 一、从GitHub浏览Prism示例代码的方式入门WPF下的Prism
最近这段时间一直在看一个开源软件PowerToys的源码,里面使用Modules的开发风格让我特别着迷,感觉比我现在写代码的风格好了太多太多.我尝试把PowerToys的架构分离了出来,但是发现代码维 ...
- 如何修改Windows 11 任务栏大小
1.首先Win+R输入regedit打开注册表编辑器 2.进入注册表编辑器后,在地址栏中输入: HKEY_CURRENT_USER\Software\Microsoft\ Window ...
- DEV C++5.11编译没有结果提示
点击"视图"菜单--选择"浮动报告 窗口"
- LeetCode 887. Super Egg Drop
题目链接:https://leetcode.com/problems/super-egg-drop/ 题意:给你K个鸡蛋以及一栋N层楼的建筑,已知存在某一个楼层F(0<=F<=N),在不高 ...
- 接入 SDK 结果翻车了?了解 SDK 的那些事
前段时间,二狗子的朋友圈被工信部发布的<关于下架侵害用户权益 App 名单的通报>给刷屏了.公告中指出有 90 款 App 未按照要求完成整改将会下架.而这 90 款 App 涉及全国各地 ...
- [源码解析] 深度学习分布式训练框架 horovod (18) --- kubeflow tf-operator
[源码解析] 深度学习分布式训练框架 horovod (18) --- kubeflow tf-operator 目录 [源码解析] 深度学习分布式训练框架 horovod (18) --- kube ...