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

news2025/3/13 7:29:39

在这里插入图片描述

文章目录

    • 引言
    • 一、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);
    }
    
    /**
     * 配置日志以显示缓存操作
     */
    @Bean
    public 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
    @Primary
    public 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缓存管理器,用于分布式缓存
     */
    @Bean
    public 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 {
    
    @Override
    public 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);
    
    /**
     * 自定义缓存解析器,添加日志记录
     */
    @Override
    public CacheResolver cacheResolver() {
        return new LoggingCacheResolver(caffeineCacheManager());
    }
    
    /**
     * 自定义缓存错误处理器
     */
    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                logger.error("Cache get error for cache: {} and key: {}", cache.getName(), key, exception);
            }
            
            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                logger.error("Cache put error for cache: {} and key: {}", cache.getName(), key, exception);
            }
            
            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                logger.error("Cache evict error for cache: {} and key: {}", cache.getName(), key, exception);
            }
            
            @Override
            public 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缓存不仅能显著提升应用性能,还能减轻数据库负担,提高系统整体响应能力和用户体验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江科大51单片机笔记【11】AT24C02(I2C总线)

一、存储器 1.介绍 RAM的特点是存储速度特别快&#xff0c;但是掉电会丢失&#xff1b;ROM的特点是存储速度特别慢&#xff0c;但是掉电不会丢失 SRAM是所有存储器最快的&#xff0c;一般用于电脑的CPU高速缓存&#xff0c;容量相对较少&#xff0c;成本较高&#xff1b;DRAM…

外层元素旋转,其包括在内的子元素一并旋转(不改变旋转中心),单元测试

思路&#xff1a;外层旋转后坐标&#xff0c;元素旋转后坐标&#xff0c;计算偏移坐标 <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&#xff0c;创建es容器2.5 编写elasticsearch.yml2.6 重启es容器2.7 测试Elasticsearch是否安装成功 三. 基于Docker安装Kibana3.1 拉取镜…

「 机器人 」扑翼飞行器通过总气动力控制四自由度运动方法

一、前言 在扑翼飞行中,总气动力(Total Aerodynamic Force)是指扑翼在运动过程中受到的所有空气动力作用的合力。它是由以下两种主要力的合成结果: 1. 升力(Lift, ):垂直于空气流方向的力,用于支持飞行器(或生物)的重量。 2. 阻力(Drag, ):平行于空气流方向的力,…

Axios简单说明,快速上手

Ajax&#xff1a;异步的JavaScript和XML 作用&#xff1a; 数据交换异步交互 Axios&#xff1a;就是对原生Ajax进行封装&#xff0c;简化书写&#xff0c;快速开发 使用逻辑&#xff1a; 首先要安装Axios&#xff0c;可以通过npm在项目中安装&#xff1a; 打开命令行工具…

云服务器安装宝塔面板部署

单机部署(前端vue项目) 服务器安装宝塔面板 连接到服务器 使用 SSH 连接到你的服务器&#xff1a; ssh rootip安装宝塔面板 运行以下命令来安装宝塔面板&#xff1a; yum install -y wget wget -O install.sh http://download.bt.cn/install/install_6.0.sh sh install.sh安…

通义万相 2.1:AIGC 领域的 “王炸” 组合如何颠覆创作生态?

引言 在数字化和人工智能的飞速发展中&#xff0c;AIGC&#xff08;AI生成内容&#xff09;技术已经成为推动创作、设计和内容生成领域创新的核心力量。而当通义万相2.1与蓝耘智算平台强强联手&#xff0c;这一“王炸”组合不仅提升了AIGC的效率&#xff0c;还为创作生态带来了…

elementPlus之日历扩展功能

在这里做个记录&#xff0c;感觉用得还挺多的 功能有如下&#xff1a; 切换月份按钮对应日历视图和中间日期都要变选择日期日历视图要变点击日历视图中的不属于当前选中月份的日期即可触发日历视图变化以及中间日期也要变 代码如下&#xff1a; <template><div clas…

C# NX二次开发:获取模型中所有表达式并且更新某个表达式的值

