Spring Cacheable 注解

Spring Cacheable 注解

在Spring框架中,缓存是一种提高应用程序性能的重要技术手段。@Cacheable注解是Spring Cache中最常用的注解之一,它用于将方法的返回值缓存起来,以便后续调用时直接从缓存中获取,而不是再次执行方法。本文将详细介绍@Cacheable注解的使用方法及其工作原理。

什么是 @Cacheable 注解

@Cacheable注解是Spring提供的一种缓存注解,它可以应用于方法上,用于标记该方法的返回值需要缓存。每次调用该方法时,Spring会首先检查缓存中是否存在该方法的返回值,如果存在则直接返回缓存中的值,否则执行该方法并将其返回值存入缓存中。

@Cacheable 注解的基本使用

引入依赖

在使用@Cacheable之前,需要在项目中引入相关的Spring Cache依赖。对于Spring Boot项目,可以在pom.xml中添加以下依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

配置缓存

接下来,需要在Spring Boot应用程序中启用缓存支持,并配置缓存管理器。例如,可以在application.properties中配置缓存:

properties 复制代码
spring.cache.type=caffeine
spring.cache.cache-names=exampleCache
spring.cache.caffeine.spec=maximumSize=100,expireAfterAccess=600s

在主应用程序类中启用缓存:

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

使用 @Cacheable 注解

在需要缓存的方法上使用@Cacheable注解,例如:

java 复制代码
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class ExampleService {

    @Cacheable("exampleCache")
    public String getData(String param) {
        // 模拟耗时操作
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Data for " + param;
    }
}

当第一次调用getData方法时,会执行实际的业务逻辑并将结果缓存起来。再次调用时,将直接从缓存中获取数据,而不再执行耗时的操作。

@Cacheable 注解的高级用法

指定缓存的Key

默认情况下,@Cacheable会使用方法参数作为缓存的Key。可以通过key属性指定自定义的Key:

java 复制代码
@Cacheable(value = "exampleCache", key = "#param")
public String getData(String param) {
    // ...
}

条件缓存

可以使用condition属性指定条件,满足条件时才进行缓存:

java 复制代码
@Cacheable(value = "exampleCache", condition = "#param.length() > 3")
public String getData(String param) {
    // ...
}

缓存同步

在并发环境中,可以使用sync属性启用缓存同步,防止缓存击穿:

java 复制代码
@Cacheable(value = "exampleCache", sync = true)
public String getData(String param) {
    // 模拟耗时操作
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Data for " + param;
}

当多个线程同时请求缓存中没有的数据时,只有一个线程会执行方法,其他线程等待结果。这种机制能够有效避免多个线程同时执行相同的方法,从而减少资源消耗,提高系统性能。

例如,当大量并发请求同时查询某个缓存中没有的数据时,如果没有同步机制,每个请求都会触发一次方法执行,这不仅浪费资源,还可能导致数据库或其他底层服务过载。而启用sync属性后,只有一个请求会执行方法,其他请求等待结果,执行完毕后,结果会被缓存,后续请求直接从缓存中获取数据。

自定义的Key生成器keyGenerator

keyGenerator参数用于指定一个自定义的Key生成器,覆盖默认的Key生成方式。需要实现org.springframework.cache.interceptor.KeyGenerator接口,并在配置类或服务类中进行注册。

自定义Key生成器示例:

java 复制代码
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Component("customKeyGenerator")
public class CustomKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object target, Method method, Object... params) {
        return method.getName() + "_" + Arrays.toString(params);
    }
}

在使用@Cacheable时指定自定义Key生成器:

java 复制代码
@Cacheable(value = "exampleCache", keyGenerator = "customKeyGenerator")
public String getData(String param) {
    // ...
}

详细介绍 cacheManager

cacheManager参数用于指定一个自定义的缓存管理器。如果没有指定,使用默认的缓存管理器。缓存管理器负责管理应用程序中的所有缓存实例。

自定义缓存管理器示例:

java 复制代码
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager customCacheManager() {
        return new ConcurrentMapCacheManager("exampleCache");
    }
}

在使用@Cacheable时指定自定义缓存管理器:

java 复制代码
@Cacheable(value = "exampleCache", cacheManager = "customCacheManager")
public String getData(String param) {
    // ...
}

自定义的缓存解析器cacheResolver

cacheResolver参数用于指定一个自定义的缓存解析器。需要实现org.springframework.cache.interceptor.CacheResolver接口,缓存解析器负责根据某些逻辑来解析和提供缓存实例。

自定义缓存解析器示例:

java 复制代码
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Collections;

@Component("customCacheResolver")
public class CustomCacheResolver implements CacheResolver {

    private final CacheManager cacheManager;

    public CustomCacheResolver(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    @Override
    public Collection<? extends Cache> resolveCaches(org.springframework.cache.interceptor.CacheOperationInvocationContext<?> context) {
        // 自定义缓存解析逻辑
        return Collections.singleton(cacheManager.getCache("exampleCache"));
    }
}

在使用@Cacheable时指定自定义缓存解析器:

java 复制代码
@Cacheable(value = "exampleCache", cacheResolver = "customCacheResolver")
public String getData(String param) {
    // ...
}

unless

unless参数是一个SpEL表达式,只有当表达式为false时才缓存方法的返回值。它的优先级高于condition,可以用于在某些条件下禁用缓存。

例如,可以在结果长度大于10时不缓存:

java 复制代码
@Cacheable(value = "exampleCache", unless = "#result.length() > 10")
public String getData(String param) {
    // ...
}

通过unless参数,可以在不改变方法逻辑的情况下,灵活控制缓存行为,避免不必要的缓存操作,提高系统性能。

缓存一致性问题

在使用缓存时,一个常见的问题是缓存一致性问题。当数据源(如数据库)中的数据发生变化时,缓存中的数据可能不会及时更新,导致缓存中的数据与实际数据不一致。为了解决这个问题,可以使用以下几种策略:

  1. 缓存失效策略:在更新或删除数据时,同时让相关缓存失效。
  2. 缓存刷新:定期刷新缓存,确保缓存中的数据是最新的。
  3. 消息队列:使用消息队列,在数据变化时通知缓存进行更新。

例如,在数据更新时手动清除缓存:

java 复制代码
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;

@Service
public class ExampleService {

    @CacheEvict(value = "exampleCache", key = "#param")
    public void updateData(String param, String newData) {
        // 更新数据库中的数据
    }
}

参考链接

  1. Spring Cache 官方文档
  2. Spring Boot Cache 官方文档
  3. Caffeine 缓存库
  4. SpEL 表达式官方文档
相关推荐
只因在人海中多看了你一眼2 小时前
分布式缓存 + 数据存储 + 消息队列知识体系
分布式·缓存
zhixingheyi_tian4 小时前
Spark 之 Aggregate
大数据·分布式·spark
求积分不加C6 小时前
-bash: ./kafka-topics.sh: No such file or directory--解决方案
分布式·kafka
nathan05296 小时前
javaer快速上手kafka
分布式·kafka
谭震鸿9 小时前
Zookeeper集群搭建Centos环境下
分布式·zookeeper·centos
天冬忘忧14 小时前
Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化
大数据·分布式·kafka
IT枫斗者19 小时前
如何解决Java EasyExcel 导出报内存溢出
java·服务器·开发语言·网络·分布式·物联网
求积分不加C19 小时前
Kafka怎么发送JAVA对象并在消费者端解析出JAVA对象--示例
java·分布式·kafka·linq
GDDGHS_20 小时前
“Kafka面试攻略:核心问题与高效回答”
分布式·面试·kafka
꧁薄暮꧂21 小时前
kafka中的数据清理策略
数据库·分布式·kafka