Spring?cache源碼深度解析
前言
Spring cache是一個(gè)緩存API層,封裝了對(duì)多種緩存的通用操作,可以借助注解方便地為程序添加緩存功能。
常見的注解有@Cacheable、@CachePut、@CacheEvict,有沒有想過背后的原理是什么?樓主帶著疑問,閱讀完Spring cache的源碼后,做一個(gè)簡(jiǎn)要總結(jié)。
先說(shuō)結(jié)論,核心邏輯在CacheAspectSupport類,封裝了所有的緩存操作的主體邏輯,下面詳細(xì)介紹。
題外話:如何閱讀開源代碼?
有2種方法,可以結(jié)合起來(lái)使用:
- 靜態(tài)代碼閱讀:查找關(guān)鍵類、方法的usage之處,熟練使用find usages功能,找到所有相關(guān)的類、方法,靜態(tài)分析核心邏輯的執(zhí)行過程,一步步追根問底,直至建立全貌
- 運(yùn)行時(shí)debug:在關(guān)鍵方法上加上斷點(diǎn),并且寫一個(gè)單元測(cè)試調(diào)用類庫(kù)/框架,熟練使用step into/step over/resume來(lái)動(dòng)態(tài)分析代碼的執(zhí)行過程
核心類圖
如圖所示,可以分成以下幾類class:
- Cache、CacheManager:Cache抽象了緩存的通用操作,如get、put,而CacheManager是Cache的集合,之所以需要多個(gè)Cache對(duì)象,是因?yàn)樾枰喾N緩存失效時(shí)間、緩存條目上限等
- CacheInterceptor、CacheAspectSupport、AbstractCacheInvoker:CacheInterceptor是一個(gè)AOP方法攔截器,在方法前后做額外的邏輯,也即查詢緩存、寫入緩存等,它繼承了CacheAspectSupport(緩存操作的主體邏輯)、AbstractCacheInvoker(封裝了對(duì)Cache的讀寫)
- CacheOperation、AnnotationCacheOperationSource、SpringCacheAnnotationParser:CacheOperation定義了緩存操作的緩存名字、緩存key、緩存條件condition、CacheManager等,AnnotationCacheOperationSource是一個(gè)獲取緩存注解對(duì)應(yīng)CacheOperation的類,而SpringCacheAnnotationParser是真正解析注解的類,解析后會(huì)封裝成CacheOperation集合供AnnotationCacheOperationSource查找
源碼分析(帶注釋解釋)
下面對(duì)Spring cache源碼做分析,帶注釋解釋,只摘錄核心代碼片段。
1、解析注解
首先看看注解是如何解析的。注解只是一個(gè)標(biāo)記,要讓它真正工作起來(lái),需要對(duì)注解做解析操作,并且還要有對(duì)應(yīng)的實(shí)際邏輯。
SpringCacheAnnotationParser:負(fù)責(zé)解析注解,返回CacheOperation集合
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable { // 解析類級(jí)別的緩存注解 @Override public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) { DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type); return parseCacheAnnotations(defaultConfig, type); } // 解析方法級(jí)別的緩存注解 @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:調(diào)用SpringCacheAnnotationParser獲取注解對(duì)應(yīng)CacheOperation
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable { // 查找類級(jí)別的CacheOperation列表 @Override protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) { return determineCacheOperations(new CacheOperationProvider() { @Override public Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) { return parser.parseCacheAnnotations(clazz); } }); } // 查找方法級(jí)別的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的父類,實(shí)現(xiàn)了獲取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); // 根據(jù)Method、Class反射信息,獲取對(duì)應(yīng)的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); // 因解析反射信息較耗時(shí),所以用map緩存,避免重復(fù)計(jì)算 // 如在map里已記錄,直接返回 if (cached != null) { return (cached != NULL_CACHING_ATTRIBUTE ? cached : null); } // 否則做一次計(jì)算,然后寫入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; } } // 計(jì)算緩存操作列表,優(yōu)先用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); // 調(diào)用findCacheOperations(由子類AnnotationCacheOperationSource實(shí)現(xiàn)),最終通過SpringCacheAnnotationParser來(lái)解析 // 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、邏輯執(zhí)行
以@Cacheable背后的邏輯為例。預(yù)期是先查緩存,如果緩存命中了就直接使用緩存值,否則執(zhí)行業(yè)務(wù)邏輯,并把結(jié)果寫入緩存。
ProxyCachingConfiguration:是一個(gè)配置類,用于生成CacheInterceptor類和CacheOperationSource類的Spring bean
CacheInterceptor:是一個(gè)AOP方法攔截器,它通過CacheOperationSource獲取第1步解析注解的CacheOperation結(jié)果(如緩存名字、緩存key、condition條件),本質(zhì)上是攔截原始方法的執(zhí)行,在之前、之后增加邏輯
// 核心類,緩存攔截器 public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable { // 攔截原始方法的執(zhí)行,在之前、之后增加邏輯 @Override public Object invoke(final MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); // 封裝原始方法的執(zhí)行到一個(gè)回調(diào)接口,便于后續(xù)調(diào)用 CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() { @Override public Object invoke() { try { // 原始方法的執(zhí)行 return invocation.proceed(); } catch (Throwable ex) { throw new ThrowableWrapper(ex); } } }; try { // 調(diào)用父類CacheAspectSupport的方法 return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments()); } catch (CacheOperationInvoker.ThrowableWrapper th) { throw th.getOriginal(); } } }
CacheAspectSupport:緩存切面支持類,是CacheInterceptor的父類,封裝了所有的緩存操作的主體邏輯
主要流程如下:
- 通過CacheOperationSource,獲取所有的CacheOperation列表
- 如果有@CacheEvict注解、并且標(biāo)記為在調(diào)用前執(zhí)行,則做刪除/清空緩存的操作
- 如果有@Cacheable注解,查詢緩存
- 如果緩存未命中(查詢結(jié)果為null),則新增到cachePutRequests,后續(xù)執(zhí)行原始方法后會(huì)寫入緩存
- 緩存命中時(shí),使用緩存值作為結(jié)果;緩存未命中、或有@CachePut注解時(shí),需要調(diào)用原始方法,使用原始方法的返回值作為結(jié)果
- 如果有@CachePut注解,則新增到cachePutRequests
- 如果緩存未命中,則把查詢結(jié)果值寫入緩存;如果有@CachePut注解,也把方法執(zhí)行結(jié)果寫入緩存
- 如果有@CacheEvict注解、并且標(biāo)記為在調(diào)用后執(zhí)行,則做刪除/清空緩存的操作
// 核心類,緩存切面支持類,封裝了所有的緩存操作的主體邏輯 public abstract class CacheAspectSupport extends AbstractCacheInvoker implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton { // CacheInterceptor調(diào)父類的該方法 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)) { // 繼續(xù)調(diào)一個(gè)private的execute方法執(zhí)行 return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass)); } } // 如果spring bean未初始化完成,則直接調(diào)用原始方法。相當(dāng)于原始方法沒有緩存功能。 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注解、并且標(biāo)記為在調(diào)用前執(zhí)行,則做刪除/清空緩存的操作 // 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)); // 如果緩存未命中(查詢結(jié)果為null),則新增到cachePutRequests,后續(xù)執(zhí)行原始方法后會(huì)寫入緩存 // 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)) { // 緩存命中的情況,使用緩存值作為結(jié)果 // If there are no put requests, just use the cache hit cacheValue = cacheHit.get(); returnValue = wrapCacheValue(method, cacheValue); } else { // 緩存未命中、或有@CachePut注解的情況,需要調(diào)用原始方法 // Invoke the method if we don't have a cache hit // 調(diào)用原始方法,得到結(jié)果值 returnValue = invokeOperation(invoker); cacheValue = unwrapReturnValue(returnValue); } // 如果有@CachePut注解,則新增到cachePutRequests // Collect any explicit @CachePuts collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); // 如果緩存未命中,則把查詢結(jié)果值寫入緩存;如果有@CachePut注解,也把方法執(zhí)行結(jié)果寫入緩存 // Process any collected put requests, either from @CachePut or a @Cacheable miss for (CachePutRequest cachePutRequest : cachePutRequests) { cachePutRequest.apply(cacheValue); } // 如果有@CacheEvict注解、并且標(biāo)記為在調(diào)用后執(zhí)行,則做刪除/清空緩存的操作 // 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表達(dá)式解析,否則使用KeyGenerator類生成 Object key = generateKey(context, result); // 根據(jù)緩存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()) { // 調(diào)用父類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 { // 調(diào)用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 } } }
總結(jié)
到此這篇關(guān)于Spring cache源碼解析的文章就介紹到這了,更多相關(guān)Spring cache源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Swing實(shí)現(xiàn)坦克大戰(zhàn)游戲
這篇文章主要介紹了Java Swing實(shí)現(xiàn)坦克大戰(zhàn)游戲,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很大的幫助喲,需要的朋友可以參考下2021-05-05Java開發(fā)環(huán)境配置JDK超詳細(xì)整理(適合新手入門)
這篇文章主要給大家介紹了關(guān)于Java開發(fā)環(huán)境配置JDK超詳細(xì)整理的相關(guān)資料,非常適合新手入門,JDK是Java語(yǔ)言的軟件開發(fā)工具包,主要用于移動(dòng)設(shè)備、嵌入式設(shè)備上的java應(yīng)用程序,需要的朋友可以參考下2023-11-11MyBatis直接執(zhí)行SQL的工具SqlMapper
今天小編就為大家分享一篇關(guān)于MyBatis直接執(zhí)行SQL的工具SqlMapper,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12Spring中@Configuration注解和@Component注解的區(qū)別詳解
這篇文章主要介紹了Spring中@Configuration注解和@Component注解的區(qū)別詳解,@Configuration 和 @Component 到底有何區(qū)別呢?我先通過如下一個(gè)案例,在不分析源碼的情況下,小伙伴們先來(lái)直觀感受一下這兩個(gè)之間的區(qū)別,需要的朋友可以參考下2023-09-09解決java?try?throw?exception?finally遇上return?break?conti
這篇文章主要介紹了解決java?try?throw?exception?finally遇上return?break?continue造成異常丟失問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Springboot Websocket Stomp 消息訂閱推送
本文主要介紹了Springboot Websocket Stomp 消息訂閱推送,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07Java Web中解決路徑(絕對(duì)路徑與相對(duì)路徑)問題
這篇文章主要介紹了Java Web中解決路徑問題的相關(guān)資料,java 文件路徑有絕對(duì)路徑與相對(duì)路徑,這里提供了幾種方法解決所有路徑問題,需要的朋友可以參考下2017-01-01