您现在的位置是:首页 >学无止境 >26、深入理解 Spring 的 @Cacheable 注解:提升系统性能的利器网站首页学无止境

26、深入理解 Spring 的 @Cacheable 注解:提升系统性能的利器

翻晒时光 2025-02-17 00:01:02
简介26、深入理解 Spring 的 @Cacheable 注解:提升系统性能的利器

什么是 @Cacheable 注解

@Cacheable 是 Spring 框架中专门用于缓存方法返回值的注解。当一个被 @Cacheable 注解标注的方法被调用时,Spring 会首先对缓存进行检查,查看是否已经存在该方法调用所对应的结果。如果缓存中存在相应结果,Spring 会直接从缓存中获取并返回,而不会执行方法体中的代码;如果缓存中不存在该结果,Spring 则会执行方法体,并将方法的返回值存入缓存,以便后续相同的方法调用能够直接从缓存中获取结果。

这种缓存机制在实际应用中具有显著的优势。以电商系统为例,商品信息的查询操作可能会被频繁调用。如果每次查询都直接从数据库中获取数据,不仅会增加数据库的压力,还会导致查询时间变长。而使用 @Cacheable 注解,将商品信息缓存起来,下次查询时直接从缓存中获取,能够大大提高查询效率,减少用户等待时间。

基本使用方法

1. 配置缓存管理器

在使用 @Cacheable 注解之前,我们需要配置一个合适的缓存管理器。Spring 框架支持多种不同类型的缓存管理器,每种管理器都有其独特的特点和适用场景。下面我们将详细介绍几种常见的缓存管理器及其配置方法。

ConcurrentMapCacheManager

ConcurrentMapCacheManager 是一个基于 ConcurrentHashMap 的简单缓存管理器,它适用于开发和测试环境,或者对缓存要求不高的场景。在 Spring Boot 项目中,我们可以通过以下配置类来使用它:

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        return new ConcurrentMapCacheManager("myCache");
    }
}

在上述代码中,@EnableCaching 注解用于启用 Spring 的缓存功能,ConcurrentMapCacheManager 构造函数中的 "myCache" 是缓存的名称。

EhCacheCacheManager

EhCache 是一个开源的、高性能的 Java 缓存框架,EhCacheCacheManager 则是 Spring 对 EhCache 的集成。使用 EhCache 可以提供更丰富的缓存配置选项,如缓存过期策略、缓存大小限制等。以下是配置 EhCacheCacheManager 的示例:
首先,需要在 pom.xml 中添加 EhCache 的依赖:

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.6</version>
</dependency>

然后,创建 ehcache.xml 配置文件,示例内容如下:

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="100000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"/>
    <cache name="myCache"
           maxEntriesLocalHeap="1000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="false"/>
</ehcache>

最后,在配置类中配置 EhCacheCacheManager

import net.sf.ehcache.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;

import java.io.InputStream;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public EhCacheCacheManager cacheManager() {
        InputStream inputStream = getClass().getResourceAsStream("/ehcache.xml");
        CacheManager ehCacheManager = CacheManager.create(inputStream);
        return new EhCacheCacheManager(ehCacheManager);
    }
}
RedisCacheManager

Redis 是一个开源的、高性能的键值对存储数据库,RedisCacheManager 是 Spring 对 Redis 的集成。使用 Redis 作为缓存可以实现分布式缓存,适用于多节点的应用场景。以下是配置 RedisCacheManager 的示例:
首先,在 pom.xml 中添加 Redis 的依赖:

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

然后,在 application.properties 中配置 Redis 连接信息:

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

最后,在配置类中配置 RedisCacheManager

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
               .entryTtl(Duration.ofMinutes(10))
               .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
               .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(redisConnectionFactory)
               .cacheDefaults(cacheConfiguration)
               .build();
    }
}

2. 使用 @Cacheable 注解

在配置好缓存管理器之后,我们就可以在需要缓存结果的方法上添加 @Cacheable 注解了。示例如下:

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

@Service
public class ProductService {

    @Cacheable("myCache")
    public Product getProductById(Long id) {
        // 模拟从数据库中查询商品信息
        System.out.println("Querying product from database with id: " + id);
        return new Product(id, "Product Name");
    }
}

