首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring缓存(一):注解指南

Spring缓存(一):注解指南

原创
作者头像
Yeats_Liao
发布2025-09-14 15:29:52
发布2025-09-14 15:29:52
1810
举报

1. @Cacheable注解详解

1.1 基本使用方式

@Cacheable是Spring缓存体系中最常用的注解,它的工作原理很简单:方法第一次执行时会将结果存入缓存,后续相同参数的调用直接从缓存返回数据。

这种机制特别适合那些计算成本高或者数据变化不频繁的场景。

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

@Service
public class UserService {
    @Cacheable(cacheNames = "userCache")
    public User getUserById(Long id) {
        // 模拟数据库查询操作
        System.out.println("从数据库查询用户: " + id);
        return userRepository.findById(id).orElse(null);
    }
}

上面这个例子中,当我们第一次调用getUserById(1L)时,会打印日志并执行数据库查询。

但第二次调用相同参数时,方法体不会执行,直接返回缓存中的User对象。

1.2 核心参数配置

cacheNames参数

这是必填参数,用来指定缓存的存储区域。你可以把它理解为缓存的"房间号",不同的业务数据可以存放在不同的房间里。

代码语言:java
复制
@Cacheable(cacheNames = {"userCache", "memberCache"})
public User getUser(Long id) {
    return userRepository.findById(id).orElse(null);
}

key参数

用来自定义缓存的键值,支持SpEL表达式。如果不指定,Spring会根据方法参数自动生成。

代码语言:java
复制
@Cacheable(cacheNames = "userCache", key = "#id")
public User getUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

@Cacheable(cacheNames = "userCache", key = "#user.id + '_' + #user.name")
public User getUserByInfo(User user) {
    return userRepository.findByIdAndName(user.getId(), user.getName());
}

condition参数

这个参数让你可以控制什么时候才启用缓存,只有当条件为true时才会缓存结果。

代码语言:java
复制
@Cacheable(cacheNames = "userCache", condition = "#id > 0")
public User getUserById(Long id) {
    return userRepository.findById(id).orElse(null);
}

2. @CachePut注解应用

2.1 更新缓存机制

@CachePut和@Cacheable最大的区别是:它每次都会执行方法体,然后将结果更新到缓存中。

这个特性让它特别适合用在数据更新的场景。

代码语言:java
复制
@Service
public class UserService {
    @CachePut(cacheNames = "userCache", key = "#user.id")
    public User updateUser(User user) {
        System.out.println("更新用户信息: " + user.getId());
        User savedUser = userRepository.save(user);
        return savedUser;
    }
}

2.2 实际业务场景

假设我们有一个商品管理系统,当商品信息发生变化时,需要同时更新数据库和缓存。

代码语言:java
复制
@Service
public class ProductService {
    
    @Cacheable(cacheNames = "productCache", key = "#id")
    public Product getProduct(Long id) {
        return productRepository.findById(id).orElse(null);
    }
    
    @CachePut(cacheNames = "productCache", key = "#product.id")
    public Product updateProduct(Product product) {
        // 更新商品价格
        product.setUpdateTime(LocalDateTime.now());
        return productRepository.save(product);
    }
}

这样设计的好处是,当商品信息更新后,缓存中的数据也会立即更新,保证了数据的一致性。

3. @CacheEvict注解详解

3.1 缓存清理策略

@CacheEvict专门用来清理缓存,当数据被删除或者失效时,对应的缓存也应该被清除。

代码语言:java
复制
@Service
public class UserService {
    
    @CacheEvict(cacheNames = "userCache", key = "#id")
    public void deleteUser(Long id) {
        System.out.println("删除用户: " + id);
        userRepository.deleteById(id);
    }
}

3.2 批量清理操作

有时候我们需要清空整个缓存区域,比如系统维护或者数据大批量更新的时候。

代码语言:java
复制
@Service
public class UserService {
    
    @CacheEvict(cacheNames = "userCache", allEntries = true)
    public void clearAllUsers() {
        System.out.println("清空所有用户缓存");
        // 执行批量数据清理逻辑
    }
    
