一篇文章搞定Spring Cache

2023-11-28  本文已影响0人  刘彦青

Spring Cache 提供了一种灵活而强大的缓存抽象,使得在应用中集成缓存变得更加容易。让我们从一些基本概念开始,然后深入讨论 Spring Cache 的用法和配置。

基本概念

  1. 缓存注解: Spring Cache 提供了一组注解,用于在方法级别声明缓存行为。其中包括 @Cacheable@CachePut@CacheEvict 等。这些注解允许你控制方法的缓存行为,如何从缓存中读取、写入和清除数据。
  2. CacheManager: CacheManager 是 Spring Cache 的核心接口,负责创建和管理缓存实例。Spring Cache 提供了默认的 ConcurrentMapCacheManager,同时也支持集成其他缓存提供者,如 Ehcache、Caffeine、Redis 等。
  3. 缓存键(Key): 缓存中的每个条目都有一个唯一的键,用于在缓存中标识数据。在 Spring Cache 中,键可以使用 SpEL 表达式构建,以根据方法参数、返回值等动态生成。

基本使用示例

让我们通过一个简单的示例来演示 Spring Cache 的基本使用。假设有一个服务类,其中包含一个根据用户ID获取用户信息的方法,我们希望将用户信息缓存起来:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Cacheable(value = "userCache", key = "#userId")
    public String getUserInfo(String userId) {
        // 模拟从数据库或其他数据源获取用户信息的操作
        System.out.println("Fetching user info for userId: " + userId);
        return "User info for userId: " + userId;
    }
}

在上面的示例中,@Cacheable 注解表示 getUserInfo 方法的结果应该被缓存,缓存的名称为 "userCache",键为方法的参数 userId。如果多次调用 getUserInfo 方法并传递相同的 userId,则只有第一次会执行方法体,后续的调用将直接从缓存中获取结果。

配置缓存管理器

在Spring中,配置常见的缓存管理器通常涉及使用Spring Cache抽象,并选择适当的缓存提供者。以下是一些常见的缓存管理器配置示例,包括Ehcache、Caffeine、Redis等。

1. 使用 Ehcache 作为缓存管理器

首先,确保项目中引入了Ehcache的相关依赖:

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

然后,在Spring Boot的配置文件中添加Ehcache的配置,比如ehcache.xml

<!-- src/main/resources/ehcache.xml -->

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd">
    <defaultCache
        maxElementsInMemory="100"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="false"
        />
    <!-- 其他缓存配置 -->
</ehcache>

然后,在Spring Boot的配置类中配置Ehcache的CacheManager

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        return new EhCacheCacheManager(ehCacheCacheManager().getObject());
    }

    @Bean
    public EhCacheManagerFactoryBean ehCacheCacheManager() {
        EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
        factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        factoryBean.setShared(true);
        return factoryBean;
    }
}

2. 使用 Caffeine 作为缓存管理器

添加以下依赖:

<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 项目的配置类中配置 Caffeine Cache 作为缓存管理器:

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeineCacheBuilder());
        return cacheManager;
    }

    private Caffeine<Object, Object> caffeineCacheBuilder() {
        return Caffeine.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存失效时间为10分钟
                .maximumSize(100); // 设置缓存的最大条目数
    }
}

在上述配置中,CaffeineCacheManager 被配置为缓存管理器,通过 caffeineCacheBuilder 方法配置了 Caffeine Cache 的一些属性,比如失效时间和最大条目数。现在,你可以在服务类中使用 @Cacheable 注解来声明缓存:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Cacheable(value = "userCache", key = "#userId")
    public String getUserInfo(String userId) {
        // 模拟从数据库或其他数据源获取用户信息的操作
        System.out.println("Fetching user info for userId: " + userId);
        return "User info for userId: " + userId;
    }
}

3. 使用 Redis 作为缓存管理器

当使用 Redis 作为缓存管理器时,首先确保项目中引入了 Redis 相关的依赖。在 Maven 中,可以添加以下依赖:

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

接下来,在 Spring Boot 项目的配置文件中配置 Redis 连接信息:

# src/main/resources/application.properties

spring.redis.host=localhost
spring.redis.port=6379

然后,在 Spring Boot 项目的配置类中配置 Redis Cache 作为缓存管理器:

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return RedisCacheManager.builder(redisConnectionFactory)
                .build();
    }
}

在这个配置中,RedisCacheManager 被配置为缓存管理器,并使用了 RedisConnectionFactory 连接到 Redis 服务器。现在,你可以在服务类中使用 @Cacheable 注解来声明缓存:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Cacheable(value = "userCache", key = "#userId")
    public String getUserInfo(String userId) {
        // 模拟从数据库或其他数据源获取用户信息的操作
        System.out.println("Fetching user info for userId: " + userId);
        return "User info for userId: " + userId;
    }
}

