SpringCache源码解析(一)

2024-08-24 09:52
文章标签 源码 springcache 解析

本文主要是介绍SpringCache源码解析(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、springCache如何实现自动装配

SpringBoot 确实是通过 spring.factories 文件实现自动配置的。Spring Cache 也是遵循这一机制来实现自动装配的。

具体来说,Spring Cache 的自动装配是通过 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration 这个类来实现的。这个类位于 spring-boot-autoconfigure 包下。

在 spring-boot-autoconfigure 包的 META-INF/spring.factories 文件中,可以找到 CacheAutoConfiguration 类的配置:
在这里插入图片描述

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

二、CacheAutoConfiguration

@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {/*** 创建CacheManagerCustomizers Bean* XxxCacheConfiguration中创建CacheManager时会用来装饰CacheManager*/@Bean@ConditionalOnMissingBeanpublic CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) {return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));}/*** 创建CacheManagerValidator Bean* 实现了InitializingBean,在afterPropertiesSet方法中校验CacheManager*/@Beanpublic CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,ObjectProvider<CacheManager> cacheManager) {return new CacheManagerValidator(cacheProperties, cacheManager);}/*** 后置处理器* 内部类,动态声明EntityManagerFactory实例需要依赖"cacheManager"实例*/@Configuration@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor {public CacheManagerJpaDependencyConfiguration() {super("cacheManager");}}/*** 缓存管理器校验器* 内部类,实现了InitializingBean接口,实现afterPropertiesSet用于校验缓存管理器*/static class CacheManagerValidator implements InitializingBean {private final CacheProperties cacheProperties;private final ObjectProvider<CacheManager> cacheManager;CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {this.cacheProperties = cacheProperties;this.cacheManager = cacheManager;}/*** 当依赖注入后处理*/@Overridepublic void afterPropertiesSet() {Assert.notNull(this.cacheManager.getIfAvailable(),() -> "No cache manager could " + "be auto-configured, check your configuration (caching "+ "type is '" + this.cacheProperties.getType() + "')");}}/*** 缓存配置导入选择器*/static class CacheConfigurationImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {CacheType[] types = CacheType.values();String[] imports = new String[types.length];for (int i = 0; i < types.length; i++) {imports[i] = CacheConfigurations.getConfigurationClass(types[i]);}return imports;}}}

借助Conditional机制实现自动配置条件:

  1. CacheManager.class在类路径上存在;
  2. CacheAspectSupport实例存在(即CacheInterceptor);
  3. CacheManager实例不存在,且cacheResolver Bean不存在;
    当满足以上要求时,就会触发SpringCache的自动配置逻辑,CacheAutoConfiguration会引入其它的Bean,具体如下:
  4. 通过@EnableConfigurationProperties使CacheProperties生效;
  5. 借助Import机制导入内部类:CacheConfigurationImportSelector和CacheManagerEntityManagerFactoryDependsOnPostProcessor;
  6. 创建了CacheManagerCustomizers、CacheManagerValidator Bean;

2.1 关于的一些疑问

关于@ConditionalOnClass,我有一个疑问:

  • value值是类名,如果类找不到,会不会编译报错;
    ——会! 那value有什么意义?只要能编译过的,肯定都存在。
    ——@ConditionalOnClass注解之前一直让我感到困惑,类存不存在,编译器就会体现出来,还要这个注解干嘛?后来想了很久,感觉java语法并不是一定要寄生在idea上,所以语法上限制和工具上限制,这是两码事,理论上就应该彼此都要去做。

@ConditionalOnClass还可以用name属性:

@ConditionalOnClass(name="com.xxx.Component2")

可以看下这篇文章:
SpringBoot中的@ConditionOnClass注解

2.2 CacheProperties

