SpringBoot+Caffeine+Redis声明式缓存
目录
【博客目的】
【应用场景】
【相关知识】
【代码实践】
引入组件
配置文件
配置类
启动类
业务层
实体类
接口层
【博客目的】
记录一个项目中同时整合了Caffeine和Redis时,怎么使用@Cacheable这样的注解,优雅地实现缓存。
【应用场景】
最近接到一个项目,里面同时整合了Caffeine和Redis。
对于像验证码,或者对用户操作做一些限制的缓存,还有分布式锁等等操作就利用redis来缓存,
对于一些热点数据,为了降低数据库查询频率,就使用Caffeine本地缓存来实现。
至于为什么这么做?这个问题问得好!下次别问了!
【相关知识】
对相关原理和注解@Cacheable/@CachePut/@CacheEvit不熟练的同学请移步相关文章,能很好地理解。
@Cacheable注解详解:SpringBoot 缓存之 @Cacheable 详细介绍_zl1zl2zl3的博客-CSDN博客_@cacheable一、简介1、缓存介绍Spring 从 3.1 开始就引入了对 Cache 的支持。定义了 org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口来统一不同的缓存技术。并支持使用 JCache(JSR-107)注解简化我们的开发。其使用方法和原理都类似于 Spring 对事务管理的支持。Spring Cache 是作用在方法上的,其核心思想是,当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个https://blog.csdn.net/zl1zl2zl3/article/details/110987968
组合拳:
Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍 - 简书SpringBoot系列缓存注解@Cacheable @CacheEvit @CachePut使用姿势介绍 Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用...https://www.jianshu.com/p/29c1916e0df3
【代码实践】
废话不多讲,直接开撸。
引入组件
先创建一个springboot项目,再pom文件中导入以下依赖:
<!-- Redis缓存 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.6.1</version></dependency><!-- 咖啡因缓存 --><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.1</version></dependency>
配置文件
接下来就是针对两个缓存组件的配置
在application.yml中,需要对redis的连接信息做一些基础配置,Caffeine不用。
spring:redis:# Redis服务器地址host: 127.0.0.1# Redis数据库索引(默认为0)database: 1# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)password: pasjedis:pool:# 连接池最大连接数(使用负值表示没有限制)max-active: 8# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 8# 连接池中的最小空闲连接min-idle: 0# 连接超时时间(毫秒)timeout: 3000ms
配置类
CacheConfig 配置类:
package com.xxx.xxx.cache;import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;@Configuration
public class CacheConfig {@Bean("caffeineCacheManager")@Primarypublic CacheManager cacheManager(){SimpleCacheManager simpleCacheManager = new SimpleCacheManager();ArrayList<Cache> caches = new ArrayList<>();caches.add(new CaffeineCache("test1",Caffeine.newBuilder().expireAfterWrite(100, TimeUnit.SECONDS).recordStats().maximumSize(Integer.MAX_VALUE).removalListener((key, value, cause) -> {System.out.println("");}).build()));caches.add(new CaffeineCache("test2",Caffeine.newBuilder().expireAfterWrite(100, TimeUnit.SECONDS).recordStats().maximumSize(Integer.MAX_VALUE).removalListener((key, value, cause) -> {System.out.println("");}).build()));simpleCacheManager.setCaches(caches);return simpleCacheManager;}@Bean("redisCacheManager")public CacheManager redisCacheManager(RedisConnectionFactory factory){RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()// 设置缓存的默认过期时间.entryTtl(Duration.ofSeconds(180)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()))// 不缓存空值.disableCachingNullValues();return RedisCacheManager.builder(factory).cacheDefaults(config).transactionAware().build();}
}
之所以要配置两个Manager的原因简单说一下。
我们在使用@Cacheable注解时,在Caffeine中,Spring底层是通过@Cacheable的cacheManager属性的值去找对应的CacheManger中名为value属性值的缓存容器实例;
而在Redist中又不一样,整个Redis就是一个缓存容器,所以是通过CacheManager的属性值去调用对应的Redis缓存容器实例,而此时的value属性值和key属性的值,一起组成了redis的key。
PS:还在研究,理解如有错误,欢迎指正!
启动类
启动类上添加注解@EnableCaching开启自动缓存支持。
package com.xxx.xxx;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;@SpringBootApplication
@EnableCaching //开启自动缓存
@EnableAsync //开启异步支持
@EnableTransactionManagement // 开启事务支持
public class ProjectApplication {public static void main(String[] args) {SpringApplication.run(ProjectApplication.class, args);}
}
基本上就算是配置结束了,下面可以直接使用了。
业务层
CacheTestService 接口:
package com.xxx.xxx.service.base;import com.xxx.xxx.entity.Person;public interface CacheTestService {/*** 测试caffeine缓存1* @return*/Person testCaffeineCache1(Long id);/*** 测试caffeine缓存2* @return*/Person testCaffeineCache2(Long id);/*** 测试redis缓存1* @return*/Person testRedisCache1(Long id);/*** 测试redis缓存2* @return*/Person testRedisCache2(Long id);
}
CacheTestServiceImpl 实现类
package com.xxx.xxx.service.base.impl;import com.xxx.xxx.entity.Person;
import com.xxx.xxx.service.base.CacheTestService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import java.util.Date;@Service
public class CacheTestServiceImpl implements CacheTestService {@Override@Cacheable(value = "test1", key = "#id", cacheManager = "caffeineCacheManager")public Person testCaffeineCache1(Long id) {// 模拟数据库查询并返回return new Person().setId(id).setAge(18).setHobby(new String[]{"java"}).setAddress("松下问童子").setName("caffeineCache1").setCreateTime(new Date());}@Override@Cacheable(value = "test2", key = "#id", cacheManager = "caffeineCacheManager")public Person testCaffeineCache2(Long id) {// 模拟数据库查询并返回return new Person().setId(id).setAge(19).setHobby(new String[]{"C#"}).setAddress("言师采药去").setName("caffeineCache2").setCreateTime(new Date());}@Override@Cacheable(value = "test1", key = "#id", cacheManager = "redisCacheManager")public Person testRedisCache1(Long id) {// 模拟数据库查询并返回return new Person().setId(id).setAge(20).setHobby(new String[]{"Python"}).setAddress("只在此山中").setName("redisCache1").setCreateTime(new Date());}@Override@Cacheable(value = "test2", key = "#id", cacheManager = "redisCacheManager")public Person testRedisCache2(Long id) {// 模拟数据库查询并返回return new Person().setId(id).setAge(21).setHobby(new String[]{"Go"}).setAddress("云深不知处").setName("redisCache2").setCreateTime(new Date());}}
实体类
package com.xxx.xxx.entity;import lombok.Data;
import lombok.experimental.Accessors;import java.io.Serializable;
import java.util.Date;/*** @version 1.0.0* @Author DG* @Date 2022/1/6 15:13*/
@Data
@Accessors(chain = true) // 链式编程
public class Person implements Serializable {private Long id;private String name;private int age;private String[] hobby;private String address;private Date createTime;
}
接口层
package com.xxx.xxx.controller;import com.xxx.xxx.entity.Person;
import com.xxx.xxx.service.base.CacheService;
import com.xxx.xxx.service.base.CacheTestService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;/*** @version 1.0.0* @Author DG* @Date 2022/1/6 15:23*/
@RestController
@Api(value = "测试功能", tags = "功能测试")
public class CacheController {@Resourceprivate CacheTestService cacheTestService;@GetMapping("/get")@ApiOperation(value = "这是测试一级缓存和二级缓存同时使用的控制器", tags = "如果入参为奇数走redis,如果入参为偶数走caffeine")@ApiImplicitParam(name = "id", value = "对象ID,就是一个标记而已", dataType = "Long", dataTypeClass = Long.class, defaultValue = "0", example = "0")public List<Person> selectPerson(Long id){Person cache1;Person cache2;if (id % 2 == 0) {cache1 = cacheTestService.testCaffeineCache1(id);cache2 = cacheTestService.testRedisCache1(id);} else {cache1 = cacheTestService.testCaffeineCache2(id);cache2 = cacheTestService.testRedisCache2(id);}return Arrays.asList(cache1, cache2);}
}
完成!成果图我就不贴了,感兴趣可以试一下!
SpringBoot+Caffeine+Redis声明式缓存相关推荐
- spring boot 缓存_SpringBoot 应用 Redis 声明式缓存
什么是声明式缓存 ? Spring 框架提供一种抽象的缓存机制,且 Spring 只提供接口,不提供缓存的具体实现.所以在程序中使用时,需要有具体缓存的实现.目前支持的常见的缓存比如 JDK Conc ...
- [WCF REST] 通过ASP.NET Output Caching实现声明式缓存
ASP.NET的输出缓存(Output Caching)机制允许我们针对整个Web页面或者页面的某个部分(主要针对用户控件)最终呈现的HTML进行缓存.对于后续针对相同资源的请求,只需要直接将缓存的H ...
- 为什么说dubbo的声明式缓存不好用!!!
title: 为什么说dubbo的声明式缓存不好用!!! tags: dubbo mock zookeeper cache cluster categories: dubbo date: 2017-0 ...
- [Springboot]SpringCache + Redis实现数据缓存
前言 本文实现了SpringCache + Redis的集中式缓存,方便大家对学习了解缓存的使用. 本文实现: SpringCache + Redis的组合 通过配置文件实现了自定义key过期时间:k ...
- Springboot+caffeine 实现两级缓存
目录: 缓存.两级缓存 spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明 spring boot + spring cache caffeine简介 sp ...
- springboot和redis处理页面缓存
页面缓存是应对高并发的一个比较常见的方案,当请求页面的时候,会先查询redis缓存中是否存在,若存在则直接从缓存中返回页面,否则会通过代码逻辑去渲染页面,并将渲染后的页面缓存到redis中,然后返回. ...
- 8分钟带你学会SpringBoot整合Redis来实现缓存技术
1.概述 随着互联网技术的发展,对技术要求也越来越高,所以在当期情况下项目的开发中对数据访问的效率也有了很高的要求,所以在项目开发中缓存技术使用的也越来越多,因为它可以极大的提高系统的访问速度,关于缓 ...
- SpringBoot使用Redis清除所有缓存
实现思路: 简单描述一下,通过遍历获取所有Redis的key值,有两种方式,分别是StringRedisTemplate的delete方法和RedisTemplate的delete方法,这里我是调 ...
- spring-boot 整合redis作为数据缓存
添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp ...
最新文章
- memcache缓存失效
- Linux上调整sshd进程的优先级
- 编译分布式并行版caffe(Open MPI)教程
- tf.placeholder使用错误
- Pose-Aware Face Recognition in the Wild--填坑1
- 实验三:分别用for、while和do-while循环语句以及递归方法计算n!,并输出算式...
- Android连接网络立即同步时区
- 样式化加载失败的图片
- 用汇编语言实现c语言程序例题,C语言详细例题大全
- 1小时场景生活圈来了!苏宁双十一引领零售体验革命
- 腾讯通rtx中心服务器,腾讯通(RTX)详细使用教程
- 补血良方 核桃红枣阿胶糕
- html表单中文字前黑点怎么弄,如何将word文档中标题前的黑点去掉
- 地壳中元素含量排名记忆口诀_地壳含量_地壳中元素含量排名口诀
- H2O机器学习平台容器化部署——基于Docker
- 六面阿里天猫,已拿offer,我的面经复盘总结,原来进大厂没那么难了
- 使用Excel数据分析工具进行多元线性回归分析
- c语言memset() 函数
- ROP Emporium ret2csu
- Traffic shaping 一个事半功倍的程序化”噪音“解决方案