前言
从一开始写 BBS-lite 的时候我就有打算整合 Redis 作为缓存, 但是在经历过 MyBatis整合Redis --> Spring Boot封装好了的Redis Cache --> 自己写Jedis控制 --> 自定义注解切入Service层进行缓存
等一系列流程后, 我发现缓存并不是这么简单。上述我使用过的方式都有一定的缺陷…
MyBatis二级缓存整合Redis
MyBatis的二级缓存作用域是基于命名空间的,默认一个XML配置文件内都处于同一命名空间,而通过 MyBatis Generator 生成的 XML 文件每一个都对应了一张表。但是头疼的来了, 在二级缓存里每次进行修改/插入/删除都会刷新当前命名空间内的缓存。也就是说:碰见修改密集的部分模块,用了缓存基本跟没用一样…当然也可能是我才疏血浅不知道更好的方案,不过我在官方文档和Google也没找出合理的解决方案。
使用Spring Boot的组件 spring-boot-starter-redis-cache
用起来能满足基本的缓存需求, 但是由于是通过注册 CacheManager Bean
实现的,所以一些Redis的特性并未体现出来。 即便是通过注册相关配置Bean 能调整的东西也有限。 一些缓存问题也不能很好的处理,如: 缓存雪崩(因为不支持随机TTL)、自增/自减(计数应该是比较频繁的场景了)。
自己写Jedis控制
这个没什么好说的,单纯因为我有代码洁癖…不想在Service里来回调用Jedis引入一大堆重复代码…毕竟基本上每段Service层的代码里都有Jedis相关代码来回穿梭还是很烦的…就跟每个类都定义一个LogFactory
的成员变量一样. @Slf4j
和Log4j
什么的它不香嘛?
你把问题抛给我,我先看看我这有没有,没有我去仓库里拿出来给你同时我再记一下答案,这样下次问同样的问题我就不用再跑到仓库里去找了。 抽象一下,就会发现跟AOP很想呐~
一些思考
暂时想不到比较好的解决方案, Google到的也只是Redis Cache的一些基本运用, 达不到我想要的效果。我想写一个比较好用的 Redis 缓存。起码能达到以下效果:
-
- 开箱即用: 通过AOP和自定义注解切入到一些层进行缓存操作。就这块而言很喜欢Spring Boot注解,在简化配置的同时侵入性也很低
-
- 可扩展: 能够被很方便的二次开发, 可以通过自定义配置信息来加入一些自定义操作(比如说某些模块未实现的功能)
-
- 自定义: 可以通过组合注解在原模块的基础注解上进行封装
-
- 能够支持
inc
(以及对 zset、 list、 set等数据结构的支持)
- 能够支持
-
- 支持随机TTL
然后,注解里应该有以下字段(仅供参考):
- key/keyGenerator(支持自定义key)
- ttl(时间)
- timeType(时间单位,目前我定义了秒/分/时/天)
- randomTtl(boolean,是否开启随机ttl.p.s:此处开启后应该在ttl一段范围内随机浮动,不用太多,个人觉得±2即可)
- strategy(缓存过期策略)
- cacheType(里面应该有四种,默认的新增,inc,delete和flush,当然也可以拆分成四个注解)
不过我现在只在BBS-lite中实现上述 1、4中对inc
的支持和 5。
想的时候很简单, 真正到写的时候就不一样了。 一开始想把缓存切入到Service - Dao
之间(理论上也应该这样),后来发现Dao
是通过MBG(MyBatis Generator)
自动生成的,如果表结构后续有改动的话(肯定有改动的)都得写一遍…
而放在Controller - Service
之间的话由于AOP是基于proxy模式实现的,所以一些数据组装的活放在Service层的话又会失效。比如我要查看一篇文章的话那么对应的应该调用Service层中 incArticleView
和 getArticleInfo
以及 getUserInfo
等方法然后将数据组装,但是如果以这种思路就得把组装放在Controller层
(前面有提到过AOP基于proxy模式实现,所以不能"我切我自己"),又违背了单一职责原则
后来又想到单独开一个Cache层,在Cache层中再注入Service,和Service实现同一个接口并加上@Primary
注解,然后Controller层通过注入接口调用,如果当前接口有实现Cache的话,则默认通过Cache层代理一些操作。但是这样又回到开头的写Jedis手动控制了…
目前来看,最优方案就只有将注解打在Service - Dao
之间的
当然如果有更好方案的,欢迎在下方评论给出建议
关注bestsort公众号,不迷路。主要是一些Java技术栈学习过程以及经验分享