前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么是Spring的缓存抽象?

什么是Spring的缓存抽象?

作者头像
凯哥的Java技术活
发布2022-07-08 14:15:39
3900
发布2022-07-08 14:15:39
举报
文章被收录于专栏:凯哥的Java技术活

在项目中是否有遇到过这样的需求?

你负责的项目需要一个功能,但是这个负责实现这个功能却不是你,很可能是第三方厂商给你的一个服务。

那么如何保证插件化呢?

有时候我会想spring是怎么做的,spring如何做到适配不同的数据库连接池,不同的缓存组件呢?

带着疑问,我翻阅了下Spring的源码,关于缓存抽象这部分。

首先我去查看官方文档,

Spring Boot只留下了简短的几行话,首先是开启@EnableCaching,然后更多文档请移步Spring Framework的文档。

行吧,那去Spring Framework文档看下

这里吐槽下,Spring文档的左边大纲不见好久了,每次都不能愉快的翻来翻去了。

文档较长,我这里总结下。

  1. 讲了缓存是干嘛的
  2. 讲了@Cacheable @CacheEvict等注解怎么用的
  3. 讲了缓存的key如何生成
  4. 讲了条件缓存
  5. 讲了如何自定义缓存注解
  6. 讲了JSR-107注解
  7. 讲了几种缓存的实现组件 ConcurrentHashMap/Caffine等实现

大部分是将如何使用的,那我们试试。

如果只是使用ConcurrentHashMap这种类型的缓存的话,我们甚至都不需要依赖spring-boot-starter-cahce(spring-context-support)这个依赖就可以使用。

依赖如下(Gradle)

这里有一个特别简单的方法, 就是传入一个参数,获取一个字符串.

主方法如下,加上@EnableCaching注解

运行后可以发现,调用了两次方法,但是实际只执行了一次

缓存生效了。

那么现在用的是哪种缓存呢?我们根据官网可以看到有很多种。

我们知道想要生效,指定是经过了某些配置的,那么一定是在autoconfigure包内,于是我们找到了CacheAutoConfiguration.

里面有个方法,往spring容器注册bean定义

于是跟着找到了如下配置

CacheAutoConfiguration导入了这么多的缓存配置,有EnCache的,有Redis的,有CAFFEINE的,真正生效的是哪个呢?

于是我们把所有的配置都打上断点,发现最终是SimpleCacheConfiguration生效

那么问题来了,为什么这么多Configuration,偏偏只是SimpleCacheConfiguration生效呢?更何况按照上面的顺序,SimpleCacheConfiguration甚至还是倒数第二的优先级。

那我们首先肯定是想要这些配置类首先是需要被解析的(不了解的可以看我之前写的spring bean的声明周期),解析Configuration Class, 然后注册bean定义,当然这里注册肯定是有条件的。

那我们现在想要搞明白的是,到底其他的9个配置类有没有注册bean定义到spring容器中呢?

我们现在知道什么?

CacheAutoConfiguration自动配置类,导入了10个Cache的配置。然后只有一个配置类生效。

那我们不妨把断点打到导入配置的部分来,也就是这里

重新启动下,查看调用栈

可以看到,在refresh,调用了invokeBeanFactoryPostProcessors,然后来到ConfigurationClassPostProcessor的processConfigBeanDefinitions()的方法中。

从名字我们就能猜出来,这个解析标注了@Configuration的类的。

然后进入到这个方法中

人家的注释也印证了我们的猜想,这个是解析所有的@Configuration类。

然后执行ConfigurationClassParser的parse方法。

在ConfigurationClassParser 有几个重要的方法:

processImports

processConfigurationClass

doProcessConfigurationClass

首先是解析CacheAutoConfiguration这个类,进入到processImports方法,processImports方法内会调用processConfigurationClass方法,然后调用doProcessConfigurationClass方法,然后会接着调用processImports,然后解析ImportSelector,获得了10个类。

也就是说在解析CacheAutoConfiguration时,加载到了10个CacheConfiguration,然后再递归解析每个配置类。

接下来比较有趣。

会调用一个asSourceClasses方法,因为我们得到的一个类的全限定名,又不是一个真正的Class对象,那我们势必是要转化一波的。

当我们解析到JCacheConfiguration时,发现在

这一步时,永远都会返回一个object对象,这里是因为这一步测试调用了相当于@ConditionalOnClass。

我们看看是怎么做的?

其实特别简单,就是Class.forName调用一下,没抛出异常就说明@ConditionOnClass匹配成功。于是就返回true。

于是上一步就返回了Object对象。

于是根据导入的10个Configuration上面的@ConditinalOnXxx。生成了如下的配置类

接下来我们把断点直接放到

可以看到,在ConfigurationParser解析完毕后,关于Cache的配置类有四个。其中一个是Object类。

然后将这个四个配置注册到bean定义中,当然,首先还是要判断@Conditioanl的,@Conditional 有两个状态 一个是解析Configuration时生效,一个是注册bean定义时。在注册bean定义的时候就用到了@ConditionalOnBean了。

然后在注册bean定义时,过滤掉了GenericCacheConfiguration和NoOpsConfiguration这两个配置类。

所以到最后只有SimpleCacheConfiguration生效了。

以上是简单的缓存实现,使用的ConcurrentHashMap进行缓存。

那我们换成Caffine试一下

这里看出来,应该是有这两个类应该就能生效了。

那我们试试看,如下图,我们加入caffeine的依赖,查看是否真的能够自动配置成功?

我们看到,确实是caffeine的配置生效了。

注意,这里面有个坑!

如果caffeine的版本不对,很可能配置不生效,因为ClassLoader加载类失败,导致OnClassCondition过不去,从而导致caffeine的配置不起作用。

为什么只加入了caffeine的实现,spring就能操作caffeine,就好像你操作其他的缓存实现一样呢?

这就奇了怪了,caffeine又不是Spring自己写的东西,给我们的感觉就像caffeine实现了spring的cache相关的接口似的。

其实我们翻开spring-boot-starter-cache这个依赖,会发现里面只有一个spring-context-support的依赖。

在spring-context-support的依赖内

Spring对caffeine进行了一层包装,使得spring的缓存抽象能够完成像其他缓存一样的操作。

似乎这可以说是 策略模式?适配器模式?装饰者模式?

或者说是防腐层?

似乎都是,你们觉得呢?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 凯哥的Java技术活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档