SpringBoot缓存抽象:@Cacheable与缓存管理器配置

文章目录
- 引言
- 一、SpringBoot缓存抽象概述
- 二、@Cacheable注解详解
- 2.1 @Cacheable的关键属性
- 三、缓存管理器配置
- 四、自定义键生成策略
- 五、缓存同步与失效策略
- 六、SpringBoot缓存最佳实践
- 总结
引言
缓存是提升应用性能的关键技术,SpringBoot提供了强大的缓存抽象层,使开发者能够以一致的方式操作不同的缓存实现。本文深入探讨SpringBoot的缓存机制,重点阐述@Cacheable注解的使用技巧及缓存管理器的配置方法,帮助开发者构建高效的缓存策略,优化应用性能。
一、SpringBoot缓存抽象概述
SpringBoot的缓存抽象建立在Spring Framework的缓存支持之上,提供了一套统一的缓存操作接口。这种抽象允许开发者在不修改业务代码的情况下,轻松切换底层缓存实现,如从本地缓存迁移到分布式缓存。缓存抽象的核心是CacheManager接口,它管理应用中的缓存,而@Cacheable等注解则提供了声明式缓存的能力。
/*** SpringBoot缓存示例项目启动类*/
@SpringBootApplication
@EnableCaching // 启用SpringBoot的缓存支持
public class CacheDemoApplication {public static void main(String[] args) {SpringApplication.run(CacheDemoApplication.class, args);}/*** 配置日志以显示缓存操作*/@Beanpublic LoggingCacheErrorHandler cacheErrorHandler() {return new LoggingCacheErrorHandler();}
}
二、@Cacheable注解详解
@Cacheable是SpringBoot缓存抽象中最常用的注解,用于标记方法的返回值应被缓存。当标记了@Cacheable的方法被调用时,SpringBoot会检查指定的缓存是否已包含对应键的值,如存在则直接返回缓存值,不执行方法;如不存在,则执行方法并将返回值放入缓存。这种机制显著减少了重复计算,提升了应用响应速度。
/*** 用户服务实现类,演示@Cacheable注解的基本用法*/
@Service
public class UserServiceImpl implements UserService {private final UserRepository userRepository;public UserServiceImpl(UserRepository userRepository) {this.userRepository = userRepository;}@Override@Cacheable(value = "users", key = "#id")public User getUserById(Long id) {// 模拟数据库查询的耗时操作try {Thread.sleep(2000); // 模拟2秒的查询延迟} catch (InterruptedException e) {Thread.currentThread().interrupt();}return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));}
}
2.1 @Cacheable的关键属性
@Cacheable注解具有多个属性,可精细控制缓存行为。value或cacheNames指定缓存名称;key定义缓存键生成规则,支持SpEL表达式;condition指定缓存条件;unless定义不缓存的条件;cacheManager指定使用的缓存管理器。合理设置这些属性可以实现精确的缓存控制,满足复杂业务场景的需求。
/*** 产品服务,展示@Cacheable的高级属性用法*/
@Service
public class ProductService {private final ProductRepository productRepository;public ProductService(ProductRepository productRepository) {this.productRepository = productRepository;}@Cacheable(cacheNames = "products", // 缓存名称key = "#category.concat('-').concat(#price)", // 组合键condition = "#price > 100", // 仅缓存价格大于100的产品unless = "#result == null", // 不缓存null结果cacheManager = "productCacheManager" // 指定缓存管理器)public List<Product> findProductsByCategoryAndPrice(String category, double price) {// 模拟复杂查询return productRepository.findByCategoryAndPriceGreaterThan(category, price);}
}
三、缓存管理器配置
缓存管理器是SpringBoot缓存抽象的核心组件,负责创建、获取和管理缓存实例。SpringBoot默认使用ConcurrentMapCacheManager作为缓存管理器,它基于ConcurrentHashMap实现,适用于开发环境或小型应用。对于生产环境,通常需要配置更高性能的缓存管理器,如Caffeine、Redis或EhCache等。
/*** 缓存配置类,演示多种缓存管理器的配置*/
@Configuration
public class CacheConfig {/*** 配置基于Caffeine的本地缓存管理器*/@Bean@Primarypublic CacheManager caffeineCacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();// 全局缓存配置cacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES) // 写入后30分钟过期.maximumSize(1000) // 最大缓存条目数.recordStats()); // 记录缓存统计信息// 预设缓存名称cacheManager.setCacheNames(Arrays.asList("users", "products", "orders"));return cacheManager;}/*** 配置Redis缓存管理器,用于分布式缓存*/@Beanpublic CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {// Redis缓存配置RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(60)) // 设置TTL为60分钟.disableCachingNullValues() // 禁止缓存null值.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));// 为不同的缓存设置不同的配置Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();cacheConfigurations.put("users", cacheConfiguration.entryTtl(Duration.ofMinutes(10)));cacheConfigurations.put("products", cacheConfiguration.entryTtl(Duration.ofHours(1)));return RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).withInitialCacheConfigurations(cacheConfigurations).build();}
}
四、自定义键生成策略
缓存键的设计直接影响缓存命中率和性能。SpringBoot默认使用方法参数的哈希值作为缓存键,但在复杂场景下,可能需要自定义键生成策略。通过实现KeyGenerator接口,开发者可以控制缓存键的生成逻辑,例如基于参数特定字段或组合多个参数生成键,从而提高缓存的精确性和有效性。
/*** 自定义缓存键生成器*/
@Component("customKeyGenerator")
public class CustomKeyGenerator implements KeyGenerator {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder key = new StringBuilder();// 添加类名key.append(target.getClass().getSimpleName()).append(":");// 添加方法名key.append(method.getName()).append(":");// 处理参数for (Object param : params) {if (param instanceof User) {// 对于User类型参数,使用其id作为键的一部分key.append(((User) param).getId());} else if (param != null) {// 对于其他参数,使用其toString()方法key.append(param.toString());} else {key.append("null");}key.append(":");}// 移除末尾的冒号if (key.charAt(key.length() - 1) == ':') {key.deleteCharAt(key.length() - 1);}return key.toString();}
}/*** 使用自定义键生成器的服务方法*/
@Service
public class OrderService {@Cacheable(cacheNames = "orders", keyGenerator = "customKeyGenerator")public Order getOrderDetails(Long orderId, String customerCode) {// 业务逻辑...return new Order(orderId, customerCode, /* 其他订单信息 */);}
}
五、缓存同步与失效策略
缓存数据与源数据的同步是缓存系统设计中的关键挑战。SpringBoot提供了@CachePut和@CacheEvict注解分别用于更新缓存和移除缓存项。@CachePut在不影响方法执行的情况下更新缓存,而@CacheEvict则负责清除缓存中的数据。通过合理使用这些注解,可以构建出高效的缓存同步机制,确保缓存数据的一致性。
/*** 演示缓存同步与失效的服务类*/
@Service
public class ProductInventoryService {private final ProductRepository productRepository;public ProductInventoryService(ProductRepository productRepository) {this.productRepository = productRepository;}@Cacheable(cacheNames = "inventory", key = "#productId")public int getProductInventory(Long productId) {// 从数据库查询库存return productRepository.findInventoryByProductId(productId);}@CachePut(cacheNames = "inventory", key = "#productId")public int updateProductInventory(Long productId, int newInventory) {// 更新数据库库存productRepository.updateInventory(productId, newInventory);return newInventory; // 返回值将被缓存}@CacheEvict(cacheNames = "inventory", key = "#productId")public void invalidateInventoryCache(Long productId) {// 仅清除缓存,不执行实际业务逻辑// 方法体可以为空,注解会处理缓存清除}// 批量清除缓存@CacheEvict(cacheNames = "inventory", allEntries = true)public void clearAllInventoryCache() {// 清除inventory缓存中的所有条目// 例如在库存批量更新后调用}
}
六、SpringBoot缓存最佳实践
在实际应用中,缓存的使用需要遵循一些最佳实践。避免过度缓存,只缓存热点数据和计算密集型操作结果;设置合理的过期时间,避免缓存数据长时间不一致;为缓存配置适当的大小限制,防止内存溢出;实现缓存监控和统计,及时发现缓存问题。遵循这些实践可以充分发挥缓存的性能优势,同时避免常见的缓存陷阱。
/*** 缓存监控配置*/
@Configuration
@EnableCaching
public class CacheMonitoringConfig extends CachingConfigurerSupport {private static final Logger logger = LoggerFactory.getLogger(CacheMonitoringConfig.class);/*** 自定义缓存解析器,添加日志记录*/@Overridepublic CacheResolver cacheResolver() {return new LoggingCacheResolver(caffeineCacheManager());}/*** 自定义缓存错误处理器*/@Overridepublic CacheErrorHandler errorHandler() {return new CacheErrorHandler() {@Overridepublic void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {logger.error("Cache get error for cache: {} and key: {}", cache.getName(), key, exception);}@Overridepublic void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {logger.error("Cache put error for cache: {} and key: {}", cache.getName(), key, exception);}@Overridepublic void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {logger.error("Cache evict error for cache: {} and key: {}", cache.getName(), key, exception);}@Overridepublic void handleCacheClearError(RuntimeException exception, Cache cache) {logger.error("Cache clear error for cache: {}", cache.getName(), exception);}};}/*** 缓存统计信息收集任务*/@Scheduled(fixedRate = 60000) // 每分钟执行一次public void reportCacheStatistics() {CaffeineCacheManager cacheManager = (CaffeineCacheManager) caffeineCacheManager();cacheManager.getCacheNames().forEach(cacheName -> {com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache = (com.github.benmanes.caffeine.cache.Cache<Object, Object>) ((CaffeineCache) cacheManager.getCache(cacheName)).getNativeCache();CacheStats stats = nativeCache.stats();logger.info("Cache: {} stats - Hit rate: {}, Eviction count: {}, Load time: {}ms",cacheName,String.format("%.2f", stats.hitRate() * 100) + "%",stats.evictionCount(),stats.totalLoadTime() / 1_000_000);});}
}
总结
SpringBoot的缓存抽象为Java应用提供了强大而灵活的缓存支持。通过@Cacheable注解和多样化的缓存管理器配置,开发者可以轻松实现高效的缓存策略。本文详细阐述了缓存抽象的核心概念、@Cacheable注解的使用技巧、缓存管理器的配置方法、自定义键生成策略以及缓存同步与失效机制。在实际应用中,开发者应根据业务需求选择合适的缓存实现,并遵循缓存最佳实践,如合理设置缓存大小和过期时间、实施缓存监控与统计等。恰当地使用SpringBoot缓存不仅能显著提升应用性能,还能减轻数据库负担,提高系统整体响应能力和用户体验。
相关文章:
SpringBoot缓存抽象:@Cacheable与缓存管理器配置
文章目录 引言一、SpringBoot缓存抽象概述二、Cacheable注解详解2.1 Cacheable的关键属性 三、缓存管理器配置四、自定义键生成策略五、缓存同步与失效策略六、SpringBoot缓存最佳实践总结 引言 缓存是提升应用性能的关键技术,SpringBoot提供了强大的缓存抽象层&am…...
下载文件,文件名乱码问题
C# .net framework 4.8 mvc 项目,做一个文件下载功能。 原项目是前端使用razor引擎方式做页面渲染的。 该项目原来就有一个模块是可供文件下载的,且文件名是中文。 但是我现在新增的这个模块,领导要求用js写,觉得razor太笨重。 …...
深入理解Linux进程管理:从基础到高级操作指南
1. 进程的定义、组成和环境 什么是进程? 想象你的电脑是一个大工厂,进程就是工厂里正在运行的机器。每个机器(进程)都有自己的任务,比如一台机器负责打印文件,另一台负责播放音乐。 进程的组成࿱…...
DOM与CSS:网页设计的核心力量
DOM与CSS:网页设计的核心力量 引言 在网页设计中,DOM(文档对象模型)与CSS(层叠样式表)是两个不可或缺的组成部分。它们共同构成了现代网页的骨架与外衣。本文将深入探讨DOM与CSS的关系、作用以及如何有效地运用它们来提升网页设计质量。 DOM:网页内容的结构化表示 什…...
深入解析pnpm与npm:颠覆传统包管理的技术革命与应用实践
深入解析pnpm与npm:颠覆传统包管理的技术革命与应用实践 引言:被node_modules支配的恐惧 "你的node_modules有多大?"这个灵魂拷问总能引发开发者会心一笑。当项目规模达到500MB时,npm install需要喝三杯咖啡的时间&am…...
OpenSSL 的主要功能及其示例命令
OpenSSL 是一个功能强大的开源工具包,用于处理各种与加密相关的任务,包括生成密钥、创建证书、加密解密数据、验证证书等。以下是 OpenSSL 的主要功能及其示例命令。 1.生成密钥 1.1 生成 RSA 私钥 openssl genrsa -out private_key.pem 2048• 说明&a…...
江科大51单片机笔记【11】AT24C02(I2C总线)
一、存储器 1.介绍 RAM的特点是存储速度特别快,但是掉电会丢失;ROM的特点是存储速度特别慢,但是掉电不会丢失 SRAM是所有存储器最快的,一般用于电脑的CPU高速缓存,容量相对较少,成本较高;DRAM…...
html css 笔记
01_浏览器相关知识 五大主流浏览器: Chrome Safari IE Firefox Opera (拥有自己的内核) 四大内核: webkit Trident Gecko blink. 02_网页相关知识 构成 网址 网站 网页 网页标准: 结构 表现 行为 分别对应 HTML CSS JavaScript 03_HTML简介 H…...
【一句话经验】ubuntu vi/vim 模式自动设置为paste
从centos过来,发现ubutun有些地方不习惯,尤其是vi的粘贴,默认自动进去了代码模式,导致每次粘贴必须得set paste,否则会出现问题。 解决办法非常简单,按照下面命令执行即可: cd ~ echo "…...
外层元素旋转,其包括在内的子元素一并旋转(不改变旋转中心),单元测试
思路:外层旋转后坐标,元素旋转后坐标,计算偏移坐标 <template><div class"outbox"><label>角度: <input v-model.number"rotate" type"number" /></label><br><div c…...
Docker容器安装软件(完整版)
文章目录 一、安装Docker1.1 docker 相关的命令1.2 配置镜像加速 二. 安装es2.1 创建网络2.2 拉取镜像2.3 创建挂载点目录2.4 部署单点es,创建es容器2.5 编写elasticsearch.yml2.6 重启es容器2.7 测试Elasticsearch是否安装成功 三. 基于Docker安装Kibana3.1 拉取镜…...
「 机器人 」扑翼飞行器通过总气动力控制四自由度运动方法
一、前言 在扑翼飞行中,总气动力(Total Aerodynamic Force)是指扑翼在运动过程中受到的所有空气动力作用的合力。它是由以下两种主要力的合成结果: 1. 升力(Lift, ):垂直于空气流方向的力,用于支持飞行器(或生物)的重量。 2. 阻力(Drag, ):平行于空气流方向的力,…...
Axios简单说明,快速上手
Ajax:异步的JavaScript和XML 作用: 数据交换异步交互 Axios:就是对原生Ajax进行封装,简化书写,快速开发 使用逻辑: 首先要安装Axios,可以通过npm在项目中安装: 打开命令行工具…...
云服务器安装宝塔面板部署
单机部署(前端vue项目) 服务器安装宝塔面板 连接到服务器 使用 SSH 连接到你的服务器: ssh rootip安装宝塔面板 运行以下命令来安装宝塔面板: yum install -y wget wget -O install.sh http://download.bt.cn/install/install_6.0.sh sh install.sh安…...
Python 编程题 第九节:二分查找、移动0、旋转字符串、判断子序列
二分查找 双指针查找 无重复数字 lst[1,15,24,36,78,92,101] nint(input()) def binary_search(n,lst):left 0right len(lst) - 1while left<right:mid(leftright)//2if lst[mid]n:return midelif lst[mid]<n:leftmid1elif lst[mid]>n:rightmid-1return -1 print…...
docker-compose部署MongoDB分片集群
前言 MongoDB 使用 keyFile 进行 节点间身份验证,我们需要先创建一个 keyFile 并确保所有副本集的节点使用相同的 keyFile。 openssl rand -base64 756 > mongo-keyfile chmod 400 mongo-keyfiledocker-compose部署分片集群 无密码方式 # docker-compose-mongodb.yml s…...
通义万相 2.1:AIGC 领域的 “王炸” 组合如何颠覆创作生态?
引言 在数字化和人工智能的飞速发展中,AIGC(AI生成内容)技术已经成为推动创作、设计和内容生成领域创新的核心力量。而当通义万相2.1与蓝耘智算平台强强联手,这一“王炸”组合不仅提升了AIGC的效率,还为创作生态带来了…...
elementPlus之日历扩展功能
在这里做个记录,感觉用得还挺多的 功能有如下: 切换月份按钮对应日历视图和中间日期都要变选择日期日历视图要变点击日历视图中的不属于当前选中月份的日期即可触发日历视图变化以及中间日期也要变 代码如下: <template><div clas…...
C# NX二次开发:获取模型中所有表达式并且更新某个表达式的值
大家好,今天要讲的是关于NX中表达式的相关UFUN函数。 UF_MODL_ask_exps_of_part (view source) tag_tpart_tagInputTag of the part to be queriedint *number_of_expsOutputNumber of expressions returnedtag_t * *expsOutput to UF_*free*All the expressions i…...
Ollama本地部署deepseek-r1蒸馏版
Docker安装Ollama 拉取镜像 docker pull ollama/ollama 启动-使用GPU docker run -d --gpusall -p 11434:11434 --name ollama ollama/ollamadocker run : Docker 的核心命令,用于创建并启动一个新的容器。 -d : 后台模式(detached mode)…...
计算机毕业设计:基于web的乡村旅游系统
基于web的乡村旅游系统mysql数据库创建语句基于web的乡村旅游系统oracle数据库创建语句基于web的乡村旅游系统sqlserver数据库创建语句基于web的乡村旅游系统springspringMVChibernate框架对象(javaBean,pojo)设计基于web的乡村旅游系统springspringMVCmybatis框架对象(javaBea…...
c#面试题整理9
1.遍历xml文档 2.解释一下这段 String s new String("xyz"); 这段在C#平台中,编译失败 3.说明一下抽象类 抽象类可以有构造函数 抽象类不能是静态和密封的类,密封的类表示无法继承,抽象类本身就不可实例化,加不好…...
【具身相关】legged_gym, isaacgym、rsl_rl关系梳理
【legged_gym】legged_gym, isaacgym代码逻辑梳理 总体关系IsaacGymlegged_gymrsl_rl三者的关系 legged_gym代码库介绍环境模块env 总体关系 IsaacGym Isaac Gym 是 NVIDIA 开发的一个高性能物理仿真平台,专门用于强化学习和机器人控制任务。它基于 NVIDIA 的 Phy…...
个人学习编程(3-12) 刷题
杨辉三角形: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 主要是发现规律:一、…...
Java Kryo 序列化与反序列化
Java Kryo 序列化与反序列化 Kryo 是一个高效的 Java 序列化框架,提供比 Java 原生序列化更快、更紧凑的序列化能力。它通常用于缓存、分布式通信和数据存储。 1. 引入 Kryo 依赖 如果你使用的是 Maven,可以添加以下依赖: <dependency><groupId>com.esoteri…...
侯捷C++课程学习笔记:构造函数那些事儿(四)
C 构造函数全面解析 上图节选自爱吃喵的鲤鱼 一、构造函数基础特性 1. 核心功能定位 对象初始化中枢:负责在对象创建时完成成员变量的初始化工作生命周期唯一性:每个对象在其生命周期内仅被调用一次,类似出生证明的签发过程 2. 基础语…...
Java 序列化和反序列化为什么要实现Serializable接口
1. 什么是序列化和反序列化 序列化:将对象的状态信息转换为可以存储或传输的形式(通常是字节序列)的过程。例如,将一个 Java 对象保存到文件中或者通过网络发送给其他程序。 反序列化:将字节序列恢复为对象的过程。比…...
java虚拟机(JVM)以及各种参数详解
Java 虚拟机(JVM)提供了许多参数来调整其行为和性能,以便更好地适应不同的应用场景。理解和使用这些参数对于优化 Java 应用程序的性能非常重要。以下是一些常用的 JVM 参数及其详细说明: 1. 内存管理参数 -Xms<size>&…...
微信小程序审核失败,你的小程序涉及提供播放、观看等服务,请补充选择:文娱-其他视频类目 解决
之前审核的都没有什么问题,结果这次就不给过还提示我们这个。 我们的视频是操作演示的视频。仅用于介绍使用。 是否接受修改指引,勾选我不理解以上内容 再勾选 下面不理解内容异项 申诉理由 视频播放和观看只限于当前用户自己使用,而视…...
电力行业研究系列报告
欧洲风能:2024年统计数据及2025-2030年展望 固态电池全景图:方兴未艾,技术竞逐 电力设备新能源2025年3月投资策略:固态电池产业加速推进,关注GTC大会HVDC电源产品应用趋势 62页PPT了解国内外40家固态电池典型企业技…...
