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 数据库增量日志解析,提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同 ...
随机推荐
- CSS经典布局——圣杯布局与双飞翼布局
一.圣杯布局和双飞翼布局的目的 实现三栏布局,中间一栏最先加载和渲染 两侧内容固定,中间内容随着宽度自适应 一般用于PC网 二.圣杯布局的实现 技术要点: 设置最小宽度min-width 使用floa ...
- docker安装和配置nginx
配置nginx docker配置nginx 本机ip是192.168.0.200 docker pull nginx 配置nginx主机 vi /root/docker/nginx/nginx01.c ...
- log4j配置相对路径实现日志记录
从网上简单搜索了一下,发现有三种介绍的方法.总结在这里1. 解决的办法自然是想办法用相对路径代替绝对路径,其实log4j的FileAppender本身就有这样的机制,如: log4j.appender ...
- c语言:2.3.3
#include <stdio.h> //赋值时类型原则:赋值号右边表达式值 变量 常量的类型最好与左边变量的类型相一致 //二者不相同时,C编译系统会自动实现数据类型转换 //转换原则: ...
- vs2013:asp.net网站发布
1."生成"菜单"生成网站" 2."发布网站" 3.配置文件--自定义,名称 4.发布方法:文件系统,确定目标位置(另外的) 5.配置选择r ...
- Grafana、Prometheus、mtail-日志监控
一:日志如何监控 在上一篇博客Grafana.Prometheus-监控平台中,简单了解了Grafana与Prometheus对项目做特定的监控打点,可视化的配置操作. 但是对于没有设置监控或者不容易 ...
- [iconfont_dart]帮你快速生成Icon,再也不用手动写Icon方法
iconfont_dart iconfont to dart.Icon can be implemented by calling iconfont classname. iconfont转dart. ...
- React构建组件的方式
一.是什么 组件就是把图形.非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式 在React中,一个类.一个函数都可以视为一个组件 在Vue系列中,我们了解到组件所存在的优势: 降低整个系 ...
- Python基础之用PyQt5创建menu
前一篇文章中,我们已经安装了PyQt5,并且已经测试过可用.那么接下来第一步开始学习如何创建菜单. 第一步:在想要运行py的地方右击External Tools-->designer,打开des ...
- flutter 解决无法安装或者安装依赖慢的问题
配置以下两个系统环境变量 右击计算机 --> 属性 --> 高级系统设置 --> 环境变量 PUB_HOSTED_URL : https://pub.flutter-io.cn FL ...