玩转Spring Cache --- 扩展缓存注解支持失效时间TTL【享学Spring】(上)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 玩转Spring Cache --- 扩展缓存注解支持失效时间TTL【享学Spring】(上)

前言


在上篇文章讲解整合分布式缓存Redis时埋下了一个伏笔:如何让我们的缓存注解支持自定义TTL失效时间呢?


这篇文章本可以不用写,因为其实基于Redis的RedisCacheManager它本身天生就是能够针对不同的Cache配置不同的TTL的。但是我发现有的小伙伴觉得使用得还是不太方便,希望能在使用注解的时候直接控制失效时间,为了帮助解决小伙伴的这个困惑,这就是我书写本文的目的~


Spring Cache与失效时间TTL


首先此处我有必要再次强调一点:Spring Cache抽象本省是并不支持Expire失效时间的设定的,我粗暴的把它归为了Spring Cache抽象的一个设计上的bug,可参考文章:【小家Spring】玩转Spring Cache — @Cacheable/@CachePut/@CacheEvict注解的原理深度剖析和使用


若想在缓存注解上指定失效时间,必须具备如下两个基本条件:


  1. 缓存实现产品支持Expire失效时间(Ehcache、Redis等几乎所有第三方实现都支持)
  2. xxxCacheManager管理的xxxCache必须扩展了Expire的实现


因为缓存的k-v键值对具有自动失效的特性实在太重要和太实用了,所以虽然org.springframework.cache.Cache它没有实现Expire,但好在第三方产品对Spring缓存标准实现的时候,大都实现了这个重要的失效策略,比如典型例子:RedisCache。


本文以最为常用的Redis缓存为例,介绍两种控制缓存失效时间的方式。


实现Cache失效时间的两种通用方式


接下来就以Redis Cache为例,介绍两种常用的、通用的管理缓存失效时间的方式。


方式一:使用源生的RedisCacheManager进行集中式控制

由于控制key的失效时间这一块非常的实用和重要,所以其实Spring Data Redis工程早就给与了支持(不管是1.x版本还是2.x版本)。因此话不多说,直接给个例子就非常清晰明了:


1、准备Cache配置:


@EnableCaching // 使用了CacheManager,别忘了开启它  否则无效
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(1)) // 默认没有特殊指定的
                .computePrefixWith(cacheName -> "caching:" + cacheName);
        // 针对不同cacheName,设置不同的过期时间
        Map<String, RedisCacheConfiguration> initialCacheConfiguration = new HashMap<String, RedisCacheConfiguration>() {{
            put("demoCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1))); //1小时
            put("demoCar", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10))); // 10分钟
            // ...
        }};
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory())
                .cacheDefaults(defaultCacheConfig) // 默认配置(强烈建议配置上)。  比如动态创建出来的都会走此默认配置
                .withInitialCacheConfigurations(initialCacheConfiguration) // 不同cache的个性化配置
                .build();
        return redisCacheManager;
    }
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName("10.102.132.150");
        configuration.setPort(6379);
        configuration.setDatabase(0);
        LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration);
        return factory;
    }
    @Bean
    public RedisTemplate<String, String> stringRedisTemplate() {
        RedisTemplate<String, String> redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        return redisTemplate;
    }
}


使用示例:


@Service
public class CacheDemoServiceImpl implements CacheDemoService {
    @Caching(cacheable = {
            @Cacheable(cacheNames = "demoCache", key = "#id + 0"),
            @Cacheable(cacheNames = "demoCar", key = "#id + 10"),
            @Cacheable(cacheNames = "demoFsx", key = "#id + 100")
    })
    @Override
    public Object getFromDB(Integer id) {
        System.out.println("模拟去db查询~~~" + id);
        return "hello cache...";
    }
}


运行单元测试


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RootConfig.class, CacheConfig.class})
public class TestSpringBean {
    @Autowired
    private CacheDemoService cacheDemoService;
    @Autowired
    private CacheManager cacheManager;
    @Test
    public void test1() {
        cacheDemoService.getFromDB(1);
        cacheDemoService.getFromDB(1);
        System.out.println("----------验证缓存是否生效----------");
        Cache cache = cacheManager.getCache("demoCache");
        System.out.println(cache);
        System.out.println(cache.get(1, String.class));
    }
}


打印结果如下:


模拟去db查询~~~1
----------验证缓存是否生效----------
org.springframework.data.redis.cache.RedisCache@721eb7df
hello cache...


缓存生效。去Redis服务端查看对应的key情况:


image.png


眼睛尖的小伙伴可能发现了,只有最后一个key前面有caching前缀,其余两个木有,这是为何呢?

这是我故意留出来的一个小问题,留给小伙伴们自行思考~


由此可见,通过RedisCacheManager完成了对不同的Cache进行了失效时间的定制化配置,达到了我们的目的。


小细节


针对如上的配置,总结如下两点小细节使时需要注意:


  1. 即使禁用前缀disableKeyPrefix(),也是不会影响对应CacheName的TTL(因为TTL针对的是Cache,而不是key)
  2. 每个CacheName都可以对应一个RedisCacheConfiguration(它里面有众多属性都可以个性化),若没配置的(比如动态生成的)都走默认配置

Spring提供的在RedisCacheManager来统一管理Cache的TTL,这种集中式的管理其实是我赞同的方式,若让他分散在各个缓存注解上,反而非常不利于后期的维护管理~~~因此这种方式我也是推荐的


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
存储 缓存 NoSQL
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
|
2月前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
29天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
8天前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
29 2
|
15天前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
15 1
|
2月前
|
缓存 Java Spring
Spring缓存实践指南:从入门到精通的全方位攻略!
【8月更文挑战第31天】在现代Web应用开发中,性能优化至关重要。Spring框架提供的缓存机制可以帮助开发者轻松实现数据缓存,提升应用响应速度并减少服务器负载。通过简单的配置和注解,如`@Cacheable`、`@CachePut`和`@CacheEvict`,可以将缓存功能无缝集成到Spring应用中。例如,在配置文件中启用缓存支持并通过`@Cacheable`注解标记方法即可实现缓存。此外,合理设计缓存策略也很重要,需考虑数据变动频率及缓存大小等因素。总之,Spring缓存机制为提升应用性能提供了一种简便快捷的方式。
41 0
|
2月前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
56 0
|
2月前
|
缓存 NoSQL Redis
【Azure Redis 缓存】Azure Cache for Redis 服务的导出RDB文件无法在自建的Redis服务中导入
【Azure Redis 缓存】Azure Cache for Redis 服务的导出RDB文件无法在自建的Redis服务中导入
|
2月前
|
缓存 开发框架 NoSQL
【Azure Redis 缓存】VM 里的 Redis 能直接迁移到 Azure Cache for Redis ? 需要改动代码吗?
【Azure Redis 缓存】VM 里的 Redis 能直接迁移到 Azure Cache for Redis ? 需要改动代码吗?
|
2月前
|
缓存 NoSQL Unix
【Azure Redis 缓存】Azure Cache for Redis 中如何快速查看慢指令情况(Slowlogs)
【Azure Redis 缓存】Azure Cache for Redis 中如何快速查看慢指令情况(Slowlogs)