4.使用Guava Cache 作为缓存容器时

添加以下依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version> <!-- 请根据最新版本调整 -->
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

然后,可以在 Spring Boot 项目的配置类中配置 Guava Cache 作为缓存管理器:

import com.google.common.cache.CacheBuilder;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.guava.GuavaCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        GuavaCacheManager cacheManager = new GuavaCacheManager();
        cacheManager.setCacheBuilder(guavaCacheBuilder());
        return cacheManager;
    }

    private CacheBuilder<Object, Object> guavaCacheBuilder() {
        return CacheBuilder.newBuilder()
                .expireAfterWrite(10, TimeUnit.MINUTES) // 设置缓存失效时间为10分钟
                .maximumSize(100); // 设置缓存的最大条目数
    }
}

@Service
public class MyService {

    @Cacheable(value = "userCache", key = "#userId")
    public String getUserInfo(String userId) {
        // 模拟从数据库或其他数据源获取用户信息的操作
        System.out.println("Fetching user info for userId: " + userId);
        return "User info for userId: " + userId;
    }
}

在上述配置中,GuavaCacheManager 被配置为缓存管理器,通过 guavaCacheBuilder 方法配置了 Guava Cache 的一些属性,比如失效时间和最大条目数。这样,你就可以在 Spring Boot 项目中使用 Guava Cache 作为缓存容器了。在实际应用中,可以根据具体的业务需求调整 Guava Cache 的配置参数。

5.使用多种缓存管理器

在 Spring 中,一个项目中可以有多个 CacheManager,并且可以在不同的地方使用不同的 CacheManager 实例。这种情况下,每个 CacheManager 可以配置不同的缓存提供者,例如使用 Ehcache、Guava Cache 或者 Redis 等。

在实际项目中,有时候需要使用多个 CacheManager 的场景可能包括:

  1. 多种缓存需求: 不同的业务模块可能有不同的缓存需求,使用不同的 CacheManager 可以更好地满足各自的要求。
  2. 本地缓存与分布式缓存: 有些数据可能适合存储在本地缓存中,而有些可能需要存储在分布式缓存中。这时可以使用两个不同的 CacheManager 分别配置本地缓存和分布式缓存。
  3. 缓存过期策略: 不同的缓存数据可能需要不同的过期策略,使用不同的 CacheManager 可以灵活配置每个缓存的过期时间。

在配置多个 CacheManager 时,需要确保为它们分配唯一的名称,以便在使用时能够正确地引用。例如:

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager localCacheManager() {
        // 配置本地缓存管理器
        return new ConcurrentMapCacheManager("localCache");
    }

    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 配置使用 Redis 的缓存管理器
        return RedisCacheManager.builder(redisConnectionFactory).build();
    }
}

在上述示例中,通过 localCacheManagerredisCacheManager 分别配置了本地缓存管理器和 Redis 缓存管理器。可以在需要的地方使用 @Cacheable 注解时指定使用哪个缓存管理器,例如:

@Service
public class MyService {

    @Cacheable(value = "localCache", key = "#userId", cacheManager = "localCacheManager")
    public String getLocalUserInfo(String userId) {
        // 从本地缓存获取用户信息
        // ...
    }

    @Cacheable(value = "redisCache", key = "#userId", cacheManager = "redisCacheManager")
    public String getRedisUserInfo(String userId) {
        // 从 Redis 缓存获取用户信息
        // ...
    }
}

总的来说,虽然一个项目可以有多个 CacheManager,但需要根据具体需求和业务场景来决定是否需要配置多个 CacheManager,以及如何使用它们。

常见的缓存管理器的区别以及怎么选型

常见的缓存管理器有不同的实现,如 Ehcache、Caffeine、Guava Cache、Redis 等,它们之间有一些区别,选型时需要考虑项目的需求和特点。以下是一些常见缓存管理器的区别和选型考虑:

  1. Ehcache:
  1. Caffeine:
  1. Guava Cache:
  1. Redis:
