我们这里以redis作为缓存载体为例,在Springboot里使用redis缓存时只需要需要简单几步配置就可以:
1、引入 spring-boot-starter-data-redis
依赖和配置
2、在Springboot启动类上加上@EnableCaching
注解
3、根据实际的缓存场景,在需要缓存的方法上加上相应的注解(@Cacheable
、@CachePut
、@CacheEvict
等)
这样就可以使用redis缓存了,那么这背后的原理是什么,Springboot是怎么实现的?
Springboot在背后实际做了两件事:
1、根据特定的注解来识别出需要使用缓存的方法并生成动态代理类,加入对缓存的增删改查的切面逻辑
2、根据配置选择缓存的载体,这篇文章以redis作为缓存载体为例
下面看下Springboot是怎么实现这两件事的
生成动态代理类
Spring在生成动态代理类的过程中,抽象了几个重要的参与者:
1、 InfrastructureAdvisorAutoProxyCreator
:负责生成动态代理类,该类根据注册到容器内的 Advisor、Pointcut、Advice
来匹配需要增强的类并生成动态代理类
2、 Pointcut
:指定在什么地方加入切面逻辑,在这里就是被@Cacheable
、@CachePut
、@CacheEvict
等注解标注的方法
3、 Advice
:要做哪些增强逻辑,这里就是根据具体注解对缓存做增删改查的逻辑
4、 Advisor
:封装了 Pointcut
和 Advice
总结一句话就是要告诉Spring容器在什么地方做哪些增强逻辑,下面看下这几个重要参与者是怎么注入到Spring容器的
InfrastructureAdvisorAutoProxyCreator
注册过程
在 @EnableCaching
注解上有个 @Import
注解,该注解上 import
了 CachingConfigurationSelector
类, @Import
注解告诉Springboot需要引入这个bean,该类可以说是总入口,负责向Spring容器注入 上面提到的 InfrastructureAdvisorAutoProxyCreator、Advisor、Pointcut、Advice
,核心逻辑在 selectImports
方法里
这里会根据配置选择是使用PROXY模式还是ASPECTJ模式,这是两种生成动态代理的技术实现,默认是PROXY,这里会向容器注册 AutoProxyRegistrar
和 ProxyCachingConfiguration
两个bean,而在 AutoProxyRegistrar
类(该类实现了 ImportBeanDefinitionRegistrar
接口,属于Springboot的向容器注入bean的一种扩展机制)里会向Spring容器注册 InfrastructureAdvisorAutoProxyCreator
类
动态代理类生成逻辑
InfrastructureAdvisorAutoProxyCreator
类实现了 BeanPostProcessor
接口,父类 AbstractAutoProxyCreator
实现了 postProcessAfterInitialization
方法,该方法会在bean的init方法执行后被Spring容器回调,遍历Spring容器里的 Advisor
,如果 Pointcut
匹配成功,则进行 Advice
增强。
在Bean生成过程中会有很多这种后置处理器,Spring容器会在每个时间点回调这些后置处理器:
Advisor、Pointcut、Advice
上面讲到的 InfrastructureAdvisorAutoProxyCreator
会根据容器内的 Advisor、Pointcut、Advice
来生成动态代理类,这里看下Springboot是怎么向容器注册这三个bean的,前面讲到 @EnableCaching
注解上有个 @Import
注解,该注解上 import
了 CachingConfigurationSelector
类,该类向容器注册 AutoProxyRegistrar
和 ProxyCachingConfiguration
两个bean,而 ProxyCachingConfiguration
负责向容器注入 Advisor、Pointcut、Advice
可以看到这个类会注册 BeanFactoryCacheOperationSourceAdvisor
,该类实现 PointcutAdvisor
接口,用来返回 Pointcut
,类图如下:
CacheOperationSource
类负责告诉容器哪些类和方法需要增强,实际是 Pointcut
的实现
CacheInterceptor
就是具体的增强逻辑了,在这里会根据具体方法上的注解来判断需要做哪些切面逻辑,详细细节可以自己debug
到这里可以看到用来生成动态代理类的几个元素都有了
选择缓存载体
缓存总是要有个载体的,这里以redis为例,在我们引入redis依赖后,Springboot是怎么知道用redis来存放缓存的? 这里涉及到Springboot的自动装载机制了,总结一句话就是Springboot在启动过程中会扫描 META-INF/spring.factories
下的 EnableAutoConfiguration
所指向的所有需要自动装载的类, 其中就有 CacheAutoConfiguration
,该类会 import CacheConfigurationImportSelector
类
CacheConfigurationImportSelector
类会告诉Spring容器加载哪些 CacheManager
,其中就包括 redisCacheManager
而在 RedisCacheConfiguration
类里会加载 RedisCacheManager
这里主要上面的几个条件注解,其中当我们引入了redis的依赖后,就会满足上面的条件,所以如果没有引入redis依赖,那么也就不会注册 RedisCacheManager
,该类实现了 CacheManager
而 Cache
里就是对缓存的怎删改查接口
到这里也弄清楚了Springboot怎么选择 CacheManager
,还有最后一个问题就是上面的切面逻辑是怎么获取到 CacheManager
的?
在前面讲到的 CacheInterceptor
类的 afterSingletonsInstantiated
方法里会遍历注册到容器内的 CacheManager
,这个方法会在bean完成实例化和依赖注入后被容器回调。 到这里核心的步骤都已经讲到,细节需要自己去debug了
作者:谦学客
链接:Springboot注解式缓存原理详解 - 掘金