SpringBootCache源码解析
Spring Boot 支持了多种缓存的自动配置,其中包括 Generic、JCache、EhCache 2.x、Hazelcast、 Infinispan、 Couchbase、 Redis 、Caffeine 和 Simple。早期版本还支持Guava 的缓存,但目前已经废弃。本章将重点讲解缓存的自动配置 CacheAutoConfiguration和默认的 SimpleCacheConfiguration 自动配置及相关内容。
Cache 简介
随着项目的发展,往往会出现- -些瓶颈, 比如与数据库的交互、与远程服务器的交互等。
此时,缓存便派上了用场。而在 Spring 3.1 中引入了基于注解的 Cache 的支持在spring-context 包 中 定 义 了 org.springframework.cache. CacheManager 和org.springframework.cache.Cache 接口,用来统一-不同的缓存的技术。
CacheManager 是 Spring 提供的各种缓存技术管理的抽象接口,而 Cache 接口包含缓存的增加、删除、读取等常用操作。针对 CacheManager, Spring 又提供了多种实现,比如基于Collection 来 实 现 的 SimpleCacheManager 、 基 于 ConcurrentHashMap 实 现 的 Concurrent-MapCacheManager、基于 EhCache 实现的 EhCacheCacheManager 和基于JCache 标准实现的 JCacheCacheManager 等。
Spring Cache 的实现与 Spring 事务管理类似,都是基于 AOP 的方式。其核心思想是:第一次调用缓存方法时,会把该方法参数和返回结果作为键值存放在缓存中,当同样的参数再次请求方法时不再执行该方法内部业务逻辑,而是直接从缓存中获取结果并返回。
Spring Cache 提供了@CacheConfig、@Cacheable 、@CachePut 、@CacheEvict 等注解来完成缓存的透明化操作,相关功能如下。.@CacheConfig:用于类上,缓存一些公共设置。
.@Cacheable:用于方法上,根据方法的请求参数对结果进行缓存,下次读取时直接读取缓存内容。
.@CachePut: 用于方法上,能够根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法的调用。
.@CacheEvict: 用于方法上,清除该方法的缓存,用在类上清除整个类的方法的缓存。
在了解了 Spring Cache 的基本作用的和定义之后,下面来看在 SpringBoot 中是如何对Cache 进行自动配置的。
Cache 自动配置
在 Spring Boot 中,关于 Cache 的默认自动配置类只有 CacheAutoConfiguration,主要用于缓存抽象的自动配置,当通过@EnableCaching 启用缓存机制时,根据情况可创建CacheManager。对于缓存存储可以通过配置自动检测或明确指定。
CacheAutoConfiguration 同样在 ME TA-INF/spring.factories 文件中配置注册。
代码语言:javascript复制# . Auto Configure
org. springframework. boot. autoconfigure . EnableAutoConfiguration=
org. springframework . boot . autoconfigure . cache . CacheAutoConfiguration,
下面先来看 CacheAutoConfiguration 类的注解部分代码实现。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass (CacheManager. class)
@ConditionalOnBeanCacheAspectSupport . class)
@ConditionalOnMissingBean(value = CacheManager .class, name = "cacheResolve
r")
@EnableConfigurationProperties (CacheProperties. class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration. class, HazelcastAutoConfig
uration. class,
HibernateJpaAutoConfiguration. class, RedisAutoConfigur
ation.class })
@Import(CacheConfigurat ionImportSelector.class)
public class CacheAutoConfiguration {
}
@ConditionalOnClass 指 定 需 要 在 classpath 下 存 在 CacheManager 类 。关 于CacheManager 类是一个缓存管理器的接口,管理各种缓存(Cache) 组件。针对不同的缓存技术,会有不同的实现类。比如,在 Spring 中提供了 SimpleCacheManager (测试)、 Concurrent-MapCacheManager ( 默 认 ) 、 NoOpCacheManager ( 测 试 ) 、EhCacheCacheManager ( 基于 EhCache)、RedisCacheManager (基于 Redis) 等实现类。CacheManager 接口提供了两个方法:根据名称获取缓存和获取缓存名称集合,相关代码如下。
代码语言:javascript复制public interface CacheManager {
//根据名称获取缓存
@Nullable
Cache getCache(String name);
//获取缓存名称集合
Collection<String> getCacheNames();
}
在 CacheManager 接 口 中 只 定 义 了 上 面 两 个 方 法 , 但 在 其 抽 象 实 现 类AbstractCache-Manager 中便扩展了新增 Cache、更新 Cache 等方法。
@ConditionalOnBean 指定需要存在 CacheAspectSupport 的 Bean 时才生效,换句话说,就 是 需要在使用 了 @EnableCaching 时 才 有 效 。这 是 因 为 该 注 解 隐 式 的 导 致 了CacheInter-ceptor 对应的 Bean 的初始化,而 CacheInterceptor 为 CacheAspectSupport的子类。
@ConditionalOnMissingBean 指定名称为 cacheResolver 的 CacheManager 对象不存在时生效。
@ EnableConfigurationProperties 加 载 缓 存 的 CacheProperties 配 置 项 , 配 置 前 缀 为spring.cache.
@AutoConfigureAfter 指定该自动配置必须在缓存数据基础组件自动配置之后进行,这里包括 Couchbase、 Hazelcast、 HibernateJpa 和 Redis 的自动配置
想要实现缓存,需要先集成对应的缓存框架或组件。这里以 Redis 为例,它的自动配置类RedisAutoConfiguration 中 便 完 成 了 Redis 相 关 的 Redis Template 和 StringRedisTemplate 的实例化。而 RedisAutoConfiguration 中导入类 JedisConnectionConfiguration又完成了 Redis 使用 Jedis 连接的配置
@Ilmport 导入 CacheConfigurationlmportSelector,其实是导入符合条件的 Spring Cache 使用的各类基础缓存框架(或组件)的配置。该类的实现就位于 CacheAutoConfiguration 中,代码如下。
代码语言:javascript复制static class CacheConfigurat ionImportSelector implements ImportSelector {
public String[] selectImports(Annotat ionMetadata importingClassMetadata)
CacheType[] types = CacheType.values();
for(inti=0:i<types.leneth:1t )5
imports[i] = CacheConfigurations . getConfigurat ionClass(types[i]);
return imports; }
}
导入类的获取是通过实现 ImportSelector 接口来完成的,具体获取步骤位于 selectlmports方法中。该方法中,首先获取枚举类 CacheType 中定义的缓存类型数据,CacheType 中定义支 持的缓存类型如下。
代码语言:javascript复制//支持的缓存类型(按照优先级定义)
public enum CacheType {
//使用上下文中的 Cache Bean 进行通用缓存
GENERIC,
// JCache(JSR- 107) 支持的缓存
JCACHE,
// EhCache 支持的缓存
EHCACHE,
// Hazelcast 支持的缓存
HAZELCAST,
// Infinispan 支持的缓存
INFINISPAN,
// Couchbase. 支持的缓存
COUCHBASE,
// Redis.支持的缓存
REDIS,
// Caffeine 支持的缓存
CAFFEINE,
//内存基本的简单缓存
SIMPLE,
// 不支持缓存
NONE
}
枚举类 CacheType 中定义了以上支持的缓存类型,而且上面的缓存类型默认是按照优先级从前到后的顺序排列的。
selectlmports 方法中,当获取 Cache Type 中定义的缓存类型数组之后,遍历该数组并通过CacheConfigurations 的 getConfigurationClass 方法获得每种类型缓存对应的自动配置类( 注解@Configuration 的类)。
CacheConfigurations 相关代码如下。
代码语言:javascript复制final class CacheConfigurations {
private static final Map<CacheType, Class<?>> MAPPINGS;
//定义 CacheType 5@Conf iguration 之间的对应关系
static {
Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
mappings . put(CacheType . GENERIC, GenericCacheConfiguration.class);mappings . put(CacheType . EHCACHE,EhCacheCacheConfiguration. class);
mappings . put (CacheType . HAZELCAST, HazelcastCacheConfiguration. class);
mappings . put(CacheType . INF INISPAN, InfinispanCacheConfiguration.class);
mappings . put (CacheType . JCACHE, JCacheCacheConfiguration. class);
mappings . put (CacheType . COUCHBASE, CouchbaseCacheConfiguration. class);
mappings . put (CacheType. REDIS, RedisCacheConfiguration.class);
mappings . put(CacheType . CAFFEINE, CaffeineCacheConfiguration. class);
mappings . put(CacheType . SIMPLE, SimpleCacheConfiguration.class);
mappings . put(CacheType . NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections . unmodifiableMap(mappings);
//根据 CacheType
型获得对应的@Configuration 类
static String getConfigurationClass(CacheType cacheType) {
Class<?> configurationClass = MAPPINGS . get(cacheType);
Assert.state(configurationClass != null, () -> "Unknown cache type ”
cacheType) ;
return configurationClass . getName();
}
}
经过以上步骤,我们会发现通过@Import 注解,CacheAutoConfiguration 导入了 CacheType中定义的所有类型的自动配置,也就是 Spring Boot 目前支持的缓存类型。而具体会自动配置哪种类型的缓存,还需要看导入的自动配置类里面的生效条件。
我们以 GenericCacheConfiguration 为例进行了解,源代码如下。
代码语言:javascript复制@Configurat ion(proxyBeanMethods = false)
@ConditionalOnBean(Cache . class)
@ConditionalOnMissingBean(CacheManager . class)
@Conditional(CacheCondition. class)
class GenericCacheConfiguration {
@Bean
SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Coll
ection
<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager . setCaches(caches);
return customizers . customize(cacheManager);
}}
在 GenericCacheConfiguration 的注解部分,@ConditionalOnBean 指定当 Cache 的 Bean存在时进行实例化操作,@ConditionalOnMissingBean 指定当 CacheManager 的 Bean 不存在时进行实例化操作,@Conditional 指定当满足 CacheCondition 指定的条件时进行实例化操作。
CacheManager 我们前面已经介绍过,不再赘述。Cache 是一 个定义了缓存通用操作的接口,其中定义了缓存名称获取、缓存值获取、清除缓存、添加缓存值等操作。对应的缓存组件或框架实现该接口,并根据组件自身的情况提供对应的操作方法实现。
下面看 CacheCondition 类中定义的条件。
代码语言:javascript复制class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome (ConditionContext context, Annot
ated-
TypeMetadata metadata) {
String sourceClass =
if (metadata instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) metadata). getClassName() ;
Condit ionMessage . Builder message = ConditionMessage . forCondition("C
ache" ,
sourceClass);
Environment environment = context . getEnvironment( ) ;
try {
//创建指定环境的 Binder, 然后绑定属性到对象上
BindResult<CacheType> specified = Binder . get( environment) . bind
("spring.
cache. type", CacheType . class);
//如果未绑定,则返回匹配
if (!specified. isBound()) {
return ConditionOutcome . match(message . because("automatic ca
che type"));
//获取所需的缓存类型
CacheType required = CacheConfigurations . getType(((AnnotationMe
tadata)
metadata) . getClassName());
//如果已绑定,并且绑定的类型与所需的缓存类型相同,则返回匹配
if (specified.get() == required) {return Conditi onOutcome . match(message. because(specified. get
() ”cache type"));
} catch (BindException ex) {
//其他情况则返回不匹配
return ConditionOutcome . noMatch(message . because("unknown cache typ
e"));
}
}
CacheCondition 的核心逻辑就是首先通过 Binder 进行指定属性和类的绑定,然后通过绑定结果( BindResult)进行判断:如果判断结果是未绑定,则直接返回条件匹配;否则,判断绑定的缓存类型与所需的缓存类型是否相等,如果相等则返回条件匹配;其他情况则返回条件不匹配。
当 GenericCacheConfiguration 满足注解指定的条件后,便会通过 cacheManager 方法进行SimpleCacheManager 类的实例化操作。首先创建 SimpleCacheManager 对象,然后将缓存 集 合 设 置 到 对 象 中 , 最 后 通 过 CacheManagerCustomizers 的 customize 方 法 对SimpleCacheManager 进行定制化处理。
SimpleCacheManager 类是接口 CacheManager 的一个实现类,通过集合来实现缓存功能,源代码如下。
代码语言:javascript复制public class SimpleCacheManager extends AbstractCacheManager {
private Collection<? extends Cache> caches = Collections . emptySet();
//设置缓存集合
public void setCaches (Collection<? extends Cache> caches) {
this.caches = caches;
//获取缓存集合
@Override
protected Collection<? extends Cache> loadCaches() {
return this. caches;
}
}
通过以上代码可以看出,SimpleCacheManager 的实现极其简单, 就是基于 Cache 的集合来实现的,它提供了设置缓存集合和获取缓存集合的方法。同样,由于实现比较简单,它往往被用于测试环境和简单缓存场景中。
上面我们以 GenericCacheConfiguration 为例讲解了@Import 引入的缓存组件配置,关于其他的类型缓存注解的配置就不再一-讲解了。
下 我 继 看 @Ilmport 的 CacheManagerEntityManagerFactoryDependsOnPostProcess-or类。该类同样为 CacheAutoConfiguration 的内部类。
代码语言:javascript复制@ConditionalOnClass(LocalContainerEnt ityManagerFactoryBean. class)
@Conditiona lOnBean(AbstractEntityManagerFactoryBean.class)
static class CacheManagerEntityManagerFactoryDependsOnPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
CacheManagerEntityManagerFactoryDependsOnPostProcessor() {
super("cacheManager");}
}
该 类 实 现 了 EntityManagerFactoryDependsOnPostProcessor, 本质上是BeanFactoryPost-Processor 的 一 个 实 现 类 。当 classpath中存在LocalContainerEntityManagerFactoryBean类和实现了抽象类AbstractEntityManagerFactoryBean 的类的 Bean 时,才会进行实例化操作。
在该类的构造方法中调用父类构造方法并传递值"cacheManager”。因此,动态声明了所有类型为 EntityManagerFactory 的 Bean 都必须依赖于名称为 cacheManager 的 Bean。
最后,我们看一下 CacheAutoConfiguration 中其余的代码。
代码语言:javascript复制//实例化 CacheManagerCus tomizers
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers(
customi zers . orderedStream(). collect(Collectors . toList()));
}
cacheManagerCustomizers 方法初始化了 CacheManagerCustomizers 对象的 Bean,主要是 将 容 器 中 存 在 的 一 一 个 或 多 个 CacheManagerCustomizer 的 Bean 组 件 包 装 为CacheManager-Customizers,并将 Bean 注入容器。
代码语言:javascript复制//实例化 CacheManagerVal idator
@Bean
public CacheManagerValidator cacheAutoConfigurat ionValidator(CachePropert
ies-
cachePropert
ies,
objectProvid
er<CacheManager> cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
// CacheManagerVal idator 的具体定义,用于检查并抛出有意义的异常 static class
CacheManagerValidator implements InitializingBean {private final CacheProperties cacheProperties;
private final objectProvider<CacheManager> cacheManager;
CacheManagerValidator(CacheProperties cacheProperties, objectProviderkCac
he-
Manager> cacheManager) {
this. cacheProperties = cacheProperties;
this. cacheManager = cacheManager;
}
@Override
public void afterPropertiesSet() {
Assert . notNull(this . cacheManager . getIfAvailable(),
) -> "No cache manager could be auto- configured, check
your configuration (caching ” "type is” this. cacheProperties .getType
() "')");
}
}
cacheAutoConfigurationValidator 方法初始化了 CacheManagerValidator 的 Bean,该 Bean用于确保容器中存在一个 CacheManager 对象, 以达到缓存机制可以继续被配置和使用的目的,同时该 Bean 也用来提供有意义的异常声明。
至此关于 Spring Boot 中 cache 的 CacheAutoConfiguration 自动配置讲解完毕,随后我们会继续讲一下Spring Boot 中默认的自动配置。
本文给大家讲解的内容是SpringBootCache源码解析:Cache自动配置
- 下篇文章给大家讲解的是SpringBootCache源码解析:默认Cache配置;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。