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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/407641.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Maven的使用

Maven 是一个项目管理工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff0c;Project Object Model&#xff09;的概念&#xff0c;通过一小段描述信息&#xff08;pom.xml&#xff09;来管理项目的构建、报告和文档。Maven 提供了一个标准化的方式来构建项目&#xf…

MyBatis-Plus 三、(进阶使用)

一、typeHandler 的使用 1、存储json格式字段 如果字段需要存储为json格式&#xff0c;可以使用JacksonTypeHandler处理器。使用方式非常简单&#xff0c;如下所示&#xff1a; 只需要加上两个注解即可&#xff1a; TableName(autoResultMap true) 表示自动…

第五节:Nodify 节点位置设置

引言 如果你尝试过前几节的代码&#xff0c;会发现节点都是出现在0,0 位置&#xff0c;及编辑器左上角。编辑器作为最外层的交互控件&#xff0c;内部封装了节点容器ItemContrainer&#xff0c;我们通过样式属性对Loaction做绑定。本节将介绍如何配置节点位置。 1、节点位置 …

DHCP DNS 欺骗武器化——实用指南

DHCP 枚举 在我们之前的文章中,我们分享了 DHCP DNS 欺骗背后的理论。实际上,需要几条信息才能有效地执行我们描述的攻击。对于攻击者来说幸运的是,发现DHCP 服务器并了解其配置的能力是 DHCP 协议的一部分,这使得侦察过程变得微不足道。 在以下章节中,我们将描述攻击者…

Git的使用教程及常用语法02

四.将文件添加到仓库 创建仓库 git init查看仓库的状态 git status 添加到暂存区 git add提交 git commitgit status 可以查看当前仓库的状态信息&#xff0c;例如包含哪些分支&#xff0c;有哪些文件以及这些文件当前处在怎样的一个状态。 由于当前没有存储任何的东西&…

基于Python的机器学习系列(7):多元逻辑回归

在本篇博文中&#xff0c;我们将探讨多元逻辑回归&#xff0c;它是一种扩展的逻辑回归方法&#xff0c;适用于分类数量超过两个的场景。与二元逻辑回归不同&#xff0c;多元逻辑回归使用Softmax函数将多个类别的概率输出映射到[0, 1]范围内&#xff0c;并确保所有类别的概率和为…

PMBOK® 第六版 控制范围

目录 读后感—PMBOK第六版 目录 结果固然重要&#xff0c;过程同样不可或缺。过程不仅是通往预期成果的途径&#xff0c;也是个人和团队能力提升与经验积累的关键阶段。过程中的每一步都是学习和成长的机会&#xff0c;每一次尝试都能激发创新&#xff0c;而公正透明的流程更增…

TCP BBR 数学模型完整版

今天顺带加入了 bbr 的所有状态和所有流程&#xff0c;获得以下的方程组&#xff1a; C Bltbw&#xff0c;R RtProp&#xff0c;T_r ProbeRTT 周期&#xff0c;g1 Startup gain&#xff0c;g2 ProbeBW gain。设 x estimated bandwidth&#xff0c;r round trip time&am…

MySQL 数据库深度解析:安装、语法与高级查询实战

一、引言 在现代软件开发和数据管理领域中&#xff0c;MySQL 数据库凭借其高效性、稳定性、开源性以及广泛的适用性&#xff0c;成为了众多开发者和企业的首选。无论是小型项目还是大型企业级应用&#xff0c;MySQL 都能提供可靠的数据存储和管理解决方案。本文将深入探讨 MyS…

系统编程-lvgl

带界面的MP3播放器 -- lvgl 目录 带界面的MP3播放器 -- lvgl 一、什么是lvgl&#xff1f; 二、简单使用lvgl 在工程中编写代码 实现带界面的mp3播放器 main.c events_init.c events_init.h 补充1&#xff1a;glob函数 补充2&#xff1a;atexit函数 一、什么是lvgl&a…

安科瑞ACREL-7000能源管控平台在综合能耗监测系统在大型园区的应用

摘要&#xff1a;大型综合园区已经成为多种能源消耗的重要区域&#xff0c;为了探索适用于大型综合园区的综合能耗监测系统&#xff0c;建立了综合能耗监测系统整体框架&#xff0c;提出了综合能耗网络、能耗关系集合、能耗均衡度等概念&#xff0c;并以某大型综合园区为例对综…

AIGC综合应用-黑神话悟空创意写真大片制作方法(实操附模型文件)

​ 怎么用AI来制作 这种黑悟空的现代时尚大片&#xff1f; 悟空不再只是传统的西游记形象&#xff0c;而是走上现代时尚的T台&#xff0c;成为时尚大片中的主角。这个创意乍一听似乎有些离奇&#xff0c;但通过AI技术的加持&#xff0c;这一切都能轻松实现。不需要昂贵的拍摄设…

自编码器(Autoencoder, AE):深入理解与应用

自编码器&#xff08;Autoencoder, AE&#xff09;&#xff1a;深入理解与应用 引言 自编码器&#xff08;Autoencoder, AE&#xff09;是一种通过无监督学习方式来学习数据有效表示的神经网络模型。其核心思想是通过编码器将输入数据压缩成低维潜在表示&#xff0c;然后通过…

dokcer 安装 redis(单机版)

准备工作 拉取redis镜像 docker pull redis 通过docker-compose 安装redis 很方便、很简单 先安装docker&#xff0c;参考我这个安装示例进行安装 https://blog.csdn.net/qq_33192671/article/details/13714973 然后安装docker-compose&#xff0c;要是拉取docker-compose无…

低代码与AI:赋能企业数字化转型

引言 随着全球经济的快速发展和科技的飞速进步&#xff0c;数字化转型已成为各个行业和企业发展的重要趋势。数字化转型的背景不仅是提升效率和竞争力的手段&#xff0c;更是适应市场变化、满足客户需求的必由之路。 在当今信息化时代&#xff0c;技术的变革推动了企业运营方式…

Java语言程序设计——篇十七(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

探索人工智能的未来:埃里克·施密特2024斯坦福大学分享六

代理与文本生成模型的未来展望 您认为明年代理或文本生成模型会出现通货膨胀点吗&#xff1f; 不&#xff0c;不会。 我听到了类似的观点&#xff0c;尤其是埃里克科维茨的看法。他有一个很好的方式来阐述这三个趋势。虽然我之前也听说过这些趋势&#xff0c;但将它们整合起…

helm安装jenkins保姆级别

一、创建nfs服务器 这一步跳过、自行百度 注意&#xff1a;要给共享目录赋予权限chmod一下&#xff0c;不然到时候容器没办法在目录里面创建文件&#xff0c;初始化时候会报错误代码2 二、添加Jenkins的Helm仓库 helm repo add jenkinsci https://charts.jenkins.io helm re…

python dash框架 油气田可视化软件设计文档

V1.1:机器学习框架(神经网络) 时间范围优化 表格布局优化 添加前端设计元素布局 V1.0&#xff1a;基础布局和对应计算函数 要求 首先第一部分是通过神经网络预测天然气流量&#xff0c;其中输入开始时间和截止时间是为了显示这一段时间内的天然气流量预测结果 第二部分&…

前端宝典十三:node网络详解Tcp/IP/Http及网络安全防御

讨论网络相关的问题前&#xff0c;我们首先看一下从浏览器输入 URL 到显示前端页面的流程&#xff0c;首先从TCP的应用层、传输层、网络层、数据链路层开始看&#xff1a; 一、应用层、传输层、网络层、数据链路层 以下是从浏览器输入 URL 到显示前端页面的流程顺序解析&…