    @CacheEvict(cacheNames = "userCache", key = "#id", beforeInvocation = true)
    public void deleteUserWithRisk(Long id) {
        // 这个方法可能会抛异常,但我们希望无论如何都清除缓存
        if (id == null) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        userRepository.deleteById(id);
    }
}

beforeInvocation参数很有用,当设置为true时,即使方法执行失败,缓存也会被清除。

4. @Caching组合注解

4.1 复杂缓存操作

在实际项目中,有时候一个方法需要同时进行多种缓存操作。@Caching注解就是为了解决这个问题。

代码语言:java
复制
@Service
public class OrderService {
    
    @Caching(
        cacheable = {
            @Cacheable(cacheNames = "orderCache", key = "#orderId")
        },
        put = {
            @CachePut(cacheNames = "userOrderCache", key = "#result.userId + '_latest'")
        },
        evict = {
            @CacheEvict(cacheNames = "orderStatCache", allEntries = true)
        }
    )
    public Order processOrder(Long orderId) {
        Order order = orderRepository.findById(orderId).orElse(null);
        if (order != null) {
            order.setStatus("PROCESSED");
            order.setProcessTime(LocalDateTime.now());
            orderRepository.save(order);
        }
        return order;
    }
}

这个例子展示了一个订单处理方法,它同时做了三件事:

  1. 将订单信息缓存到orderCache中
  2. 将用户最新订单信息更新到userOrderCache中
  3. 清空订单统计缓存,因为统计数据已经发生变化

4.2 电商系统实战案例

假设我们在开发一个电商系统的商品评价功能,当用户提交评价后需要更新多个缓存。

代码语言:java
复制
@Service
public class ReviewService {
    
    @Caching(
        put = {
            @CachePut(cacheNames = "reviewCache", key = "#review.id"),
            @CachePut(cacheNames = "productReviewCache", key = "#review.productId + '_latest'")
        },
        evict = {
            @CacheEvict(cacheNames = "productStatCache", key = "#review.productId"),
            @CacheEvict(cacheNames = "userReviewCountCache", key = "#review.userId")
        }
    )
    public Review submitReview(Review review) {
        // 保存评价信息
        Review savedReview = reviewRepository.save(review);
        
        // 更新商品评分
        updateProductRating(review.getProductId());
        
        return savedReview;
    }
    
    private void updateProductRating(Long productId) {
        // 重新计算商品平均评分
        Double avgRating = reviewRepository.calculateAvgRating(productId);
        productService.updateRating(productId, avgRating);
    }
}

5. 缓存实现原理

5.1 Spring缓存抽象层

Spring的缓存功能基于AOP实现,通过代理模式在方法调用前后插入缓存逻辑。

当你在方法上添加缓存注解时,Spring会创建一个代理对象来处理缓存操作。

代码语言:java
复制
@Configuration
@EnableCaching
public class CacheConfig {
    
    @Bean
    public CacheManager cacheManager() {
        // 使用内存缓存
        return new ConcurrentMapCacheManager("userCache", "productCache", "orderCache");
    }
}

5.2 多种缓存实现

Spring支持多种缓存实现,你可以根据项目需求选择合适的方案。

代码语言:java
复制
// Redis缓存配置
@Configuration
@EnableCaching
public class RedisCacheConfig {
    
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30)) // 设置过期时间
            .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
            
        return RedisCacheManager.builder(connectionFactory)
            .cacheDefaults(config)
            .build();
    }
}

通过合理使用这些缓存注解,你可以大幅提升应用性能,减少数据库压力。

缓存不是万能的,要根据业务特点选择合适的缓存策略,避免过度缓存导致的数据一致性问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. @Cacheable注解详解
    • 1.1 基本使用方式
    • 1.2 核心参数配置
  • 2. @CachePut注解应用
    • 2.1 更新缓存机制
    • 2.2 实际业务场景
  • 3. @CacheEvict注解详解
    • 3.1 缓存清理策略
    • 3.2 批量清理操作
  • 4. @Caching组合注解
    • 4.1 复杂缓存操作
    • 4.2 电商系统实战案例
  • 5. 缓存实现原理
    • 5.1 Spring缓存抽象层
    • 5.2 多种缓存实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档