SpringBootCache源码解析:Cache自动配置

愿天堂没有BUG

共 7238字,需浏览 15分钟

 ·

2022-04-25 07:52


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 文件中配置注册。

# . 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 接口提供了两个方法:根据名称获取缓存和获取缓存名称集合,相关代码如下。

public interface CacheManager {
//根据名称获取缓存
@Nullable
Cache getCache(String name);
//获取缓存名称集合
Collection 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 中,代码如下。

static class CacheConfigurat ionImportSelector implements ImportSelector {
public String[] selectImports(Annotat ionMetadata importingClassMetadata)
CacheType[] types = CacheType.values();
for(inti=0:i1t+)5
imports[i] = CacheConfigurations . getConfigurat ionClass(types[i]);
return imports; }
}

导入类的获取是通过实现 ImportSelector 接口来完成的,具体获取步骤位于 selectlmports方法中。该方法中,首先获取枚举类 CacheType 中定义的缓存类型数据,CacheType 中定义支 持的缓存类型如下。

//支持的缓存类型(按照优先级定义)
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 相关代码如下。

final class CacheConfigurations {
private static final Map> MAPPINGS;
//定义 CacheType 5@Conf iguration 之间的对应关系
static {
Map> mappings = new EnumMap<>(CacheType.class);
mappings . put(CacheType . GENERIC, GenericCacheConfiguration.class);mappings . put(CacheType . EHCACHEEhCacheCacheConfiguration. 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 为例进行了解,源代码如下。

@Configurat ion(proxyBeanMethods = false)
@ConditionalOnBean(Cache . class)
@ConditionalOnMissingBean(CacheManager . class)
@Conditional(CacheCondition. class)
class GenericCacheConfiguration {
@Bean
SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Coll
ection
caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager . setCaches(caches);
return customizers . customize(cacheManager);
}}

在 GenericCacheConfiguration 的注解部分,@ConditionalOnBean 指定当 Cache 的 Bean存在时进行实例化操作,@ConditionalOnMissingBean 指定当 CacheManager 的 Bean 不存在时进行实例化操作,@Conditional 指定当满足 CacheCondition 指定的条件时进行实例化操作。

CacheManager 我们前面已经介绍过,不再赘述。Cache 是一 个定义了缓存通用操作的接口,其中定义了缓存名称获取、缓存值获取、清除缓存、添加缓存值等操作。对应的缓存组件或框架实现该接口,并根据组件自身的情况提供对应的操作方法实现。

下面看 CacheCondition 类中定义的条件。

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 的一个实现类,通过集合来实现缓存功能,源代码如下。

public class SimpleCacheManager extends AbstractCacheManager {
private Collection caches = Collections . emptySet();
//设置缓存集合
public void setCaches (Collection caches) {
this.caches = caches;
//获取缓存集合
@Override
protected Collection loadCaches() {
return this. caches;
}
}

通过以上代码可以看出,SimpleCacheManager 的实现极其简单, 就是基于 Cache 的集合来实现的,它提供了设置缓存集合和获取缓存集合的方法。同样,由于实现比较简单,它往往被用于测试环境和简单缓存场景中。

上面我们以 GenericCacheConfiguration 为例讲解了@Import 引入的缓存组件配置,关于其他的类型缓存注解的配置就不再一-讲解了。

下 我 继 看 @Ilmport 的
CacheManagerEntityManagerFactoryDependsOnPostProcess-or类。该类同样为 CacheAutoConfiguration 的内部类。

@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 中其余的代码。

//实例化 CacheManagerCus tomizers
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider> customizers) {
return new CacheManagerCustomizers(
customi zers . orderedStream(). collect(Collectors . toList()));
}

cacheManagerCustomizers 方法初始化了 CacheManagerCustomizers 对象的 Bean,主要是 将 容 器 中 存 在 的 一 一 个 或 多 个 CacheManagerCustomizer 的 Bean 组 件 包 装 为CacheManager-Customizers,并将 Bean 注入容器。

//实例化 CacheManagerVal idator
@Bean
public CacheManagerValidator cacheAutoConfigurat ionValidator(CachePropert
ies-
cachePropert
ies,
objectProvid
er cacheManager) {
return new CacheManagerValidator(cacheProperties, cacheManager);
// CacheManagerVal idator 的具体定义,用于检查并抛出有意义的异常 static class
CacheManagerValidator implements InitializingBean {private final CacheProperties cacheProperties;
private final objectProvider 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自动配置

  1. 下篇文章给大家讲解的是SpringBootCache源码解析:默认Cache配置;

  2. 觉得文章不错的朋友可以转发此文关注小编;

  3. 感谢大家的支持!

本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。


浏览 9
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报