您好,欢迎来到刀刀网。
搜索
您的当前位置:首页SpringCache源码解析(三)——@EnableCaching

SpringCache源码解析(三)——@EnableCaching

来源:刀刀网

一、源码阅读

让我们进行源码阅读把。

1.1 阅读源码基础:

@Import(xxx.class)里的类可以有两种类:

  • ImportSelector接口的实现类;
  • ImportBeanDefinitionRegistrar接口的实现类;

两种接口简介:

  • ImportSelector接口:在配置类中被@Import加入到Spring容器中以后。Spring容器就会把ImportSelector接口方法返回的字符串数组中的类new出来对象然后放到工厂中去。

  • ImportBeanDefinitionRegistrar:ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象,注册到Spring容器中,功能类似于注解@Service @Component。

1.2 CachingConfigurationSelector

@EnableCaching导入了CachingConfigurationSelector。
org.springframework.cache.annotation.CachingConfigurationSelector:

@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}

getProxyImports方法:

private String[] getProxyImports() {
		List<String> result = new ArrayList<>(3);
		//将AutoProxyRegistrar注入到容器
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ProxyCachingConfiguration.class.getName());
		if (jsr107Present && jcacheImplPresent) {
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		return StringUtils.toStringArray(result);
	}

getAspectJImports方法:

private String[] getAspectJImports() {
		List<String> result = new ArrayList<>(2);
		result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		if (jsr107Present && jcacheImplPresent) {
			result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		}
		return StringUtils.toStringArray(result);
	}

1.3 getProxyImports方法内部

1.3.1 AutoProxyRegistrar

  顾名思义,AutoProxyRegistrar从名称上就可以看出其本质是一个ImportBeanDefinitionRegistrar,而ImportBeanDefinitionRegistrar最重要的方法为registerBeanDefinitions(),这个方法的主要作用就是用方法参数中的registry向容器中注入一些BeanDefinition。

源码追踪:

@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
		//1.遍历类元数据中的所有注解类型
		for (String annType : annTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			//2.检查每个注解是否包含mode和proxyTargetClass属性,并判断其类型是否正确。
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
					Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				//3.如果属性类型正确且mode为AdviceMode.PROXY,则根据proxyTargetClass值配置自动代理创建器。
				if (mode == AdviceMode.PROXY) {
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
		if (!candidateFound && logger.isInfoEnabled()) {
			String name = getClass().getSimpleName();
			logger.info(String.format("%s was imported but no annotations were found " +
					"having both 'mode' and 'proxyTargetClass' attributes of type " +
					"AdviceMode and boolean respectively. This means that auto proxy " +
					"creator registration and configuration may not have occurred as " +
					"intended, and components may not be proxied as expected. Check to " +
					"ensure that %s has been @Import'ed on the same class where these " +
					"annotations are declared; otherwise remove the import of %s " +
					"altogether.", name, name, name));
		}
	}

该方法用于注册Bean定义。主要功能如下:

下面这是@EnableCaching里的mode值和proxyTargetClass值:

拓展:

1.3.1.1 AutoProxyRegistrar.registerBeanDefinitions()方法总结

总结:AutoProxyRegistrar.registerBeanDefinitions()往容器中注入了一个类InfrastructureAdvisorAutoProxyCreator。

这段是不是很熟悉,没错,声明式事务中注入的也是这个InfrastructureAdvisorAutoProxyCreator类,用来生成代理对象。

1.3.2 ProxyCachingConfiguration

ProxyCachingConfiguration向Spring容器中注入了三个Bean:

  1. BeanFactoryCacheOperationSourceAdvisor:切面,用来匹配目标方法和目标类,看那些方法需要实现缓存的增强;
  2. CacheOperationSource:用来解析缓存相关的注解;
  3. CacheInterceptor:实现了Advice,是一个通知,实现对目标方法的增强。
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		advisor.setCacheOperationSource(cacheOperationSource());
		advisor.setAdvice(cacheInterceptor());
		if (this.enableCaching != null) {
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
		return new AnnotationCacheOperationSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor() {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource());
		return interceptor;
	}

}

ProxyCachingConfiguration继承自AbstractCachingConfiguration,AbstractCachingConfiguration是抽象基础配置类,为启用Spring的注解驱动的缓存管理功能提供通用结构;代码如下:

1.3.2.1 BeanFactoryCacheOperationSourceAdvisor

BeanFactoryCacheOperationSourceAdvisor是一个切面,切面需要包括通知和切点:

  1. Advice通知:CacheInterceptor,由ProxyCachingConfiguration注入;
  2. Pointcut切点:内部CacheOperationSourcePointcut类型的属性;
1.3.2.2 CacheOperationSourcePointcut

Pointcut主要包含两部分,对目标类的匹配,对目标方法的匹配。
对目标类的匹配,其实啥也没干,返回true,主要逻辑在对目标方法的匹配上,大部分Pointcut都是如此:

private class CacheOperationSourceClassFilter implements ClassFilter {

	@Override
	public boolean matches(Class<?> clazz) {
		if (CacheManager.class.isAssignableFrom(clazz)) {
			return false;
		}
		// 啥也没干
		CacheOperationSource cas = getCacheOperationSource();
		return (cas == null || cas.isCandidateClass(clazz));
	}
}

