最近想写批量操作的缓存 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) |