在上述代码中,@Cacheable("myCache") 表示将 getProductById 方法的返回值缓存到名为 "myCache" 的缓存中。当多次调用该方法并传入相同的 id 时,只有第一次会执行方法体中的代码,后续调用会直接从缓存中获取结果。

@Cacheable 注解的属性

@Cacheable 注解有多个属性,通过合理配置这些属性,我们可以根据不同的业务需求实现更加灵活和高效的缓存策略。

1. value 或 cacheNames

这两个属性用于指定缓存的名称,可以是一个或多个。例如:

@Cacheable(value = {"cache1", "cache2"})
public Product getProduct(Long id) {
    // ...
}

2. key

该属性用于指定缓存的键,默认情况下,Spring 会根据方法的参数自动生成键。我们可以使用 SpEL(Spring Expression Language)表达式自定义键,例如:

@Cacheable(value = "myCache", key = "#id")
public Product getProductById(Long id) {
    // ...
}

上述代码中,#id 表示使用方法的 id 参数作为缓存的键。

3. condition

此属性用于指定缓存的条件,只有当条件为 true 时才会进行缓存。例如:

@Cacheable(value = "myCache", condition = "#id > 0")
public Product getProductById(Long id) {
    // ...
}

上述代码中,只有当 id 大于 0 时才会将方法的返回值缓存起来。

4. unless

该属性用于指定不进行缓存的条件,当条件为 true 时,不会将方法的返回值缓存起来。例如:

@Cacheable(value = "myCache", unless = "#result == null")
public Product getProductById(Long id) {
    // ...
}

上述代码中,如果方法的返回值为 null,则不会将其缓存。

高级应用技巧

1. 多级缓存

在实际应用中,我们可以结合不同的缓存管理器实现多级缓存。例如,将本地缓存(如 ConcurrentMapCacheManager)和分布式缓存(如 RedisCacheManager)结合使用。先从本地缓存中查找数据,如果找不到再从分布式缓存中查找,最后再从数据库中获取数据。这样可以进一步提高系统的性能和响应速度。以下是一个简单的多级缓存示例:

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

@Service
public class ProductService {

    @Cacheable(value = {"localCache", "redisCache"}, key = "#id")
    public Product getProductById(Long id) {
        // 模拟从数据库中查询商品信息
        System.out.println("Querying product from database with id: " + id);
        return new Product(id, "Product Name");
    }
}

2. 缓存更新和删除

@Cacheable 注解主要用于缓存方法的返回值,对于缓存的更新和删除,我们可以使用 @CachePut 和 @CacheEvict 注解。@CachePut 注解用于更新缓存,无论缓存中是否已经存在该键,都会执行方法体并将返回值存入缓存中;@CacheEvict 注解用于删除缓存,可以指定删除某个或多个缓存项。示例如下:

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

@Service
public class ProductService {

    @Cacheable("myCache")
    public Product getProductById(Long id) {
        // 模拟从数据库中查询商品信息
        System.out.println("Querying product from database with id: " + id);
        return new Product(id, "Product Name");
    }

    @CachePut("myCache")
    public Product updateProduct(Product product) {
        // 模拟更新数据库中的商品信息
        System.out.println("Updating product in database with id: " + product.getId());
        return product;
    }

    @CacheEvict("myCache")
    public void deleteProduct(Long id) {
        // 模拟从数据库中删除商品信息
        System.out.println("Deleting product from database with id: " + id);
    }
}

总结

@Cacheable 注解是 Spring 框架中一个非常实用的功能,它为我们提供了一种简单而高效的方式来实现方法结果的缓存,从而减少重复计算和数据库查询,提高系统的性能和响应速度。通过合理配置 @Cacheable 注解的属性,我们可以根据不同的业务需求实现更加灵活和高效的缓存策略。同时,结合不同类型的缓存管理器(如 ConcurrentMapCacheManagerEhCacheCacheManager 和 RedisCacheManager),我们可以满足不同场景下的缓存需求。此外,利用 @CachePut 和 @CacheEvict 注解,我们可以实现缓存的更新和删除操作,确保缓存数据的一致性。在实际开发中,我们应该根据系统的特点和性能需求,合理使用 @Cacheable 注解及其相关功能,为系统的性能优化提供有力支持。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。