Spring系列之Spring Cache缓存注解的使用

目录

一、概述

二、缓存注解

[1、@Cacheable 缓存结果](#1、@Cacheable 缓存结果)

[2、@CachePut 更新缓存](#2、@CachePut 更新缓存)

[3、@CacheEvict 清除缓存](#3、@CacheEvict 清除缓存)

[4、@Caching 组合缓存(不常用)](#4、@Caching 组合缓存(不常用))

[5、@CacheConfig 类级别缓存配置(不常用)](#5、@CacheConfig 类级别缓存配置(不常用))

[6、@CacheResult 设置缓存超时(不常用)](#6、@CacheResult 设置缓存超时(不常用))

三、使用方式

1、@EnableCaching开启缓存

2、在方法上添加注解@Cacheable等注解

[四 、优劣势](#四 、优劣势)

五、总结


一、概述

Spring Cache 是 Spring Framework 提供的一个用于操作缓存的抽象层,它简化了在 Spring 应用程序中集成和使用缓存的过程

Spring Cache 不直接实现任何缓存技术,而是定义了一套标准接口和注解,使得开发人员可以轻松地切换底层缓存实现,而无需修改业务逻辑代码

是不是有点似懂非懂,那么使用一个🌰来了解

🌰:一般会将用户相关信息存储到缓存中,为了提高用户信息的查询效率,那么就是如下操作:

java 复制代码
@Service
public class UserService {

       @Autowire
       private UserMapper userMapper;

       @Autowired
       private RedisTemplate<String, Object> redisTemplate;

       //查询用户
       public User getUserById(Long userId) {
           String userKey = "userId_" + userId;
           Uesr user = (User) redisTemplate.opsForValue().get(userKey);
           if(user != null) {
                return user;
           }
           user = userMapper.getUserById(userId);
           if(user != null) {
                redisTemplate.opsForValue().set(userKey, user);
                return user;
           }

       }

       //更新用户
       public User updateUser(User user) {
           userMapper.updateUser(user);
           String userKey = "userId_" + userId;
           return (User) redisTemplate.opsForValue().get(userKey);
       }

       //删除用户
       public void deleteUser(Long userId){
           userMapper.deleteUserById(userId);
           String userKey = "userId_" + userId.getId();
           redisTemplate.delete(userKey);
       }
}

一般都会是这样写,非常容易理解,但是

  1. 代码不够优雅,每次读用户信息查询、新增、修改、删除,都需要调用缓存命令的API,产生很多重复代码
  2. 代码有侵入性 ,缓存操作和业务逻辑之间的代码耦合度高

侵入性主要体现如下两点:

  • 开发联调阶段,需要去掉缓存,只能注释或者临时删除缓存操作代码,也容易出错;

  • 某些场景下,需要更换缓存组件,每个缓存组件有自己的API,更换成本颇高

那么为了解决上述问题,Spring Cache提供了一种简单的方式来处理缓存,解决了手动管理缓存的复杂性

二、缓存注解

Spring Cache 提供了五个注解:

  • @Cacheable:标记在方法上,表示该方法的结果应该被缓存

根据方法的请求参数对其结果进行缓存,下次同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法

  • @CachePut :更新缓存中的值,通常用于更新缓存而不改变返回值

  • @CacheEvict:根据一定的条件删除缓存

  • @Caching:组合多个缓存注解

  • @CacheConfig:类级别共享缓存相关的公共配置

1、@Cacheable 缓存结果

@Cacheable 注解用于声明一个方法的结果是可缓存的

@Cacheable注解属性介绍

  • valuecacheNames: 指定缓存名称,可以是单个或多个

  • key: 指定缓存键的SpEL表达式。

  • condition: 指定缓存的条件SpEL表达式。

  • unless: 指定不缓存的条件SpEL表达式

使用 Spring Cache 的示例代码如下:

java 复制代码
    @Cacheable(value = "users", key = "#userId")
    public User getUser(String userId) {
        // 从数据库中获取用户信息
        return findUserById(userId);
    }

在这个示例中, getUser() 方法的返回值会被缓存,缓存的键是 userId 。每次调用这个方法时,都会检查缓存中是否存在对应的用户信息

多个缓存名称和条件:

java 复制代码
@Cacheable(value = {"users","userInfo"},   //缓存名称
                     key = "#userId"。     //缓存键,使用SpEL表达式
                     condition = "#userId.length() > 3",  // 缓存条件,只有当userId长度大于3时才缓存        
                     unless = "#result == null" // 除非条件,如果结果为null,则不缓存
           )
public User getUser(String userId) {
        // 从数据库中获取用户信息
        return findUserById(userId);
}

2、@CachePut 更新缓存

@CachePut 注解用于在方法执行后更新缓存,属性与 @Cacheable相同

java 复制代码
@CachePut(value = "users", key = "#user.id", condition = "#user.age > 18" )
public User updateUser(User user) {
        // 更新用户信息并缓存
        return updateUserInDatabase(user);
}

3、@CacheEvict 清除缓存

@CacheEvict 注解用于在方法执行后清除缓存

@CacheEvict 注解属性介绍

  • valuecacheNames: 指定缓存名称,可以是单个或多个。

  • allEntries: 清除所有缓存项。

  • condition: 指定清除缓存的条件SpEL表达式

java 复制代码
@CacheEvict(value = "users", key = "#userId")
public void deleteUser(String userId) {
        // 从数据库中删除用户信息
        deleteUserFromDatabase(userId);
}

4、@Caching 组合缓存(不常用)

@Caching有三个属性:cacheable、put和evict,分别用于指定@Cacheable , @CachePut , @CacheEvict 。对于一个数据的变动,更新多个缓存的场景

@Caching 注解用于组合多个缓存操作

java 复制代码
@Caching(
        cacheable = {@Cacheable(value = "userCache", key = "#userId")},
        put = {@CachePut(value = "userCache", key = "#user.id")},
        evict = { @CacheEvict(value = "archivedUsers", key = "#userId")}
)
public User updateUser(Long userId, String name) {
        return findUserByIdAndName(userId, name);
}

5、@CacheConfig 类级别缓存配置(不常用)

**@CacheConfig**注解用于在类级别提供缓存相关的共享配置

@CacheConfig注解说明

  • cacheNames: 指定类中所有缓存操作的默认缓存名称。

  • keyGenerator: 指定默认的缓存键生成器。

  • condition: 指定类中所有缓存操作的默认条件

java 复制代码
@CacheConfig(cacheNames = "userCache", keyGenerator = "customKeyGenerator")
public class UserService { 
    // 类中的方法可以使用缓存注解
}

6、@CacheResult 设置缓存超时(不常用)

@CacheResult 是一个非官方的注解,用于替代 @Cacheable,提供额外的配置选项,如超时时间

@CacheResult注解说明

  • cacheName: 缓存名称。

  • condition: 缓存条件。

  • timeout: 缓存超时时间(毫秒)

java 复制代码
@CacheResult(cacheName = "users", timeout = 3600000)  设置超时时间为1小时
public User getUserById(String userId) {
        // 从数据库中获取用户信息
        return findUserById(userId);
}

三、使用方式

1、**@EnableCaching**开启缓存

在主配置类或启动类上添加 **@EnableCaching **注解来启用缓存功能

java 复制代码
@EnableCaching
public class UserApplication{
    ......
}

2、在方法上添加注解**@Cacheable**等注解

因此对上面例子🌰使用SpringCache,代码示例如下

java 复制代码
@Service
public class UserService {

    @Cacheable(value = "users", key = "#userId")
    public User getUserById(String userId) {
        // 从数据库中获取用户信息
        return findUserById(userId);
    }

    @CachePut(value = "users", key = "#user.id", condition = "#user.age > 18" )
    public User updateUser(User user) {
        // 更新用户信息并缓存
        return updateUserInDatabase(user);
    }

    @CacheEvict(value = "users", key = "#userId")
    public void deleteUser(String userId) {
        // 从数据库中删除用户信息
        deleteUserFromDatabase(userId);
    }
}

在这个示例中, @Cacheable注解用于缓存 getUser 方法的结果,@CachePut 用于更新用户信息并缓存,@CacheEvict 用于删除缓存中的用户信息

通过这些注解,可以轻松管理缓存,而不需要直接使用 RedisTemplate

总之,Spring Cache 通过提供高层次的抽象和简化的 API,解决了手动管理缓存所带来的复杂性,使得开发者能够更专注于业务逻辑的实现

四 、优劣势

Spring Cache相对于手动使用RedisTemplate的一些优势和劣势的问题

优势

  1. 简化代码 :使用 Spring Cache,您可以通过注解(如 @Cacheable , @CachePut , @CacheEvict )来声明缓存,而不需要手动编写缓存的逻辑。这样可以减少样板代码,提高代码的可读性和可维护性
  2. 统一的缓存抽象Spring Cache 提供了一个统一的 API,可以与多种缓存解决方案(如 Redis, Ehcache, Caffeine 等)集成。这样,可以在不同的缓存实现之间切换,而不需要修改业务逻辑代码
  3. 自动化处理 :Spring Cache 可以自动处理缓存的创建、更新和失效。例如,当方法被调用时,Spring Cache 会自动检查缓存中是否存在结果,如果存在则直接返回,不再执行方法
  4. 灵活配置:可以通过配置文件或注解轻松地配置缓存的行为,例如设置缓存的过期时间、最大大小等
  5. 支持方法级别的缓存:通过注解,可以对特定的方法进行缓存,而不需要在整个类中实现缓存逻辑,这样更加灵活

劣势

  • 抽象层次高:隐藏了底层缓存实现的复杂性。这可能导致开发者对缓存的工作原理不够了解,从而在调试和优化时遇到困难
  • 缓存一致性问题:当数据更新时,缓存可能不会立即失效,导致数据不一致。在分布式系统中,确保缓存和数据库之间的一致性是一个挑战

关于缓存一致性问题可以看这篇:Redis与MySQL数据一致性问题的策略模式及解决方案_mysql redis 数据一致性 实现方案-CSDN博客

  • 复杂的配置:对于复杂的缓存策略(例如,基于时间的过期策略、缓存清理等),可能需要额外的配置和代码,增加了实现的复杂性
  • 性能开销:在某些情况下,Spring Cache 的抽象层可能会引入额外的性能开销,尤其是在高并发环境下,可能会对性能产生影响

五、总结

Spring缓存机制通过**@EnableCaching** 开启,配合 **@Cacheable , @CachePut , @CacheEvict**等注解,为Java应用提供了一种声明式管理缓存的方式。这些注解使得缓存配置变得简洁明了,允许开发者轻松实现数据的自动缓存、更新和清除,从而优化应用性能,减少不必要的计算和数据访问开销


推荐文章:使用 Spring Cache 实现缓存,这种方式才叫优雅!

相关推荐
初晴~23 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱5813628 分钟前
InnoDB 的页分裂和页合并
数据库·后端
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala
黑胡子大叔的小屋1 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark1 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
星就前端叭2 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
雷神乐乐2 小时前
Spring学习(一)——Sping-XML
java·学习·spring
小林coding3 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
AI理性派思考者3 小时前
【保姆教程】手把手教你在Linux系统搭建早期alpha项目cysic的验证者&证明者
后端·github·gpu