Spring缓存注解@Cacheable详细介绍和实际使用案例

@Cacheable是Spring框架中用于缓存方法返回结果的注解,它可以显著提高应用程序的性能,特别是对于一些计算密集型或频繁调用且结果不经常变化的方法。以下是关于@Cacheable的详细介绍:

基本使用

  • 添加依赖:使用@Cacheable注解前,需要在项目的pom.xml中添加Spring Cache依赖,例如使用Spring Boot时,可以添加以下依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • 启用缓存:在Spring Boot的主应用程序类上添加@EnableCaching注解来启用缓存功能,如下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching
public class MyApplication {public static void main(String[] args) {SpringApplication.run(MyApplication.class, args);}
}
  • 使用@Cacheable注解方法:在需要缓存结果的方法上添加@Cacheable注解,如下:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class MyService {@Cacheable("myCache")public String getCachedData(String key) {// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "Data for key: " + key;}
}

在上述示例中,@Cacheable("myCache")注解告诉Spring将该方法的结果存储在名为myCache的缓存中。当这个方法被调用时,Spring首先检查myCache中是否已经有了对应key的缓存结果。如果有,直接从缓存中返回结果,而不执行方法体;如果没有,则执行方法并将结果存储在缓存中。

注解属性

以下是对 @Cacheable 注解的主要参数的详细介绍:

1. value

  • 作用
    • 指定缓存的名称。它是 @Cacheable 注解最常用的参数之一,用来标识一个或多个缓存存储区域。当方法的返回结果需要存储时,会被存储在这些指定的缓存名称下。cacheNames参数与之相同。
  • 使用示例
    @Cacheable(value = "userCache")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    在上述示例中,方法 getUserById 的结果将被存储在名为 userCache 的缓存区域中。
  • 多值使用
    • 可以使用一个数组来指定多个缓存名称,此时结果会被存储在多个缓存区域中。
    @Cacheable(value = {"cache1", "cache2"})
    public String getSomeData(String key) {// 业务逻辑
    }
    
    当调用 getSomeData 方法时,其结果会同时存储在 cache1cache2 缓存区域中。

2. key

  • 作用
    • 用于指定缓存存储和检索的键。默认情况下,会使用方法的参数作为键,但可以使用 SpEL(Spring Expression Language)自定义键的生成表达式。
  • 使用示例(使用参数作为键)
    @Cacheable(value = "userCache", key = "#id")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    这里使用 #id 作为键,即调用 getUserById 方法时传入的 id 参数将作为缓存的键。
  • 使用示例(使用多个参数作为键)
    @Cacheable(value = "userCache", key = "#firstName + '-' + #lastName")
    public User getUserByName(String firstName, String lastName) {// 业务逻辑
    }
    
    此示例中,将使用 firstName lastName 参数组合(使用 - 分隔)作为缓存的键。
  • 使用示例(使用方法信息作为键)
    @Cacheable(value = "userCache", key = "#root.methodName + '_' + #id")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    这里使用方法名称和 id 参数组合作为键,例如对于 getUserById(1L) 的调用,键可能是 getUserById_1
  • 特殊键生成器使用
    • 可以使用自定义的键生成器,需要实现 org.springframework.cache.interceptor.KeyGenerator 接口,并在 @Cacheable 注解中引用。
    @Cacheable(value = "userCache", keyGenerator = "myCustomKeyGenerator")
    public User getUserById(Long id) {// 业务逻辑
    }
    

3. condition

  • 作用
    • 用于指定一个 SpEL 表达式,当表达式的结果为 true 时,才会进行缓存操作。可以根据方法的参数、返回值或其他信息来决定是否缓存结果。
  • 使用示例
    @Cacheable(value = "userCache", condition = "#id > 0")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    仅当 id 参数大于 0 时,才会将 getUserById 方法的结果存储到 userCache 中。
  • 更复杂的条件示例
    @Cacheable(value = "userCache", condition = "#user.age > 18 && #user.active")
    public User getUser(User user) {// 业务逻辑
    }
    
    仅当 user 的年龄大于 18 岁且 active 属性为 true 时,结果才会被缓存。

4. unless

  • 作用
    • 也是使用 SpEL 表达式,当表达式的结果为 true 时,不会将结果存储在缓存中。与 condition 相反,condition 决定是否缓存,unless 决定不缓存的情况。
  • 使用示例
    @Cacheable(value = "userCache", unless = "#result == null")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    如果 getUserById 方法的返回结果为 null,则不会将结果存储在 userCache 中。
  • 使用示例(根据结果属性判断)
    @Cacheable(value = "userCache", unless = "#result.name == null")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    getUserById 的返回结果的 name 属性为 null 时,不会存储该结果。

5. cacheManager

  • 作用
    • 可以指定一个特定的缓存管理器来管理缓存操作,用于在多个缓存管理器存在的情况下,选择使用哪个缓存管理器。
  • 使用示例
    @Cacheable(value = "userCache", cacheManager = "customCacheManager")
    public User getUserById(Long id) {// 业务逻辑
    }
    
    上述代码中,使用名为 customCacheManager 的缓存管理器来管理 userCache 中的缓存操作。

6. cacheResolver

  • 作用
    • 可以指定一个特定的缓存解析器,用于更灵活地解析缓存操作的存储位置,实现 org.springframework.cache.interceptor.CacheResolver 接口的自定义缓存解析器。
  • 使用示例
    @Cacheable(value = "userCache", cacheResolver = "myCacheResolver")
    public User getUserById(Long id) {// 业务逻辑
    }
    

7. sync

  • 作用
    • 是一个布尔值参数,用于指示是否以同步方式执行缓存操作。默认为 false,表示使用异步方式。
    • 当设置为 true 时,同一时间内只有一个线程可以执行该方法,其他线程会阻塞等待缓存结果,避免多个线程同时执行该方法。
  • 使用示例
    @Cacheable(value = "userCache", sync = true)
    public User getUserById(Long id) {// 业务逻辑
    }
    

这些参数可以灵活组合使用,以满足不同的缓存需求,通过使用 @Cacheable 及其参数,可以在 Spring 应用程序中轻松实现缓存机制,提高系统性能和响应速度,同时也需要根据具体的业务场景和性能需求,合理调整这些参数的使用。

缓存工作原理

  • 当一个被@Cacheable注解的方法被调用时,Spring的AOP机制会拦截该方法的调用。
  • 首先,根据valuekey属性查找缓存中是否已经存储了相应的结果。
  • 如果缓存中存在结果,则直接返回缓存中的结果,不执行方法体。
  • 如果缓存中不存在结果,则执行方法体,将结果存储在缓存中,并返回结果。

适用场景

  • 数据访问层:在数据访问层中,对于经常查询但很少修改的数据,如配置信息、字典数据等,可以使用@Cacheable进行缓存,减少对数据库的访问。
  • 业务逻辑层:对于一些复杂的计算逻辑,如计算用户积分、统计报表等,当输入参数相同时,可以使用@Cacheable避免重复计算。

注意事项

  • 缓存更新:当被缓存的数据发生变化时,需要使用@CacheEvict@CachePut等注解来更新或清除缓存,以保证数据的一致性。
  • 缓存失效:缓存的存储是有限的,需要考虑缓存失效的策略,如LRU(Least Recently Used)等,避免内存溢出。
  • 序列化:存储在缓存中的对象需要是可序列化的,因为缓存存储可能会将对象存储在分布式环境中,如Redis等,确保对象实现了Serializable接口或使用其他序列化机制。

与其他缓存注解的配合使用

  • @CacheEvict:用于清除缓存,例如:
    @CacheEvict(value = "myCache", key = "#key")
    public void clearCache(String key) {// 清除myCache中指定key的缓存
    }
    
  • @CachePut:用于更新缓存,无论缓存中是否存在结果,都会执行方法体,并将结果存储在缓存中,例如:
    @CachePut(value = "myCache", key = "#key")
    public String updateCachedData(String key) {return "Updated data for key: " + key;
    }
    

缓存存储的选择

  • 可以使用Spring默认的缓存存储,也可以集成外部缓存,如Redis、Ehcache等。集成外部缓存时,需要在配置文件中添加相应的配置,例如使用Redis时:
spring:cache:type: redisredis:cache-null-values: false

通过合理使用@Cacheable和其他缓存注解,可以有效提高应用程序的性能和响应速度,同时需要根据具体的应用场景和需求,灵活运用这些注解,确保缓存的一致性和有效性。

实际使用案例

以下是一个更完整的 @Cacheable 注解的实际使用案例,结合了一个简单的 Spring Boot 应用程序,包括服务层、控制器和数据访问层,展示如何在不同场景下使用 @Cacheable 注解来提高性能:

1. 添加依赖

首先,在 pom.xml 中添加 Spring Boot 的缓存依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2. 启用缓存

在 Spring Boot 的主应用程序类上添加 @EnableCaching 注解,以启用缓存功能:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching
public class CacheableApplication {public static void main(String[] args) {SpringApplication.run(CacheableApplication.class, args);}
}

3. 数据访问层 (Repository)

假设我们有一个简单的数据访问接口和实现类,模拟从数据库中获取用户信息:

import org.springframework.stereotype.Repository;@Repository
public interface UserRepository {User findById(Long id);
}import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;@Repository
public class UserRepositoryImpl implements UserRepository {private final Map<Long, User> userMap = new HashMap<>();public UserRepositoryImpl() {// 初始化一些用户数据userMap.put(1L, new User(1L, "Alice"));userMap.put(2L, new User(2L, "Bob"));userMap.put(3L, new User(3L, "Charlie"));}@Overridepublic User findById(Long id) {// 模拟从数据库获取数据的延迟try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return userMap.get(id);}
}

4. 服务层 (Service)

在服务层使用 @Cacheable 注解来缓存 findById 方法的结果:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;@Cacheable(value = "userCache", key = "#id")public User findUserById(Long id) {return userRepository.findById(id);}
}

5. 控制器 (Controller)

在控制器中调用服务层的方法,观察缓存的效果:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/users/{id}")public User getUserById(@PathVariable Long id) {return userService.findUserById(id);}
}

