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        }    }}