// 接收以"spring.cache"为前缀的配置参数
@ConfigurationProperties(prefix = "spring.cache")
public class CacheProperties {// 缓存实现类型// 未指定,则由环境自动检测,从CacheConfigurations.MAPPINGS自上而下加载XxxCacheConfiguration// 加载成功则检测到缓存实现类型private CacheType type;// 缓存名称集合// 通常,该参数配置了则不再动态创建缓存private List<String> cacheNames = new ArrayList<>();private final Caffeine caffeine = new Caffeine();private final Couchbase couchbase = new Couchbase();private final EhCache ehcache = new EhCache();private final Infinispan infinispan = new Infinispan();private final JCache jcache = new JCache();private final Redis redis = new Redis();// getter and setter.../*** Caffeine的缓存配置参数*/public static class Caffeine {...}/*** Couchbase的缓存配置参数*/public static class Couchbase {...}/*** EhCache的缓存配置参数*/public static class EhCache {...}/*** Infinispan的缓存配置参数*/public static class Infinispan {...}/*** JCache的缓存配置参数*/public static class JCache {...}/*** Redis的缓存配置参数*/public static class Redis {// 过期时间,默认永不过期private Duration timeToLive;// 支持缓存空值标识,默认支持private boolean cacheNullValues = true;// 缓存KEY前缀private String keyPrefix;// 使用缓存KEY前缀标识,默认使用private boolean useKeyPrefix = true;// 启用缓存统计标识private boolean enableStatistics;// getter and setter...}
}

2.3 CacheConfigurationImportSelector

CacheAutoConfiguration的静态内部类,实现了ImportSelector接口的selectImports方法,导入Cache配置类;

/*** 挑选出CacheType对应的配置类*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {CacheType[] types = CacheType.values();String[] imports = new String[types.length];// 遍历CacheTypefor (int i = 0; i < types.length; i++) {// 获取CacheType的配置类imports[i] = CacheConfigurations.getConfigurationClass(types[i]);}return imports;
}

CacheConfigurationImportSelector,把所有缓存的配置类拿出来,加入到spring中被加载。

有的开发者可能就会问:
为什么要重载所有的配置类?而不是配置了哪种缓存类型就加载哪种缓存类型?
——这里只是加载所有的配置类,比如Redis,只是加载RedisCacheConfiguration.class配置类,后续会根据条件判断具体加载到哪个配置,如下图:
在这里插入图片描述

2.4 CacheConfigurations

维护了CacheType和XxxCacheConfiguration配置类的映射关系;

/*** CacheType和配置类的映射关系集合* 无任何配置条件下,从上而下,默认生效为SimpleCacheConfiguration*/
static {Map<CacheType, String> mappings = new EnumMap<>(CacheType.class);mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName());mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class.getName());mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName());mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName());mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName());mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName());mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName());mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName());mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName());mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName());MAPPINGS = Collections.unmodifiableMap(mappings);
}/*** 根据CacheType获取配置类*/
static String getConfigurationClass(CacheType cacheType) {String configurationClassName = MAPPINGS.get(cacheType);Assert.state(configurationClassName != null, () -> "Unknown cache type " + cacheType);return configurationClassName;
}/*** 根据配置类获取CacheType*/
static CacheType getType(String configurationClassName) {for (Map.Entry<CacheType, String> entry : MAPPINGS.entrySet()) {if (entry.getValue().equals(configurationClassName)) {return entry.getKey();}}throw new IllegalStateException("Unknown configuration class " + configurationClassName);
}

2.5 CacheType

缓存类型的枚举,按照优先级定义;

GENERIC     // Generic caching using 'Cache' beans from the context.
JCACHE      // JCache (JSR-107) backed caching.
EHCACHE     // EhCache backed caching.
HAZELCAST   // Hazelcast backed caching.
INFINISPAN  // Infinispan backed caching.
COUCHBASE   // Couchbase backed caching.
REDIS       // Redis backed caching.
CAFFEINE    // Caffeine backed caching.
SIMPLE      // Simple in-memory caching.
NONE        // No caching.

2.4 CacheCondition

它是所有缓存配置使用的通用配置条件;

2.4.1 准备工作

介绍之前先看一些CacheCondition类使用的地方,可以看到是各种缓存类型类型的配置类使用了它。
在这里插入图片描述

@Conditional注解的作用是什么?
——请看这篇文章@Conditional 注解有什么用?

说穿了,就是根据注解参数Condition实现类(这里是CacheCondition类)的matches方法返回,来判断是否加载这个配置类。

2.4.2 CacheCondition核心代码

/*** 匹配逻辑*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {String sourceClass = "";if (metadata instanceof ClassMetadata) {sourceClass = ((ClassMetadata) metadata).getClassName();}ConditionMessage.Builder message = ConditionMessage.forCondition("Cache", sourceClass);Environment environment = context.getEnvironment();try {// 获取配置文件中指定的CacheTypeBindResult<CacheType> specified = Binder.get(environment).bind("spring.cache.type", CacheType.class);if (!specified.isBound()) {// 不存在则根据CacheConfiguration.mappings自上而下匹配合适的CacheConfigurationreturn ConditionOutcome.match(message.because("automatic cache type"));}CacheType required = CacheConfigurations.getType(((AnnotationMetadata) metadata).getClassName());// 存在则比较CacheType是否一致if (specified.get() == required) {return ConditionOutcome.match(message.because(specified.get() + " cache type"));}}catch (BindException ex) {}return ConditionOutcome.noMatch(message.because("unknown cache type"));
}

三、常见的CacheConfiguration

3.1 RedisCacheConfiguration

它是Redis缓存配置;

// Bean方法不被代理
@Configuration(proxyBeanMethods = false)
// RedisConnectionFactory在类路径上存在
@ConditionalOnClass(RedisConnectionFactory.class)
// 在RedisAutoConfiguration之后自动配置
@AutoConfigureAfter(RedisAutoConfiguration.class)
// RedisConnectionFactory实例存在
@ConditionalOnBean(RedisConnectionFactory.class)
// CacheManager实例不存在
@ConditionalOnMissingBean(CacheManager.class)
// 根据CacheCondition选择导入
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {/*** 创建RedisCacheManager*/@BeanRedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {// 使用RedisCachemanagerRedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));List<String> cacheNames = cacheProperties.getCacheNames();if (!cacheNames.isEmpty()) {// 设置CacheProperties配置的值builder.initialCacheNames(new LinkedHashSet<>(cacheNames));}if (cacheProperties.getRedis().isEnableStatistics()) {// 设置CacheProperties配置的值builder.enableStatistics();}// 装饰redisCacheManagerBuilderredisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));// 装饰redisCacheManagerreturn cacheManagerCustomizers.customize(builder.build());}/*** 如果redisCacheConfiguration不存在则使用默认值*/private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(CacheProperties cacheProperties,ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,ClassLoader classLoader) {return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));}/*** 根据CacheProperties创建默认RedisCacheConfigutation*/private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(CacheProperties cacheProperties, ClassLoader classLoader) {Redis redisProperties = cacheProperties.getRedis();org.springframework.data.redis.cache.RedisCacheConfiguration config =     org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig();config = config.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));// 优先使用CacheProperties的值,若存在的话if (redisProperties.getTimeToLive() != null) {config = config.entryTtl(redisProperties.getTimeToLive());}if (redisProperties.getKeyPrefix() != null) {config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());}if (!redisProperties.isCacheNullValues()) {config = config.disableCachingNullValues();}if (!redisProperties.isUseKeyPrefix()) {config = config.disableKeyPrefix();}return config;}
}

3.2 SimpleCacheConfiguration

// Bean方法不被代理
@Configuration(proxyBeanMethods = false)
// 不存在CacheManager实例
@ConditionalOnMissingBean(CacheManager.class)
// 根据CacheCondition选择导入
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {/*** 创建CacheManager*/@BeanConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,CacheManagerCustomizers cacheManagerCustomizers) {// 使用ConcurrentMapCacheManagerConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();List<String> cacheNames = cacheProperties.getCacheNames();// 如果CacheProperties配置了cacheNames,则使用if (!cacheNames.isEmpty()) {// 配置了cacheNames,则不支持动态创建缓存了cacheManager.setCacheNames(cacheNames);}// 装饰ConcurrentMapCacheManagerreturn cacheManagerCustomizers.customize(cacheManager);}
}

这篇关于SpringCache源码解析(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102143

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [