CacheInterceptor 的理解

523 阅读1分钟

1 导语

在前面的Spring AOP的代理对象调用过程中我们讲到是调用interceptor.invoke(this)这个this

2 CacheInterceptor#invoke

invocation.proceed();

public Object invoke(final MethodInvocation invocation) throws Throwable {
	Method method = invocation.getMethod();

	CacheOperationInvoker aopAllianceInvoker = () -> {
		try {
		// 重复调用
			return invocation.proceed();
		}
		catch (Throwable ex) {
			throw new CacheOperationInvoker.ThrowableWrapper(ex);
		}
	};

	try {
		return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
	}
	catch (CacheOperationInvoker.ThrowableWrapper th) {
		throw th.getOriginal();
	}
}

3 CacheInterceptor#execute

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 cacheOperationSource = getCacheOperationSource();
		if (cacheOperationSource != null) {
			Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
			if (!CollectionUtils.isEmpty(operations)) {
				return execute(invoker, method,
						new CacheOperationContexts(operations, method, args, target, targetClass));
			}
		}
	}


	return invoker.invoke();
}

4 CacheInterceptor#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, () -> 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);
		}
	}


	// Process any early evictions
	processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
			CacheOperationExpressionEvaluator.NO_RESULT);

	// Check if we have a cached item matching the conditions
	Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

	// Collect puts from any @Cacheable miss, if no cached item is found
	List<CachePutRequest> cachePutRequests = new LinkedList<>();
	if (cacheHit == null) {
		collectPutRequests(contexts.get(CacheableOperation.class),
				CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
	}

	Object cacheValue;
	Object returnValue;

	if (cacheHit != null && !hasCachePut(contexts)) {
		// If there are no put requests, just use the cache hit
		cacheValue = cacheHit.get();
		returnValue = wrapCacheValue(method, cacheValue);
	}
	else {
		// Invoke the method if we don't have a cache hit
		returnValue = invokeOperation(invoker);
		cacheValue = unwrapReturnValue(returnValue);
	}

	// Collect any explicit @CachePuts
	collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

	// Process any collected put requests, either from @CachePut or a @Cacheable miss
	for (CachePutRequest cachePutRequest : cachePutRequests) {
		cachePutRequest.apply(cacheValue);
	}

	// Process any late evictions
	processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

	return returnValue;
}