Spring cache是一个缓存API层,封装了对多种缓存的通用操作,可以借助注解方便地为程序添加缓存功能。

常见的注解有@Cacheable、@CachePut、@CacheEvict,有没有想过背后的原理是什么?楼主带着疑问,阅读完Spring cache的源码后,做一个简要总结。

先说结论,核心逻辑在CacheAspectSupport类,封装了所有的缓存操作的主体逻辑,下面详细介绍。

题外话:如何阅读开源代码?

有2种方法,可以结合起来使用:

  • 静态代码阅读:查找关键类、方法的usage之处,熟练使用find usages功能,找到所有相关的类、方法,静态分析核心逻辑的执行过程,一步步追根问底,直至建立全貌
  • 运行时debug:在关键方法上加上断点,并且写一个单元测试调用类库/框架,熟练使用step into/step over/resume来动态分析代码的执行过程

核心类图

如图所示,可以分成以下几类class:

  • Cache、CacheManager:Cache抽象了缓存的通用操作,如get、put,而CacheManager是Cache的集合,之所以需要多个Cache对象,是因为需要多种缓存失效时间、缓存条目上限等
  • CacheInterceptor、CacheAspectSupport、AbstractCacheInvoker:CacheInterceptor是一个AOP方法拦截器,在方法前后做额外的逻辑,也即查询缓存、写入缓存等,它继承了CacheAspectSupport(缓存操作的主体逻辑)、AbstractCacheInvoker(封装了对Cache的读写)
  • CacheOperation、AnnotationCacheOperationSource、SpringCacheAnnotationParser:CacheOperation定义了缓存操作的缓存名字、缓存key、缓存条件condition、CacheManager等,AnnotationCacheOperationSource是一个获取缓存注解对应CacheOperation的类,而SpringCacheAnnotationParser是真正解析注解的类,解析后会封装成CacheOperation集合供AnnotationCacheOperationSource查找

源码分析(带注释解释)

下面对Spring cache源码做分析,带注释解释,只摘录核心代码片段。

1、解析注解

首先看看注解是如何解析的。注解只是一个标记,要让它真正工作起来,需要对注解做解析操作,并且还要有对应的实际逻辑。

SpringCacheAnnotationParser:负责解析注解,返回CacheOperation集合

public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {

        // 解析类级别的缓存注解
@Override
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
} // 解析方法级别的缓存注解
@Override
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
} // 解析缓存注解
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = null; // 解析@Cacheable注解
Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
if (!cacheables.isEmpty()) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
} // 解析@CacheEvict注解
Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
if (!evicts.isEmpty()) {
ops = lazyInit(ops);
for (CacheEvict evict : evicts) {
ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
}
} // 解析@CachePut注解
Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
if (!puts.isEmpty()) {
ops = lazyInit(ops);
for (CachePut put : puts) {
ops.add(parsePutAnnotation(ae, cachingConfig, put));
}
} // 解析@Caching注解
Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
if (!cachings.isEmpty()) {
ops = lazyInit(ops);
for (Caching caching : cachings) {
Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
if (cachingOps != null) {
ops.addAll(cachingOps);
}
}
} return ops;
}

AnnotationCacheOperationSource:调用SpringCacheAnnotationParser获取注解对应CacheOperation

public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {

        // 查找类级别的CacheOperation列表
@Override
protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
return determineCacheOperations(new CacheOperationProvider() {
@Override
public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {
return parser.parseCacheAnnotations(clazz);
}
}); } // 查找方法级别的CacheOperation列表
@Override
protected Collection<CacheOperation> findCacheOperations(final Method method) {
return determineCacheOperations(new CacheOperationProvider() {
@Override
public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {
return parser.parseCacheAnnotations(method);
}
});
} }

AbstractFallbackCacheOperationSource:AnnotationCacheOperationSource的父类,实现了获取CacheOperation的通用逻辑