对目标方法的匹配:

public boolean matches(Method method, Class<?> targetClass) {
	CacheOperationSource cas = getCacheOperationSource();
	/**
	 * @see AbstractFallbackCacheOperationSource#getCacheOperations(java.lang.reflect.Method, java.lang.Class)
	 */
	// 就是看目标方法上面有没有缓存注解
	return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}

这里会委托给CacheOperationSource来解析缓存注解,这个类是在ProxyCachingConfiguration中注入的。

1.3.3 org.springframework.cache.jcache.config.ProxyJCacheConfiguration

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		advisor.setCacheOperationSource(cacheOperationSource());
		advisor.setAdvice(cacheInterceptor());
		if (this.enableCaching != null) {
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
		return new AnnotationCacheOperationSource();
	}

	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheInterceptor cacheInterceptor() {
		CacheInterceptor interceptor = new CacheInterceptor();
		interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
		interceptor.setCacheOperationSource(cacheOperationSource());
		return interceptor;
	}

}

ProxyCachingConfiguration向Spring容器中注入了三个Bean:

  1. BeanFactoryCacheOperationSourceAdvisor:切面,用来匹配目标方法和目标类,看那些方法需要实现缓存的增强;
  2. CacheOperationSource:用来解析缓存相关的注解;
  3. CacheInterceptor:实现了Advice,是一个通知,实现对目标方法的增强。
1.3.3.1 BeanFactoryCacheOperationSourceAdvisor

BeanFactoryCacheOperationSourceAdvisor是一个切面,切面需要包括通知和切点:

  • Advice通知:CacheInterceptor,由ProxyCachingConfiguration注入;
  • Pointcut切点:内部CacheOperationSourcePointcut类型的属性;
1.3.3.2 CacheOperationSourcePointcut

Pointcut主要包含两部分,对目标类的匹配,对目标方法的匹配。

对目标类的匹配,其实啥也没干,返回true,主要逻辑在对目标方法的匹配上,大部分Pointcut都是如此:

abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {

	@Override
	public boolean matches(Method method, Class<?> targetClass) {
		if (CacheManager.class.isAssignableFrom(targetClass)) {
			return false;
		}
		// 就是看目标方法上面有没有缓存注解
		CacheOperationSource cas = getCacheOperationSource();
		return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
	}
}

这里会委托给CacheOperationSource来解析缓存注解,这个类是在ProxyCachingConfiguration中注入的。

最终会调用到org.springframework.cache.annotation.SpringCacheAnnotationParser#parseCacheAnnotations(org.springframework.cache.annotation.SpringCacheAnnotationParser.DefaultCacheConfig, java.lang.reflect.AnnotatedElement, boolean)

private Collection<CacheOperation> parseCacheAnnotations(
		DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

	Collection<? extends Annotation> anns = (localOnly ?
			AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
			AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
	if (anns.isEmpty()) {
		return null;
	}

	// @Cacheable解析为CacheableOperation
	// @CacheEvict解析为CacheEvictOperation
	// @CachePut解析为CachePutOperation
	final Collection<CacheOperation> ops = new ArrayList<>(1);
	anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
			ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
	anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
			ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
	anns.stream().filter(ann -> ann instanceof CachePut).forEach(
			ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
	anns.stream().filter(ann -> ann instanceof Caching).forEach(
			ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
	return ops;
}

1.3.3.3 CacheInterceptor

CacheInterceptor是一个Advice,用来实现对目标方法的增强,当调用目标方法时会先进入CacheInterceptor.invoke()方法:

org.springframework.cache.interceptor.CacheInterceptor#invoke

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {

	@Override
	@Nullable
	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();
		}
	}

}

org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
	// Special handling of synchronized invocation
	if (contexts.isSynchronized()) { // false不会进入
		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, handleSynchronizedGet(invoker, key, cache));
			}
			catch (Cache.ValueRetrievalException ex) {
				// Directly propagate ThrowableWrapper from the invoker,
				// or potentially also an IllegalArgumentException etc.
				ReflectionUtils.rethrowRuntimeException(ex.getCause());
			}
		}
		else {
			// No caching required, only call the underlying method
			return invokeOperation(invoker);
		}
	}


	// Process any early evictions
	// 先调用@CacheEvict注解中的属性beforeInvocation=true的,实际上这个值默认为false,这里一般不会干啥,看后面的调用
	// cache.evict()
	processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
			CacheOperationExpressionEvaluator.NO_RESULT);

	// Check if we have a cached item matching the conditions
	// cache.get()
	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
	// 调用@CacheEvict注解中的属性beforeInvocation=false的
	processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

	return returnValue;
}

1.4 扩展缓存注解

有时候由于业务需要或者Spring提供的缓存不满足我们的要求,如无法解决缓存雪崩问题,扩展步骤如下:

  1. 实现CacheManager接口或继承AbstractCacheManager,管理自身的cache实例,也可以直接使用内置的SimpleCacheManager;
  2. 实现Cache接口,自定义缓存实现逻辑;
  3. 将自定义的Cache和CacheManager进行关联并注入到Spring容器中。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- gamedaodao.com 版权所有 湘ICP备2022005869号-6

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务