选型考虑因素:
  1. 项目规模: 对于小规模的单节点应用,本地缓存(如 Ehcache、Caffeine、Guava Cache)可能已经足够。对于大规模分布式系统,可能需要选择分布式缓存(如 Redis)。
  2. 性能需求: 如果追求高性能、低延迟,可以考虑使用 Caffeine 或 Guava Cache。它们都是本地缓存,具有快速的读写性能。
  3. 分布式需求: 如果应用部署在多个节点上,需要考虑使用支持分布式的缓存管理器,比如 Redis。Ehcache 的分布式版本也是一个选择。
  4. 数据持久性: 如果需要在系统重启后保留缓存数据,可以选择 Redis,因为它支持数据的持久化。
  5. 数据复杂性: 如果缓存的数据结构较为简单,且只需要基本的过期策略和最大条目数配置,本地缓存可能更轻量。如果需要更丰富的功能,如监听缓存变化、异步加载等,可以选择 Guava Cache。
  6. 系统架构: 考虑项目的整体架构和技术栈。如果已经在项目中使用了某个缓存管理器,且满足需求,可以继续使用,避免引入额外的复杂性。

常用注解

  1. **@Cacheable**
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
    // 方法体逻辑
}
  1. **@CachePut**
@CachePut(value = "userCache", key = "#userId")
public String updateUserInfo(String userId, String newInfo) {
    // 方法体逻辑
}
  1. **@CacheEvict**
@CacheEvict(value = "userCache", key = "#userId")
public void clearUserInfoCache(String userId) {
    // 方法体逻辑
}
  1. **@Caching**
@Caching(
    cacheable = {
        @Cacheable(value = "userCache", key = "#userId"),
        @Cacheable(value = "emailCache", key = "#email")
    },
    put = {
        @CachePut(value = "userCache", key = "#userId"),
        @CachePut(value = "emailCache", key = "#email")
    }
)
public String getUserInfoByEmailOrId(String userId, String email) {
    // 方法体逻辑
}

这些注解可以与 SpEL 表达式一起使用,以动态生成缓存键。通过在方法上使用这些注解,可以方便地配置和管理缓存,而无需手动编写缓存逻辑。要使用这些注解,你需要在配置类上添加 @EnableCaching 注解,以启用 Spring Cache 功能。

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
    // 配置相关的 CacheManager 等
}

这只是 Spring Cache 的一小部分功能,你可以根据具体的需求和业务场景选择合适的注解来实现缓存。在实际使用中,还可以配置一些高级功能,比如缓存的失效策略、条件缓存、缓存的自定义过期时间等。

缓存键的多种写法

在 Spring Cache 中,key 属性是用于指定缓存键的关键属性,它是一个 SpEL(Spring Expression Language)表达式。这意味着你可以使用 SpEL 表达式动态地构建缓存键。以下是一些常见的 key 属性的写法:

  1. 基本写法:
@Cacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
    // 方法体逻辑
}
  1. 多个参数:
@Cacheable(value = "userCache", key = "#userId + '-' + #userName")
public String getUserInfo(String userId, String userName) {
    // 方法体逻辑
}
  1. SpEL 表达式:
@Cacheable(value = "userCache", key = "#userId.concat('-').concat(#userName.toUpperCase())")
public String getUserInfo(String userId, String userName) {
    // 方法体逻辑
}
  1. 返回值作为键:
@Cacheable(value = "userCache", key = "#result.userId")
public UserInfo getUserInfo(String userId) {
    // 方法体逻辑
}
  1. 自定义 SpEL 函数:
@Cacheable(value = "userCache", key = "#myUtil.generateKey(#userId)")
public String getUserInfo(String userId) {
    // 方法体逻辑
}

在这个例子中,假设你已经在项目中定义了 myUtil 这个工具类,并在该类中有一个 generateKey 方法。

高级功能简单介绍

  1. 缓存失效策略:
@Cacheable(value = "userCache", key = "#userId", condition = "#result != null && #result.isValid()")
public UserInfo getUserInfo(String userId) {
    // 方法体逻辑
}
  1. 条件缓存:
@Cacheable(value = "userCache", key = "#userId", unless = "#result.isDisabled()")
public UserInfo getUserInfo(String userId) {
    // 方法体逻辑
}
  1. 自定义缓存管理器:
@Configuration
@EnableCaching
public class CustomCacheConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager() {
        // 自定义的缓存管理器
        return new MyCustomCacheManager();
    }
}
  1. 自定义缓存注解:
@MyCustomCacheable(value = "userCache", key = "#userId")
public String getUserInfo(String userId) {
    // 方法体逻辑
}

其中 @MyCustomCacheable 是一个自定义的缓存注解,你需要在配置类中注册它,并实现相应的缓存逻辑。

  1. 缓存的自定义过期时间:
@Cacheable(value = "userCache", key = "#userId", expire = 600) // 过期时间为 600 秒
public UserInfo getUserInfo(String userId) {
    // 方法体逻辑
}
上一篇下一篇

猜你喜欢

热点阅读