大家好&#xff0c;今天要讲的是关于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 的核心命令&#xff0c;用于创建并启动一个新的容器。 -d : 后台模式&#xff08;detached mode&#xff09…

计算机毕业设计:基于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#平台中&#xff0c;编译失败 3.说明一下抽象类 抽象类可以有构造函数 抽象类不能是静态和密封的类&#xff0c;密封的类表示无法继承&#xff0c;抽象类本身就不可实例化&#xff0c;加不好…

【具身相关】legged_gym, isaacgym、rsl_rl关系梳理

【legged_gym】legged_gym, isaacgym代码逻辑梳理 总体关系IsaacGymlegged_gymrsl_rl三者的关系 legged_gym代码库介绍环境模块env 总体关系 IsaacGym Isaac Gym 是 NVIDIA 开发的一个高性能物理仿真平台&#xff0c;专门用于强化学习和机器人控制任务。它基于 NVIDIA 的 Phy…

侯捷C++课程学习笔记:构造函数那些事儿(四)

C 构造函数全面解析 上图节选自爱吃喵的鲤鱼 一、构造函数基础特性 1. 核心功能定位 ​对象初始化中枢&#xff1a;负责在对象创建时完成成员变量的初始化工作​生命周期唯一性&#xff1a;每个对象在其生命周期内仅被调用一次&#xff0c;类似出生证明的签发过程 2. 基础语…

微信小程序审核失败,你的小程序涉及提供播放、观看等服务,请补充选择:文娱-其他视频类目 解决

之前审核的都没有什么问题&#xff0c;结果这次就不给过还提示我们这个。 我们的视频是操作演示的视频。仅用于介绍使用。 是否接受修改指引&#xff0c;勾选我不理解以上内容 再勾选 下面不理解内容异项 申诉理由 视频播放和观看只限于当前用户自己使用&#xff0c;而视…

蓝桥杯嵌入式组第七届省赛题目解析+STM32G431RBT6实现源码

文章目录 1.题目解析1.1 分而治之&#xff0c;藕断丝连1.2 模块化思维导图1.3 模块解析1.3.1 KEY模块1.3.2 ADC模块1.3.3 IIC模块1.3.4 UART模块1.3.5 LCD模块1.3.6 LED模块1.3.7 TIM模块 2.源码3.第七届题目 前言&#xff1a;STM32G431RBT6实现嵌入式组第七届题目解析源码&…

苹果iOS 18.4将强制升级HomeKit架构,旧版设备或无法使用

在科技飞速发展的当下,智能家居领域也在不断革新。而苹果公司作为科技行业的巨头,其每一次动作都备受关注。近日,有消息称苹果计划在iOS 18.4版本中停止对旧版HomeKit架构的支持,这一举措意味着用户将被迫升级,也可能对众多使用Apple Home应用的智能家居设备用户产生深远影…

在MATLAB中实现PID控制仿真

在MATLAB中实现PID控制仿真可以通过代码编程或Simulink图形化建模两种方式完成。以下是两种方法的详细操作步骤和示例&#xff1a; 方法1&#xff1a;使用MATLAB脚本编程&#xff08;基于控制系统工具箱&#xff09; 步骤1&#xff1a;定义被控对象的数学模型 假设被控对象是…

Python核心模块的高级用法及Linux系统常用命令

一、Python相关 1、正则表达式 &#xff08;1&#xff09;正则表达式基础 ①含义&#xff1a;记录文本规则的代码。 ②注意&#xff1a;需要导入re模块 ③特点&#xff1a; 语法比较复杂&#xff0c;可读性较差。通用性很强&#xff0c;适用于多种编程语言 ④步骤&#…

Spring 框架学习

技术体系结构 总体技术体系 单一架构 一个项目&#xff0c;一个工程&#xff0c;导出为一个 war 包&#xff0c;在一个 Tomcat 上运行&#xff0c;也叫 all in one。 单一架构&#xff0c;项目主要应用技术框架为&#xff1a;Spring、SpringMVC 、Mybatis。 分布式架构 一个…