Tianyi's Blog Tianyi's Blog
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)

Tianyi

一直向前,永不停止
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)
  • Java

  • Golang

  • JVM的奇妙世界

  • Spring

  • Spring增强封装

  • Redis

    • Redis迭代演化
    • Redis安装配置
    • Redis数据结构
    • Spring集成Redis组件
    • Spring Cache
      • Spring Cache
        • 区分 Cache 与 Buffer
        • JSR-107 与 Spring 自带注解对比
        • 3 issues when using Spring Cache
        • 通过自定义CacheManager解决上述问题
    • Redis持久化
    • Bloom Filter
    • Cluster-主从模式
    • Cluster-Sentinel
    • Cluster-三主三从
    • Redis理论知识
  • MySQL

  • RabbitMQ

  • Kafka

  • 分享

  • 后端
  • Redis
tianyi
2024-03-08
目录

Spring Cache

# Spring Cache

自 Spring 3.1 版本以来,Spring 框架支持低侵入的方式向已有 Spring 应用加入缓存特性。与声明式事务类似,声明式缓存 Spring Cache 抽象允许一致的 API 来支持多种不同的缓存解决方案,同时将对代码的影响减少到最小。从 Spring 4.1 开始,Spring 已完整支持 JSR-107 注解和更多的定制选项。

# 区分 Cache 与 Buffer

很多情况下,Buffer(缓冲)与 Cache(缓存)是类似的。然而在表现形式与应用场景上两个的差别还是比较明显的。

传统意义上,Buffer 作为快速实体与慢速实体之间的桥梁。比如:硬盘上的文件数据会先到内存,再被 CPU 加载,内存作为 Buffer 可以减少等待时间,同时利用 Buffer 可将原本小块数据攒成整块一次性交给处理者,可以有效减少 IO。此外,通常至少有一个对象对其可见。

而 Cache 缓存,相对来说是隐藏的,对于访问者与被访问者来说应该是隐藏的,好的程序设计可以让使用者对缓存无感知,同时它还可以提高性能,允许应用多次、快速的读取缓存数据。

# JSR-107 与 Spring 自带注解对比

JSR-107(JCache)

标准化:提供统一的 Java 缓存 API,使代码与具体缓存实现(如 Ehcache、Hazelcast)解耦。

注解驱动:通过简洁的注解(如 @CacheResult)声明缓存行为。

灵活性:支持缓存加载、过期策略、监听器等高级特性。

Spring JSR-107 Remark
Cacheable CacheResult Fairly similar. BacheResult can cache specific exceptions and force the execution of the method regardless of the content of the cache.
CachePut CachePut While Spring updates the cache with the result of the method invocation. JCache requires that it be passed it as an argument that is annotated with QCacnevalve.Due to this difference,JCache allows updating the cache before or after the actual method invocation.
CacheEvict CacheRenove Fairly similar. (acacheRenovo $supports conditional eviction when the method invocation results in an exception.
@cachecrict(allentrie s-true) CacheRemoveAll Seecacheremove.
CacheConfig CacheDefaults Lets you configure the same concepts, in a similar fashion.
  • @Cacheable / @CacheResult:用于读取 / 设置缓存
  • @CachePut:添加 / 更新缓存
  • @CacheEvict / @CacheRemove:移除缓存

# 3 issues when using Spring Cache

  1. 默认 Spring Cache 采用 :: 分割数据,并不是约定俗成的冒号分割。
  2. 默认使用 JDK 序列化,JDK 序列化的问题之前我们也提到了,应该为 JSON 序列化。
  3. 默认 Spring Cache 注解是不支持 Expire 过期的,但这是日常开发中必然会用到的特性,该如何处理呢?

# 通过自定义CacheManager解决上述问题

@Configuration 
public class SpringCacheConfgiration { 
    @Bean 
    @Primary //设置默认的CacheManager 
    public CacheManager cacheManager(LettuceConnectionFactory factory){ 
        //加载默认Spring Cache配置信息 
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); 
        //设置有效期为1小时 
        config = config.entryTtl(Duration.ofHours(1)); 
        //说明缓存Key使用单冒号进行分割 
        config = config.computePrefixWith(cacheName -> cacheName + ":"); 
        //Redis Key采用String直接存储 
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); 
        //Redis Value则将对象采用JSON形式存储 
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); 
        //不缓存Null值对象 
        config = config.disableCachingNullValues(); 
        //实例化CacheManger缓存管理器 
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder 
                //绑定REDIS连接工厂 
                .fromConnectionFactory(factory) 
                //绑定配置对象 
                .cacheDefaults(config) 
                //与声明式事务注解@Transactional进行兼容 
                .transactionAware() 
                //完成对象构建 
                .build(); 
        return cacheManager; 
    }

    //为不同的TTL创建不同的CacheManager
    @Bean 
    public CacheManager cacheManager1m(LettuceConnectionFactory factory){ 
        //加载默认Spring Cache配置信息 
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); 
        //设置有效期为1小时 
        config = config.entryTtl(Duration.ofMinutes(1)); 
        //说明缓存Key使用单冒号进行分割 
        config = config.computePrefixWith(cacheName -> cacheName + ":"); 
        //Redis Key采用String直接存储 
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())); 
        //Redis Value则将对象采用JSON形式存储 
        config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); 
        //不缓存Null值对象 
        config = config.disableCachingNullValues(); 
        //实例化CacheManger缓存管理器 
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder 
                //绑定REDIS连接工厂 
                .fromConnectionFactory(factory) 
                //绑定配置对象 
                .cacheDefaults(config) 
                //与声明式事务注解@Transactional进行兼容 
                .transactionAware() 
                //完成对象构建 
                .build(); 
        return cacheManager; 
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

这里还要注意一个点,这里是责任链的设计模式, 不能断开赋值的

自定义CacheManager通过对RedisCacheConfiguration进行一系列配置来解决使用Spring Cache时遇到的问题:

  • 更改数据分割符号:使用config = config.computePrefixWith(cacheName -> cacheName + ":");语句,将缓存Key的分割符号设置为冒号。这样就把默认的::分割改为了约定俗成的冒号分割,满足开发者的使用习惯。
  • 替换序列化方式:通过config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));和config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));这两句,将Redis Key设置为采用String直接存储 ,Redis Value则将对象采用JSON形式存储,把默认的JDK序列化替换为JSON序列化,解决了JDK序列化存在的问题。
  • 支持过期时间设置:在RedisCacheConfiguration中使用config = config.entryTtl(Duration.ofHours(1));(以设置1小时有效期为例)或config = config.entryTtl(Duration.ofMinutes(1));(设置1分钟有效期)来设置缓存的有效期。在@Cacheable注解中增加cacheManager属性,指定使用不同配置的CacheManager,从而实现不同的过期时间设置,满足日常开发中对缓存过期时间的需求。

在Service使用时,如果需要特别的过期时间,需要在@Cacheable增加cacheManager属性

@Service 
public class EmpService { 
    @Resource 
    EmpDAO empDao; 
    @Cacheable(value = "emp" , key = "#empId" ,condition = "#empId != 1000", cacheManager = "cacheManager1m") 
    public Emp findById(Integer empId) { 
        return empDao.findById(empId); 
    }
}
1
2
3
4
5
6
7
8
9
完善页面 (opens new window)
Spring集成Redis组件
Redis持久化

← Spring集成Redis组件 Redis持久化→

最近更新
01
JDK
02-23
02
BadTasteCode && 优化
09-11
03
Gradle 实践操作指南及最佳实践
09-11
更多文章>
Theme by Vdoing | Copyright © 2021-2025 Tandy | 粤ICP备2023113440号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式