public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {

	/**
* Cache of CacheOperations, keyed by method on a specific target class.
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
private final Map<Object, Collection<CacheOperation>> attributeCache =
new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024); // 根据Method、Class反射信息,获取对应的CacheOperation列表
@Override
public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
} Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey); // 因解析反射信息较耗时,所以用map缓存,避免重复计算
// 如在map里已记录,直接返回
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
// 否则做一次计算,然后写入map
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
} // 计算缓存操作列表,优先用target代理类的方法上的注解,如果不存在则其次用target代理类,再次用原始类的方法,最后用原始类
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
} // The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); // 调用findCacheOperations(由子类AnnotationCacheOperationSource实现),最终通过SpringCacheAnnotationParser来解析
// First try is the method in the target class.
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
} // Second try is the caching operation on the target class.
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
} if (specificMethod != method) {
// Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// Last fallback is the class of the original method.
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
} return null;
}

2、逻辑执行

以@Cacheable背后的逻辑为例。预期是先查缓存,如果缓存命中了就直接使用缓存值,否则执行业务逻辑,并把结果写入缓存。

ProxyCachingConfiguration:是一个配置类,用于生成CacheInterceptor类和CacheOperationSource类的Spring bean

CacheInterceptor:是一个AOP方法拦截器,它通过CacheOperationSource获取第1步解析注解的CacheOperation结果(如缓存名字、缓存key、condition条件),本质上是拦截原始方法的执行,在之前、之后增加逻辑

// 核心类,缓存拦截器
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable { // 拦截原始方法的执行,在之前、之后增加逻辑
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod(); // 封装原始方法的执行到一个回调接口,便于后续调用
CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
@Override
public Object invoke() {
try {
// 原始方法的执行
return invocation.proceed();
}
catch (Throwable ex) {
throw new ThrowableWrapper(ex);
}
}
}; try {
// 调用父类CacheAspectSupport的方法
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
} }

CacheAspectSupport:缓存切面支持类,是CacheInterceptor的父类,封装了所有的缓存操作的主体逻辑

主要流程如下:

  1. 通过CacheOperationSource,获取所有的CacheOperation列表
  2. 如果有@CacheEvict注解、并且标记为在调用前执行,则做删除/清空缓存的操作
  3. 如果有@Cacheable注解,查询缓存
  4. 如果缓存未命中(查询结果为null),则新增到cachePutRequests,后续执行原始方法后会写入缓存
  5. 缓存命中时,使用缓存值作为结果;缓存未命中、或有@CachePut注解时,需要调用原始方法,使用原始方法的返回值作为结果
  6. 如果有@CachePut注解,则新增到cachePutRequests
  7. 如果缓存未命中,则把查询结果值写入缓存;如果有@CachePut注解,也把方法执行结果写入缓存
  8. 如果有@CacheEvict注解、并且标记为在调用后执行,则做删除/清空缓存的操作
// 核心类,缓存切面支持类,封装了所有的缓存操作的主体逻辑
public abstract class CacheAspectSupport extends AbstractCacheInvoker
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton { // CacheInterceptor调父类的该方法
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
// 通过CacheOperationSource,获取所有的CacheOperation列表
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
// 继续调一个private的execute方法执行
return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));
}
} // 如果spring bean未初始化完成,则直接调用原始方法。相当于原始方法没有缓存功能。
return invoker.invoke();
} private的execute方法
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = context.getCaches().iterator().next();
try {
return wrapCacheValue(method, cache.get(key, new Callable<Object>() {
@Override
public Object call() throws Exception {
return unwrapReturnValue(invokeOperation(invoker));
}
}));
}
catch (Cache.ValueRetrievalException ex) {
// The invoker wraps any Throwable in a ThrowableWrapper instance so we
// can just make sure that one bubbles up the stack.
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
} // 如果有@CacheEvict注解、并且标记为在调用前执行,则做删除/清空缓存的操作
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT); // 如果有@Cacheable注解,查询缓存
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); // 如果缓存未命中(查询结果为null),则新增到cachePutRequests,后续执行原始方法后会写入缓存
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
} Object cacheValue;
Object returnValue; if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
// 缓存命中的情况,使用缓存值作为结果
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// 缓存未命中、或有@CachePut注解的情况,需要调用原始方法
// Invoke the method if we don't have a cache hit
// 调用原始方法,得到结果值
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
} // 如果有@CachePut注解,则新增到cachePutRequests
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); // 如果缓存未命中,则把查询结果值写入缓存;如果有@CachePut注解,也把方法执行结果写入缓存
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
} // 如果有@CacheEvict注解、并且标记为在调用后执行,则做删除/清空缓存的操作
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); return returnValue;
} private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
for (CacheOperationContext context : contexts) {
// 如果满足condition条件,才查询缓存
if (isConditionPassing(context, result)) {
// 生成缓存key,如果注解中指定了key,则按照Spring表达式解析,否则使用KeyGenerator类生成
Object key = generateKey(context, result);
// 根据缓存key,查询缓存值
Cache.ValueWrapper cached = findInCaches(context, key);
if (cached != null) {
return cached;
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
} private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
for (Cache cache : context.getCaches()) {
// 调用父类AbstractCacheInvoker的doGet方法,查询缓存
Cache.ValueWrapper wrapper = doGet(cache, key);
if (wrapper != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}
return wrapper;
}
}
return null;
}

AbstractCacheInvoker:CacheAspectSupport的父类,封装了最终查询Cache接口的逻辑

public abstract class AbstractCacheInvoker {
// 最终查询缓存的方法
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
try {
// 调用Spring Cache接口的查询方法
return cache.get(key);
}
catch (RuntimeException ex) {
getErrorHandler().handleCacheGetError(ex, cache, key);
return null; // If the exception is handled, return a cache miss
}
}
}

Spring cache源码分析的更多相关文章

  1. spring AOP源码分析(三)

    在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...

  2. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  3. Spring Boot源码分析-启动过程

    Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...

  4. 精尽Spring MVC源码分析 - ViewResolver 组件

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  5. Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析

    Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...

  6. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  7. Spring Ioc源码分析系列--Bean实例化过程(二)

    Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...

  8. Spring Security 源码分析(四):Spring Social实现微信社交登录

    社交登录又称作社会化登录(Social Login),是指网站的用户可以使用腾讯QQ.人人网.开心网.新浪微博.搜狐微博.腾讯微博.淘宝.豆瓣.MSN.Google等社会化媒体账号登录该网站. 前言 ...

  9. spring事务源码分析结合mybatis源码(一)

    最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...

随机推荐

  1. Gateway 简介

    概述 微服务可能分布在不同的主机上,这样有许多缺点:前端需要硬编码调用不同地址的微服务很麻烦:存在跨域访问的问题:微服务地址直接暴露是不安全的.还有所以需要为前端提供一个统一的访问入口.Gateway ...

  2. python中面向对象知识框架

    案列: 1 class Chinese: # 类的创建,类名首字母要大写 2 eye = 'black' # 类属性的创建 3 4 def __init__(self,hometown): # 类的初 ...

  3. Java泛型的那些事

    1.泛型概述 1.1.为什么使用泛型 没有泛型,在编写代码时只能使用具体类型或Object类型,无法做到使用者想要使用什么类型就是类型.比如:创建一个方法,形参需要指定需要使用的数据类型,在创建方法之 ...

  4. 商业智能bi行业现状,BI应用的3个层次

    ​商业智能bi行业现状.传统的报表系统技术上已经相当成熟,大家熟悉的Excel等都已经被广泛使用.但是,随着数据的增多,需求的提高,传统报表系统面临的挑战也越来越多. 1. 数据太多,信息太少 密密麻 ...

  5. python基础之序列类型的方法——列表&元组

    Hello大家好,我是python学习者小杨同学,上次跟大家分享关于python的数值类型和序列类型,本次就承接上一节的内容,说一说序列类型的方法. 序列类型的方法,简单的来说就是四个字:增删改查.随 ...

  6. NFA转化为DFA

    NFA(不确定的有穷自动机)转化为DFA(确定的有穷自动机) NFA转换DFA,通常是将带空串的NFA(即:ε-NFA)先转化为不带空串的NFA(即:NFA),然后再转化为DFA. 提示:ε是空串的意 ...

  7. RDD的运行机制

    1. RDD 的设计与运行原理 Spark 的核心是建立在统一的抽象 RDD 之上,基于 RDD 的转换和行动操作使得 Spark 的各个组件可以无缝进行集成,从而在同一个应用程序中完成大数据计算任务 ...

  8. 【C# 异常处理】StackTrace 堆栈跟踪

    作用 在使用.NET编写的代码在debug时很容易进行排查和定位问题,一旦项目上线并出现问题的话那么只能依靠系统日志来进行问题排查和定位,但当项目复杂时,即各种方法间相互调用将导致要获取具体的出错方法 ...

  9. 图解volatile

    volatile是什么 出去面试的时候,很多面试官都会问你:说说你对volatile的理解. 下面我将用图的方式告诉大家,volatile是什么? 如上图所示:每个线程都有自己的工作内存,同时还能访问 ...

  10. 渗透测试中dns log的使用

    转至:https://www.cnblogs.com/rnss/p/11320305.html 一.预备知识 dns(域名解析): 域名解析是把域名指向网站空间IP,让人们通过注册的域名可以方便地访问 ...