前言
现在项目中大家基本都会使用到缓存,本地缓存,Spring引入缓存,或者是直接使用三方缓存的API进行缓存的操作,基本离不开这三种,如果项目没有使用springboot的话,有些配置还需要自己手动去引入,因为没有与Spring进行自动集成,今天我们讨论的是在springboot的基础上进行缓存的使用,以redis和Spring默认缓存为例进行说明
Spring的缓存抽象
Spring Cache 只负责维护抽象层,具体的实现由自己的技术选型来决定,将缓存处理和缓存技术解除耦合。
使用Spring缓存抽象时我们需要关注以下两点:
- 确定那些方法需要被缓存
- 缓存策略
重要接口
org.springframework.cache.Cache
- 缓存抽象的规范接口,缓存实现有:RedisCache、EhCache、 ConcurrentMapCache等,具体的数据存储操作都是在这里进行实现
org.springframework.cache.CacheManager
- 缓存管理器,管理Cache的生命周期
SpringCache源码加载
到这一步就已经清楚了,自动按需装配;自己当时看的时候有一个疑问,echche和redis能一起使用吗,按照spring boot目前的方式是不行的,因为他们都实现了CacheManager这个接口,加载的时候,都有ConditionalOnMissingBean({CacheManager.class})这个注解,所以注定只能注入一个缓存管理器,然后加载自己对应的cache,再有个疑问,为什么Spring有默认的缓存管理,是哪一个,为什么?看下边源代码,入口方法是:
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.CacheConfigurationImportSelector
MAPPINGS这里边就是springboot实现缓存自动装配的配置类,就以redis和simple为例看下
观察SimpleCacheConfiguration这个类,上边的注解判断条件并没有额外外部类的限制,不需要额外引入外部的依赖,但是像REDIS,CAFFEINE,EHCACHE,它们都是需要额外引入依赖的,所以Spring默认缓存加载是SIMPLE;
建议配置文件加上(spring.cache.type=redis),可以更加清晰准确的加载到对应的配置信息,当然如果你配置的是spring.cache.type=simple,虽然你引入了redis,但是并不会生效,原因自己看下@Conditional({CacheCondition.class}),这个实现就知道了。
再谈一下规范
使用@Cacheable,如果是对象的话,需定义key字段,比如
@Cacheable(value="yyy",cacheManager = "redisCacheManager",key="#person.getId()")
value属于外部的索引,key属于内部的索引,不管是任何的缓存方式,整体结构都可以理解为双map形式;
获取缓存的源码跟踪
org.springframework.cache.interceptor.AbstractCacheResolver#resolveCaches,这个位置打断点
org.springframework.cache.concurrent.ConcurrentMapCacheManager#getCache
如果是redis的话,是看这个org.springframework.cache.support.AbstractCacheManager#getCache
最后再补充一点:
这个注解相当于你有多个缓存管理器的实现,但是Spring需要你有一个默认的实现,这个注解就是干这用的;若不指定CacheManager,会从spring容器中查找是否有存在CacheManager,若存在一个CacheManager会使用该CacheManager,若存在多个CacheManager则会抛出异常,即必须指定一个。
异常信息:No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary (or give it the name 'cacheManager') or declare a specific CacheManager to use, that serves as the default one.
再补充一个问题,关于@cacheable注解中(value=“cacheName”,key="#id"),value不起作用的解释,印象中应该是用于key的分组,类似于上hset的方式,其实不是,可以自己实验下,解释如下:
value属性,确实是指定了缓存的名称,但是并没有强行将value不同的缓存值加以区分(这个是有道理的,因为有些时候,需要把不同业务属性的实体存在一个缓存里,这种情况肯定是有的,所以如果强行通过value区分的话,上述需求反而实现不了了)
注:从Spring 4.1开始,value缓存注释的属性不再是强制性的,新定义的一个实行cachename是value的别名
参考链接:https://blog.csdn.net/a1035434631/article/details/100563706