6. 实体类 (Entity)

定义一个简单的 User 实体类:

import java.io.Serializable;public class User implements Serializable {private Long id;private String name;public User() {}public User(Long id, String name) {this.id = id;this.name = name;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

7. 测试缓存效果

  • 当你第一次访问 http://localhost:8080/users/1 时,会看到一个大约 2 秒的延迟,因为它会调用 UserRepositoryImplfindById 方法从模拟的数据库中获取数据。
  • 再次访问 http://localhost:8080/users/1 时,会立即返回结果,因为结果已经被缓存,不会再调用 findById 方法。

8. 高级使用

  • 使用条件缓存
    @Cacheable(value = "userCache", key = "#id", condition = "#id > 0")
    public User findUserById(Long id) {return userRepository.findById(id);
    }
    
    上述代码中,仅当 id 大于 0 时才会进行缓存。
  • 使用 unless 排除缓存结果
    @Cacheable(value = "userCache", key = "#id", unless = "#result == null")
    public User findUserById(Long id) {return userRepository.findById(id);
    }
    
    当结果为 null 时,不缓存结果。

9. 结合其他缓存注解

  • 可以使用 @CachePut 来更新缓存:
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.stereotype.Service;@Service
    public class UserService {@Autowiredprivate UserRepository userRepository;@Cacheable(value = "userCache", key = "#id")public User findUserById(Long id) {return userRepository.findById(id);}@CachePut(value = "userCache", key = "#user.id")public User updateUser(User user) {// 模拟更新用户信息return userRepository.update(user);}
    }
    
    当调用 updateUser 方法时,会更新缓存中的用户信息。
  • 使用 @CacheEvict 来清除缓存:
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.stereotype.Service;@Service
    public class UserService {@Autowiredprivate UserRepository userRepository;@Cacheable(value = "userCache", key = "#id")public User findUserById(Long id) {return userRepository.findById(id);}@CacheEvict(value = "userCache", key = "#id")public void deleteUser(Long id) {userRepository.delete(id);}
    }
    
    当调用 deleteUser 方法时,会清除 userCache 中对应 id 的缓存。

10. 缓存管理

  • 可以使用不同的缓存管理器或缓存解析器,例如使用 Redis 作为缓存存储:
    spring:cache:type: redisredis:cache-null-values: false
    
    并在 @Cacheable 中指定:
    @Cacheable(value = "userCache", cacheManager = "redisCacheManager")
    public User findUserById(Long id) {return userRepository.findById(id);
    }
    

通过以上示例,你可以看到如何在一个实际的 Spring Boot 应用程序中使用 @Cacheable 注解来实现缓存功能,提高应用程序的性能和响应速度,同时根据不同的业务场景和需求,灵活运用 @Cacheable 及其相关注解。你可以通过测试不同的请求和观察日志,来更好地理解缓存的存储、更新和清除操作。

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

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

相关文章

gitlab使用多数据库

1. 说明 默认情况下&#xff0c;GitLab 使用一个单一的应用数据库&#xff0c;称为主数据库。为了扩展 GitLab&#xff0c;您可以将 GitLab 配置为使用多个应用数据库。 设置多个数据库后&#xff0c;GitLab 将使用第二个应用数据库用于 CI/CD 功能&#xff0c;称为 CI 数据库…

常用排序算法之插入排序

目录 前言 一、基本原理 1.算法步骤 2.动画演示 3.插入排序的实现代码 二、插入排序的时间复杂度 1. 时间复杂度 1.最优时间复杂度 2.最差时间复杂度 3.平均时间复杂度 2. 空间复杂度 三、插入排序的优缺点 1.优点 2.缺点 四、插入排序的改进与变种 五、插入排…

【机器学习实战入门】使用OpenCV进行性别和年龄检测

Gender and Age Detection Python 项目 首先,向您介绍用于此高级 Python 项目的性别和年龄检测中的术语: 什么是计算机视觉? 计算机视觉是一门让计算机能够像人类一样观察和识别数字图像和视频的学科。它面临的挑战大多源于对生物视觉有限的了解。计算机视觉涉及获取、处…

python爬虫的学习流程(1-前提准备)

这里主要记录一下我的python爬虫初级的学习的流程 1.python爬虫入门实战课 什么是爬虫&#xff1f;众说纷纭&#xff0c;我们引用维基百科上对网络爬虫的介绍&#xff1a; 网络爬虫&#xff08;英语&#xff1a;Web crawler&#xff09;&#xff0c;也叫网络蜘蛛&#xff08;…

PyTorch使用教程(13)-一文搞定模型的可视化和训练过程监控

一、简介 在现代深度学习的研究和开发中&#xff0c;模型的可视化和监控是不可或缺的一部分。PyTorch&#xff0c;作为一个流行的深度学习框架&#xff0c;通过其丰富的生态系统提供了多种工具来满足这一需求。其中&#xff0c;torch.utils.tensorboard 是一个强大的接口&…

学习ASP.NET Core的身份认证(基于JwtBearer的身份认证6)

重新创建WebApi项目&#xff0c;安装Microsoft.AspNetCore.Authentication.JwtBearer包&#xff0c;将之前JwtBearer测试项目中的初始化函数&#xff0c;jwt配置类、token生成类全部挪到项目中。   重新编写login函数&#xff0c;之前测试Cookie和Session认证时用的函数适合m…

【Linux系统】—— 编译器 gcc/g++ 的使用

【Linux系统】—— 编译器 gcc/g 的使用 1 用 gcc 直接编译2 翻译环境2.1 预处理&#xff08;进行宏替换&#xff09;2.2 编译&#xff08;生成汇编&#xff09;2.3 汇编&#xff08;生成机器可识别代码&#xff09;2.4 链接2.5 记忆小技巧2.6 编译方式2.7 几个问题2.7.1 如何理…

[已解决]chatgpt被降智了怎么办?(无法联网、识别图片、文件、画图)

文章目录 1、治标办法一发图2、治本方法—使用ChatGPT中国区代理官方站点 1、治标办法一发图 该方法原本就有&#xff0c;但是在1.1日ChatGPT降智事件中突然失效。于1月11日&#xff0c;该方法又突然有效&#xff0c;因此也标志着本次ChatGPT降智事件的结束。当你ChatGPT出现降…

缓存、数据库双写一致性解决方案

双写一致性问题的核心是确保数据库和缓存之间的数据同步&#xff0c;以避免缓存与数据库数据不同步的问题&#xff0c;尤其是在高并发和异步环境下。本文将探讨双写一致性面临的主要问题和解决方案&#xff0c;重点关注最终一致性。 本文讨论的是最终一致性问题 双写一致性面…

element el-table合并单元格

合并 表格el-table添加方法:span-method"” <el-table v-loading"listLoading" :data"SHlist" ref"tableList" element-loading-text"Loading" border fit highlight-current-row :header-cell-style"headClass" …

qml OpacityMask详解

1、概述 OpacityMask是QML&#xff08;Qt Meta-Object Language&#xff09;中的一种图形效果&#xff0c;它使用另一个项目&#xff08;通常是一个图像或图形项&#xff09;作为遮罩来控制源项目的透明度。这种效果允许开发者通过遮罩的alpha通道来精确地控制源项目中哪些部分…

RabbitMQ1-消息队列

目录 MQ的相关概念 什么是MQ 为什么要用MQ MQ的分类 MQ的选择 RabbitMQ RabbitMQ的概念 四大核心概念 RabbitMQ的核心部分 各个名词介绍 MQ的相关概念 什么是MQ MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&am…

渗透测试之SSRF漏洞原理 危害 产生的原因 探测手法 防御手法 绕过手法 限制的手段

目录 SSRF说明: SSRF攻击流程 原理&#xff1a; 危害: SSRF产生的原因 ssrf漏洞利用{危害} 探测手法是否存在SSRF漏洞 如何找ssrf漏洞位置 分享连接地址 google hack url关键字 PHP语言中可能出现的ssrf漏洞函数 file_get_contents sockopen() curl_exec() SSRF…

centos9编译安装opensips 二【进阶篇-定制目录+模块】推荐

环境&#xff1a;centos9 last opensips -V version: opensips 3.6.0-dev (x86_64/linux) flags: STATS: On, DISABLE_NAGLE, USE_MCAST, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, HP_MALLOC, DBG_MALLOC, CC_O0, FAST_LOCK-ADAPTIVE_WAIT ADAPTIVE_WAIT_LOOPS1024, MAX_RE…

【前端】CSS学习笔记(1)

目录 CSS的简介CSS的概念语法 CSS的引入方式内联样式&#xff08;行内样式&#xff09;内部样式外部样式&#xff08;推荐&#xff09; 选择器全局选择器元素选择器类选择器ID选择器合并选择器后代选择器子选择器相邻兄弟选择器通用兄弟选择器伪类选择器:link:visited:hover:ac…

Java面试专题——面向对象

面向过程和面向对象的区别 面向过程&#xff1a;当事件比较简单的时候&#xff0c;利用面向过程&#xff0c;注重的是事件的具体的步骤/过程&#xff0c;注重的是过程中的具体的行为&#xff0c;以函数为最小单位&#xff0c;考虑怎么做。 面向对象&#xff1a;注重找“参与者…

电阻电位器可调电阻信号隔离变送器典型应用

电阻电位器可调电阻信号隔离变送器典型应用 产品描述&#xff1a; 深圳鑫永硕科技的XYS-5587系列是一进一出线性电子尺(电阻/电位计信号及位移)信号隔离变送器&#xff0c;是将输入电阻,线性电子尺,角度位移传感器信号进行采集,隔离,放大并转换成模拟量信号的小型仪表设备,并以…

Alluxio 联手 Solidigm 推出针对 AI 工作负载的高级缓存解决方案

作者&#xff1a;Wayne Gao, Yi Wang, Jie Chen, Sarika Mehta Alluxio 作为全球领先的 AI 缓存解决方案供应商&#xff0c; 提供针对 GPU 驱动 AI 负载的高速缓存。其可扩展架构支持数万个节点&#xff0c;能显著降低存储带宽的消耗。Alluxio 在解决 AI 存储挑战方面的前沿技…

文件系统格式RAW数据恢复全攻略

一、RAW文件系统格式深度解析 在数据存储的世界里&#xff0c;文件系统扮演着至关重要的角色&#xff0c;它负责管理和组织硬盘、U盘、SD卡等存储设备上的数据。而RAW文件系统格式&#xff0c;作为一种特殊状态&#xff0c;常常让许多用户感到困惑与不安。RAW格式并非一种标准…

探索 Stable-Diffusion-Webui-Forge:更快的AI图像生成体验

目录 简介&#x1f31f; 主要特点&#x1f4e5; 安装步骤1. 下载2. 配置环境和安装依赖3. 模型目录说明 &#x1f680; 运行 Stable-Diffusion-Webui-Forge1. 进入项目目录2. 运行项目3. 打开页面 &#x1f3a8; 使用体验常见问题&#x1f4dd; 小结 简介 Stable-Diffusion-We…