最近想写批量操作的缓存 AOP,于是研究了一下 Spring 原生的 Cache 源码,以便编些自己的 BatchCache AOP。
Spring Cache
配置(Configuration)
基类:AbstractCachingConfiguration
包含几个关键配置对象可供配置:
1 | protected AnnotationAttributes enableCaching; |
入口配置:ProxyCachingConfiguration
1 | (name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME) |
注解解析器(Annotation Parser)
CacheAnnotationParser 接口
包含两个方法的声明。
1 | public interface CacheAnnotationParser { |
能够将 class 或 method 的 cache 注解解析为 CacheOperation
操作。
SpringCacheAnnotationParser 类
SpringCacheAnnotationParser
实现了 CacheAnnotationParser
。同时还实现了 Serializable
。
两个 parseCacheAnnotations
方法的内部实现,都是首先通过 getDefaultCacheConfig(type)
(对于 method
,参数取 method.getDeclaringClass()
)取一个默认的 cache 配置,然后走同一套 parseCacheAnnotations(defaultConfig, type)
逻辑。
基本上调用方只有 AnnotationCacheOperationSource
。
parseCacheAnnotations
通过 getAllMergedAnnotations
方法,将类型(Class<?>
或 Method
)的 Cacheable
、CacheEvict
、CachePut
、Caching
注解分别取出,分别通过 parseCacheableAnnotation
、parseEvictAnnotation
、parsePutAnnotation
、parseCachingAnnotation
方法解析为成 CacheOperation,最终合并为一个集合。
前三个 parseXXXAnnotation 的方法基本类似,取出注解中的参数(name、cacheName、key 等等,前三个注解略有不同),与 defaultConfig 合并,校验参数并返回。parseCachingAnnotation
是前三个注解的组合注解,所以将其内部的三种分别取出,再执行前三个 parseXXXAnnotation 方法。代码如下:
1 | CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) { |
最终校验
CacheableOperation
时,key 和 keyGenerator 至少需要有一个定义,cacheManager 和 cacheResolver 至少需要有一个定义。
缓存操作源
CacheOperationSource 接口
只定义了一个方法,用于获取 CacheOperation 集合
1 | Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass); |
AbstractFallbackCacheOperationSource
做了一层缓存。
由 computeCacheOperations(Method method, Class<?> targetClass)
计算 CacheOperation 集合。
取 class 的 CacheConfig 注解作为 DefaultCacheConfig
,没有则全为 null。
也就是说,Method 的 CacheConfig 定义需要在声明它的 class 上。
computeCacheOperations 方法
方法声明为:
1 | private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) |
首先,如果需要的话,过滤只考虑 public 方法。(默认需要过滤)
其次,通过 ClassUtils.getMostSpecificMethod
和 BridgeMethodResolver.findBridgedMethod
找到最合适的 method 定义。
桥接方法是为了兼容 Java 1.5 没有泛型语义时允许传递 Object 的问题而由编译器自动生成的方法。
先使用 findCacheOperations(Class<?> clazz)
尝试从方法上解析 cache 操作,有则直接返回。没有则尝试使用 findCacheOperations(Method method)
从定义方法的类上解析,有则直接返回。
都取不到则尝试从原方法上用相同的流程解析。
都解析不到则返回 null。
AnnotationCacheOperationSource 类
AnnotationCacheOperationSource
继承了 AbstractFallbackCacheOperationSource
并实现了 Serializable
。
AnnotationCacheOperationSource 会读取 Spring 的 Cacheable
、 CachePut
和 CacheEvict
批注,并将相应的 cache operation 定义公开给 Spring 的缓存基础结构。此类也可以用作自定义 CacheOperationSource 的基类。
内部定义了一个 Set<CacheAnnotationParser> annotationParsers
保存用到的注解分析器。
提供了自定义是否只使用 public 方法和自定义缓存注解解析器(CacheAnnotationParser
)的构造方法。默认只解析 public 方法,且解析器集合只有上文提到的 SpringCacheAnnotationParser
。
determineCacheOperations(provider)
确定给定 CacheOperationProvider
的 CacheOperation
。该实现委托配置的 CacheAnnotationParser
(默认为 SpringCacheAnnotationParser)来将已知的注解解析为 Spring 的元 attribute 类。可以重写以支持带有 caching metadata 的自定义注解。参数 provider
是要使用的 cache operation 提供者,类型为 CacheOperationProvider
。
定义了基类使用的 findCacheOperations
方法,实现是遍历所有 annotationParsers
,调用其 parseCacheAnnotations
,然后将 cache 操作合并为一个 set。
CacheOperationSource 用法小结
这个类,最终对外提供的是定义在 AbstractFallbackCacheOperationSource
下的 getCacheOperations(Method method, Class<?> targetClass)
方法。用于 CacheAspectSupport
和 CacheOperationSourcePointcut
。
Pointcut
与 Cache 无关的 AOP 基类
StaticMethodMatcherPointcut -> Pointcut
StaticMethodMatcherPointcut -> StaticMethodMatcher -> MethodMatcher
CacheOperationSourcePointcut
是一个抽象类,继承自 StaticMethodMatcherPointcut,实现了 Serializable。
有一个待实现的 getCacheOperationSource
方法,以返回缓存操作源。
实现了基类的 match 方法。
1 |
|
也就是说,如果 cacheOperationSource
非空且 cacheOperationSource.getCacheOperations(method, targetClass)
取到了一个或多个 cache 操作则可以匹配上。
缓存操作的调用上下文 CacheOperationInvocationContext
缓存操作的调用上下文类,提供四个接口:
1 | public interface CacheOperationInvocationContext<O extends BasicOperation> { |
泛型的基类定义如下:
1 | public interface BasicOperation { |
缓存解析器(cache resolver)
CacheResolver
1 | public interface CacheResolver { |
AbstractCacheResolver、SimpleCacheResolver 和 NamedCacheResolver
抽象类,实现了 CacheResolver 接口,框架提供了两个默认实现 SimpleCacheResolver 和 NamedCacheResolver。
区别在于 SimpleCacheResolver 取 Operation 上的 cacheNames,NamedCacheResolver 手动指定 cacheNames。
而后都遍历 cacheNames 执行 CacheManager.getCache(cacheName) 并整合至同一集合并返回。
CacheResolver 用法小结
对外提供了 resolveCaches 方法,主要用在 CacheAspectSupport
。
Interceptor 拦截器
AbstractCacheInvoker
在基本的 Cache
类的 doGet、doPut、doEvict 方法外包了一层 try-catch 并定义了异常日志的打印方式。默认直接抛出异常。
CacheAspectSupport
抽象类,CacheAspectSupport 继承自 AbstractCacheInvoker,还实现了 BeanFactoryAware, InitializingBean, SmartInitializingSingleton。
涉及到众多需要 set 的依赖:
1 | private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = |
定义了一些对 Optional 的解包逻辑。
关键是 execute 方法。真正在方法执行前后对缓存执行对应操作。
1 | private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) { |
CacheInterceptor
继承自 CacheAspectSupport,实现了 MethodInterceptor 和 Serializable
将 CacheAspectSupport 父类的 execute 方法包装为接口的 invoke 方法的实现。
Advisor
与 Cache 无关的 AOP advisor 基类
AbstractBeanFactoryPointcutAdvisor -> AbstractPointcutAdvisor -> PointcutAdvisor -> Advisor
BeanFactoryCacheOperationSourceAdvisor
继承自 AbstractBeanFactoryPointcutAdvisor。
getPointcut 方法返回一个 CacheOperationSourcePointcut 的实现,getCacheOperationSource 返回用户自定义的 CacheOperationSource。
注册时代码如下:
1 | (name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME) |