溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring?cache源碼分析

發(fā)布時間:2022-03-30 16:47:06 來源:億速云 閱讀:179 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下Spring cache源碼分析的相關(guān)知識點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

題外話:如何閱讀開源代碼?

有2種方法,可以結(jié)合起來使用:

  • 靜態(tài)代碼閱讀:查找關(guān)鍵類、方法的usage之處,熟練使用find usages功能,找到所有相關(guān)的類、方法,靜態(tài)分析核心邏輯的執(zhí)行過程,一步步追根問底,直至建立全貌

  • 運(yùn)行時debug:在關(guān)鍵方法上加上斷點(diǎn),并且寫一個單元測試調(diào)用類庫/框架,熟練使用step into/step over/resume來動態(tài)分析代碼的執(zhí)行過程

核心類圖

Spring?cache源碼分析

如圖所示,可以分成以下幾類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是一個獲取緩存注解對應(yīng)CacheOperation的類,而SpringCacheAnnotationParser是真正解析注解的類,解析后會封裝成CacheOperation集合供AnnotationCacheOperationSource查找

源碼分析(帶注釋解釋)

下面對Spring cache源碼做分析,帶注釋解釋,只摘錄核心代碼片段。

1、解析注解

首先看看注解是如何解析的。注解只是一個標(biāo)記,要讓它真正工作起來,需要對注解做解析操作,并且還要有對應(yīng)的實際邏輯。

SpringCacheAnnotationParser:負(fù)責(zé)解析注解,返回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:調(diào)用SpringCacheAnnotationParser獲取注解對應(yīng)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的父類,實現(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反射信息,獲取對應(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);

                // 因解析反射信息較耗時,所以用map緩存,避免重復(fù)計算
                // 如在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;
		}
	}

        // 計算緩存操作列表,優(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實現(xiàn)),最終通過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、邏輯執(zhí)行

以@Cacheable背后的邏輯為例。預(yù)期是先查緩存,如果緩存命中了就直接使用緩存值,否則執(zhí)行業(yè)務(wù)邏輯,并把結(jié)果寫入緩存。

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

CacheInterceptor:是一個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í)行到一個回調(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í)行原始方法后會寫入緩存

  • 緩存命中時,使用緩存值作為結(jié)果;緩存未命中、或有@CachePut注解時,需要調(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)一個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í)行原始方法后會寫入緩存
		// 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
		}
	}
}

以上就是“Spring cache源碼分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI