Spring cache原理详解

2021-05-07 14:53:06 浏览数 (1)

一、概述

从Spring3.1版本开始,Spring框架就支持显式地将缓存添加到现有的Spring应用程序中。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的侵入最小。

Spring缓存的实现在spring-context包,如果是基于springboot基础框架编程,在spring-boot-autoconfige

中有很多默认的配置和定义,能更大程度上让用户无感知开启cache能力,如果不需要三方套件提供的缓存能力,就不需要引入额外的依赖。本篇的分析是基于springboot2.1.3版本。

缓存使用原理大致如下图:

二、使用方式

Spring cache提供了开箱即用的接入方式,只需要若干注解和缓存管理类即可接入.

1.开启缓存能力

引入缓存依赖:

代码语言:txt复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

在应用启动类添加@EnableCaching注解:

代码语言:txt复制
@SpringBootApplication
@EnableCaching
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
2.使用缓存

在业务方法添加@Cacheable注解:

代码语言:txt复制
@Cacheable(cacheNames = {"task"})
public TaskInfoDTO getTask(String taskId) {
    log.info("TestBuzz.getTask mock query from DB......");
    TaskInfoDTO taskInfoDTO = new TaskInfoDTO();
    taskInfoDTO.setTaskId(taskId);
    taskInfoDTO.setApplicantId("system");
    taskInfoDTO.setDescription("test");
    return taskInfoDTO;
}

模拟请求:

代码语言:txt复制
@GetMapping("/test_cache")
public IResp<TaskInfoDTO> testCache() {
    TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");
    return IResp.getSuccessResult(taskInfoDTO);
}

连续发送两次查询请求:

代码语言:txt复制
curl http://localhost:port/test_cache
image.pngimage.png

从日志中看到只打印了一次DB调用,也就是说明第二次走了缓存。就这么简单我们就开启并使用了spring的缓存能力。

三、源码&原理解析

我们主要从spring-context和spring-boot-autoconfige两个包的cache模块分析cache原理。

开启缓存时序图:

image.pngimage.png

缓存通知配置时序图:

image.pngimage.png

缓存通知装配

image.pngimage.png
1.开启缓存

@EnableCaching

代码语言:txt复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
	//是否直接代理目标类
	boolean proxyTargetClass() default false;
	//通知模式,默认是代理
	AdviceMode mode() default AdviceMode.PROXY;
	//顺序
	int order() default Ordered.LOWEST_PRECEDENCE;
}

这个注解和@EnableAsync注解特别像,说明都是基于Aop和代理做了能力增强,该类导入了CachingConfigurationSelector类,我们看一下其实现:

代码语言:txt复制
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

	private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
			"org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
	private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.cache.aspectj.AspectJCachingConfiguration";
	private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.cache.aspectj.AspectJJCacheConfiguration";
	private static final boolean jsr107Present;
	private static final boolean jcacheImplPresent;
	static {
		ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
		jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
		jcacheImplPresent = ClassUtils.isPresent(PROXY_JCACHE_CONFIGURATION_CLASS, classLoader);
	}
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}
	private String[] getProxyImports() {
		List<String> result = new ArrayList<>(3);
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ProxyCachingConfiguration.class.getName());
		if (jsr107Present && jcacheImplPresent) {
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		return StringUtils.toStringArray(result);
	}
	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);
	}
}

有个概念需要解释一下,jsr107是关于java关于缓存的规范提案,使用的话需要引入javax.cache相关包,出场率不高。CachingConfigurationSelector类的核心是selectImports方法,根据@EnableCaching配置的模式选择不同的配置类型,默认是PROXY模式,导入AutoProxyRegistrar和ProxyCachingConfiguration两个配置。

2.缓存通知配置
ProxyCachingConfiguration.pngProxyCachingConfiguration.png

父类AbstractCachingConfiguration实现:

代码语言:txt复制
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		this.enableCaching = AnnotationAttributes.fromMap(
				importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
		if (this.enableCaching == null) {
			throw new IllegalArgumentException(
					"@EnableCaching is not present on importing class "   importMetadata.getClassName());
		}
	}
	@Autowired(required = false)
	void setConfigurers(Collection<CachingConfigurer> configurers) {
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
		if (configurers.size() > 1) {
			throw new IllegalStateException(configurers.size()   " implementations of "  
					"CachingConfigurer were found when only 1 was expected. "  
					"Refactor the configuration such that CachingConfigurer is "  
					"implemented only once or not at all.");
		}
		CachingConfigurer configurer = configurers.iterator().next();
		useCachingConfigurer(configurer);
	}
	protected void useCachingConfigurer(CachingConfigurer config) {
		this.cacheManager = config::cacheManager;
		this.cacheResolver = config::cacheResolver;
		this.keyGenerator = config::keyGenerator;
		this.errorHandler = config::errorHandler;
	}
}

做了两件事,首先把注解元数据属性解析出来,然后把用户自定义的缓存组件装配进来(CacheManager,KeyGenerator和异常处理器)。

在看ProxyCachingConfiguration配置实现:

代码语言:txt复制
@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复用了父类的能力并且定了AOP的三个核心组件(Pointcut,Advice和Advisor),先看AnnotationCacheOperationSource(此时还不能被称作Pointcut):

AnnotationCacheOperationSource.pngAnnotationCacheOperationSource.png

AnnotationCacheOperationSource继承AbstractFallbackCacheOperationSource类实现CacheOperationSource接口,实现getCacheOperations方法将目标方法上缓存注解解析成缓存操作集合,AbstractFallbackCacheOperationSource.getCacheOperations:

代码语言:txt复制
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
	if (method.getDeclaringClass() == Object.class) {
		return null;
	}
	Object cacheKey = getCacheKey(method, targetClass);
	Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
	if (cached != null) {
		return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
	}
	else {
		Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
		if (cacheOps != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Adding cacheable method '"   method.getName()   "' with attribute: "   cacheOps);
			}
			this.attributeCache.put(cacheKey, cacheOps);
		}
		else {
			this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
		}
		return cacheOps;
	}
}

cacheKey是由method和class构造成的MethodClassKey,如果缓存中有缓存操作集合直接返回,否则调用computeCacheOperations计算:

代码语言:txt复制
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable 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 = AopUtils.getMostSpecificMethod(method, targetClass);
	// 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;
}

该方法是从目标类和目标方法上(优先方法维度)解析缓存注解装配成缓存操作(@Cacheable -> CacheableOperation),看子类

AnnotationCacheOperationSource实现:

代码语言:txt复制
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
	this.publicMethodsOnly = publicMethodsOnly;
	this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
	return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Method method) {
	return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}

protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
	Collection<CacheOperation> ops = null;
	for (CacheAnnotationParser annotationParser : this.annotationParsers) {
		Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
		if (annOps != null) {
			if (ops == null) {
				ops = annOps;
			}
			else {
				Collection<CacheOperation> combined = new ArrayList<>(ops.size()   annOps.size());
				combined.addAll(ops);
				combined.addAll(annOps);
				ops = combined;
			}
		}
	}
	return ops;
}

AnnotationCacheOperationSource默认构造器使用的是SpringCacheAnnotationParser解析器,解析操作最终委托给SpringCacheAnnotationParser.parseCacheAnnotations,将注解分别解析成对应的操作:

代码语言:txt复制
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
	DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
	return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
	DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
	return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
	Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
	if (ops != null && ops.size() > 1) {
		// More than one operation found -> local declarations override interface-declared ones...
		Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
		if (localOps != null) {
			return localOps;
		}
	}
	return ops;
}
@Nullable
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;
	}
	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;
}
private CacheableOperation parseCacheableAnnotation(
		AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
	CacheableOperation.Builder builder = new CacheableOperation.Builder();
	builder.setName(ae.toString());
	builder.setCacheNames(cacheable.cacheNames());
	builder.setCondition(cacheable.condition());
	builder.setUnless(cacheable.unless());
	builder.setKey(cacheable.key());
	builder.setKeyGenerator(cacheable.keyGenerator());
	builder.setCacheManager(cacheable.cacheManager());
	builder.setCacheResolver(cacheable.cacheResolver());
	builder.setSync(cacheable.sync());
	defaultConfig.applyDefault(builder);
	CacheableOperation op = builder.build();
	validateCacheOperation(ae, op);
	return op;
}
private CacheEvictOperation parseEvictAnnotation(
		AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
	CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
	builder.setName(ae.toString());
	builder.setCacheNames(cacheEvict.cacheNames());
	builder.setCondition(cacheEvict.condition());
	builder.setKey(cacheEvict.key());
	builder.setKeyGenerator(cacheEvict.keyGenerator());
	builder.setCacheManager(cacheEvict.cacheManager());
	builder.setCacheResolver(cacheEvict.cacheResolver());
	builder.setCacheWide(cacheEvict.allEntries());
	builder.setBeforeInvocation(cacheEvict.beforeInvocation());
	defaultConfig.applyDefault(builder);
	CacheEvictOperation op = builder.build();
	validateCacheOperation(ae, op);
	return op;
}

private CacheOperation parsePutAnnotation(
		AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
	CachePutOperation.Builder builder = new CachePutOperation.Builder();
	builder.setName(ae.toString());
	builder.setCacheNames(cachePut.cacheNames());
	builder.setCondition(cachePut.condition());
	builder.setUnless(cachePut.unless());
	builder.setKey(cachePut.key());
	builder.setKeyGenerator(cachePut.keyGenerator());
	builder.setCacheManager(cachePut.cacheManager());
	builder.setCacheResolver(cachePut.cacheResolver());
	defaultConfig.applyDefault(builder);
	CachePutOperation op = builder.build();
	validateCacheOperation(ae, op);
	return op;
}
private void parseCachingAnnotation(
		AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
	Cacheable[] cacheables = caching.cacheable();
	for (Cacheable cacheable : cacheables) {
		ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
	}
	CacheEvict[] cacheEvicts = caching.evict();
	for (CacheEvict cacheEvict : cacheEvicts) {
		ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
	}
	CachePut[] cachePuts = caching.put();
	for (CachePut cachePut : cachePuts) {
		ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
	}
}

然后看通知CacheInterceptor:

CacheInterceptor.pngCacheInterceptor.png

CacheInterceptor实现Advice接口并继承了CacheAspectSupport,看一些实现:

代码语言:txt复制
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();
		}
	}
}

当拦截到调用时,将调用封装成CacheOperationInvoker并交给父类执行,父类CacheAspectSupport实现了SmartInitializingSingleton接口,在单例初始化后容器会调用afterSingletonsInstantiated方法:

代码语言:txt复制
public void afterSingletonsInstantiated() {
	if (getCacheResolver() == null) {
		// Lazily initialize cache resolver via default cache manager...
		Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
		try {
			setCacheManager(this.beanFactory.getBean(CacheManager.class));
		}
		catch (NoUniqueBeanDefinitionException ex) {
			throw new IllegalStateException("No CacheResolver specified, and no unique bean of type "  
					"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
		}
		catch (NoSuchBeanDefinitionException ex) {
			throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. "  
					"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
		}
	}
	this.initialized = true;
}

检查有没有合适的CacheManager,并且将initialized设置为true,继续看CacheAspectSupport.execute:

代码语言:txt复制
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();
}

使用AnnotationCacheOperationSource目标类和方法上的缓存注解解析成操作集合,然后构造CacheOperationContexts上下文并调用重载方法:

代码语言:txt复制
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
		Object[] args, Object target, Class<?> targetClass) {
	this.contexts = new LinkedMultiValueMap<>(operations.size());
	for (CacheOperation op : operations) {
        //
		this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
	}
    //
	this.sync = determineSyncFlag(method);
}

将每个操作包装对应上下文映射关系,并检查是否是同步操作(@Cacheable独有属性),继续看execute:

代码语言:txt复制
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) {
				throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
			}
		}
		else {
			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;
}

该方法是缓存操作的核心逻辑,首先检查是否是同步操作(@Cacheable特性),如果是且满足条件调用缓存获取逻辑并返回,否则返回业务逻辑免缓存调用invokeOperation,然后执行@CacheEvict的前置清除(beforeInvocation=true),接着检查@Cacheable是否命中缓存,如果没有命中则放入需要执行CachePutRequest列表暂存,然后检查是否缓存命中且没有需要更新的缓存,如果满足则返回结果使用缓存结果,否则使用业务查询结果作为返回结果,并且填充需要缓存的结果,然后收集@CachePut操作,把@CachePut和@Cacheable未命中的请求同步到缓存,最后清理@CacheEvict的缓存(beforeInvocation=false)。

3.缓存代理装配

前边讲述了缓存配置和工作流程,那么上述的Aop配置什么时候生效?在哪里生效?如何生效?接下来我们将从AutoProxyRegistrar作为切入点展开分析缓存代理的装配逻辑.

代码语言:txt复制
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
	private final Log logger = LogFactory.getLog(getClass());
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		boolean candidateFound = false;
		Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
		for (String annoType : annoTypes) {
			AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
			if (candidate == null) {
				continue;
			}
			Object mode = candidate.get("mode");
			Object proxyTargetClass = candidate.get("proxyTargetClass");
			if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
					Boolean.class == proxyTargetClass.getClass()) {
				candidateFound = true;
				if (mode == AdviceMode.PROXY) {
					AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
					if ((Boolean) proxyTargetClass) {
						AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
						return;
					}
				}
			}
		}
	}
}

AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,registerBeanDefinitions会从启用缓存注解@EnableCaching提取属性,然后手动注册自动代理创建器:

代码语言:txt复制
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
	return registerAutoProxyCreatorIfNecessary(registry, null);
}

public static BeanDefinition registerAutoProxyCreatorIfNecessary(
		BeanDefinitionRegistry registry, @Nullable Object source) {
	return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

private static BeanDefinition registerOrEscalateApcAsRequired(
		Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
		BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
		if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
			int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
			int requiredPriority = findPriorityForClass(cls);
			if (currentPriority < requiredPriority) {
				apcDefinition.setBeanClassName(cls.getName());
			}
		}
		return null;
	}
	RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
	beanDefinition.setSource(source);
	beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
	beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
	return beanDefinition;
}

手动注册了InfrastructureAdvisorAutoProxyCreator到容器中,看一下InfrastructureAdvisorAutoProxyCreator继承关系:

InfrastructureAdvisorAutoProxyCreator.pngInfrastructureAdvisorAutoProxyCreator.png
代码语言:txt复制
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
	@Nullable
	private ConfigurableListableBeanFactory beanFactory;

	@Override
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		super.initBeanFactory(beanFactory);
		this.beanFactory = beanFactory;
	}
	@Override
	protected boolean isEligibleAdvisorBean(String beanName) {
		return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
				this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
	}
}

InfrastructureAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator类实现了BeanFactory初始化和isEligibleAdvisorBean方法,继续看父类AbstractAdvisorAutoProxyCreator实现:

代码语言:txt复制
public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
	@Nullable
	private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
	}
	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}
	protected List<Advisor> findCandidateAdvisors() {
		Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}
	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}
	protected boolean isEligibleAdvisorBean(String beanName) {
		return true;
	}
	private class BeanFactoryAdvisorRetrievalHelperAdapter extends BeanFactoryAdvisorRetrievalHelper {
		public BeanFactoryAdvisorRetrievalHelperAdapter(ConfigurableListableBeanFactory beanFactory) {
			super(beanFactory);
		}
		@Override
		protected boolean isEligibleBean(String beanName) {
			return AbstractAdvisorAutoProxyCreator.this.isEligibleAdvisorBean(beanName);
		}
	}
}

AbstractAdvisorAutoProxyCreator定义了Advisor操作的工具方法,并且定义了Advisor提取适配器BeanFactoryAdvisorRetrievalHelperAdapter,委托给子类isEligibleAdvisorBean方法实现(InfrastructureAdvisorAutoProxyCreator)。

重点在于AbstractAdvisorAutoProxyCreator父类AbstractAutoProxyCreator实现的postProcessBeforeInstantiation方法,该方法在InstantiationAwareBeanPostProcessor接口定义,该方法在bean创建之前调用,如果该方法返回非null对象,那么bean的创建过程将会短路,此处的作用是为满足BeanFactoryCacheOperationSourceAdvisor增强器切入逻辑的类织入增强逻辑,也就是缓存能力,看一下方法实现:

代码语言:txt复制
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
	Object cacheKey = getCacheKey(beanClass, beanName);

	if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
		if (this.advisedBeans.containsKey(cacheKey)) {
			return null;
		}
		if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return null;
		}
	}
	// Create proxy here if we have a custom TargetSource.
	// Suppresses unnecessary default instantiation of the target bean:
	// The TargetSource will handle target instances in a custom fashion.
	TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
	if (targetSource != null) {
		if (StringUtils.hasLength(beanName)) {
			this.targetSourcedBeans.add(beanName);
		}
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
		Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}

	return null;
}

此处的逻辑和AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法很相似,都是拦截bean创建过程并织入增强逻辑,这里是自动生成代理类并且将缓存逻辑织入进去。这里也是往上说的自动代理实现APC的核心逻辑。该方法前半段是从缓存中获取目标类是否被代理过,如果被代理过直接把增强逻辑织入,避免重复创建代理,后半段就是生成代理的逻辑,创建代理过程我们之前分析过,此处不再分析,重点分析一下从候选增强器中获取增强逻辑的方法getAdvicesAndAdvisorsForBean:

代码语言:txt复制
protected Object[] getAdvicesAndAdvisorsForBean(
		Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
	List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
		return DO_NOT_PROXY;
	}
	return advisors.toArray();
}

该方法在子类AbstractAdvisorAutoProxyCreator中实现,接着调用了findEligibleAdvisors方法:

代码语言:txt复制
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
	return eligibleAdvisors;
}

先通过前边定义的BeanFactoryAdvisorRetrievalHelper获取候选增强器,然后调用findAdvisorsThatCanApply方法筛选出对当前代理类适用的增强器:

代码语言:txt复制
protected List<Advisor> findAdvisorsThatCanApply(
		List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

	ProxyCreationContext.setCurrentProxiedBeanName(beanName);
	try {
		return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
	}
	finally {
		ProxyCreationContext.setCurrentProxiedBeanName(null);
	}
}

该方法将筛选逻辑委托为Aop工具类的findAdvisorsThatCanApply方法处理:

代码语言:txt复制
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
	if (candidateAdvisors.isEmpty()) {
		return candidateAdvisors;
	}
	List<Advisor> eligibleAdvisors = new ArrayList<>();
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
			eligibleAdvisors.add(candidate);
		}
	}
	boolean hasIntroductions = !eligibleAdvisors.isEmpty();
	for (Advisor candidate : candidateAdvisors) {
		if (candidate instanceof IntroductionAdvisor) {
			// already processed
			continue;
		}
		if (canApply(candidate, clazz, hasIntroductions)) {
			eligibleAdvisors.add(candidate);
		}
	}
	return eligibleAdvisors;
}

从ProxyCachingConfiguration中增强器的定义来看,BeanFactoryCacheOperationSourceAdvisor是PointcutAdvisor类型,方法前半段IntroductionAdvisor逻辑跳过,通过canApply检查是否符合条件,如果符合则加入返回列表:

代码语言:txt复制
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
	if (advisor instanceof IntroductionAdvisor) {
		return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
	}
	else if (advisor instanceof PointcutAdvisor) {
		PointcutAdvisor pca = (PointcutAdvisor) advisor;
		return canApply(pca.getPointcut(), targetClass, hasIntroductions);
	}
	else {
		// It doesn't have a pointcut so we assume it applies.
		return true;
	}
}

直接进入第二个条件分支,检查PointcutAdvisor是否符合切入逻辑:

代码语言:txt复制
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
	Assert.notNull(pc, "Pointcut must not be null");
	if (!pc.getClassFilter().matches(targetClass)) {
		return false;
	}

	MethodMatcher methodMatcher = pc.getMethodMatcher();
	if (methodMatcher == MethodMatcher.TRUE) {
		// No need to iterate the methods if we're matching any method anyway...
		return true;
	}
	IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
	if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
		introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
	}
	Set<Class<?>> classes = new LinkedHashSet<>();
	if (!Proxy.isProxyClass(targetClass)) {
		classes.add(ClassUtils.getUserClass(targetClass));
	}
	classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
	for (Class<?> clazz : classes) {
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
		for (Method method : methods) {
			if (introductionAwareMethodMatcher != null ?
					introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
					methodMatcher.matches(method, targetClass)) {
				return true;
			}
		}
	}
	return false;
}

这个方法也不复杂,其实就是检查目标类和方法上是否有缓存相关注解(@Cacheable,@CachePut,@CacheEvict等),如果有说明增强器对目标代理类适用,然后找到合适的增强器列表在APC中调用createProxy创建代理:

代码语言:txt复制
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
		@Nullable Object[] specificInterceptors, TargetSource targetSource) {
	if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
		AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
	}
	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.copyFrom(this);
	if (!proxyFactory.isProxyTargetClass()) {
		if (shouldProxyTargetClass(beanClass, beanName)) {
			proxyFactory.setProxyTargetClass(true);
		}
		else {
			evaluateProxyInterfaces(beanClass, proxyFactory);
		}
	}
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	proxyFactory.addAdvisors(advisors);
	proxyFactory.setTargetSource(targetSource);
	customizeProxyFactory(proxyFactory);
	proxyFactory.setFrozen(this.freezeProxy);
	if (advisorsPreFiltered()) {
		proxyFactory.setPreFiltered(true);
	}
	return proxyFactory.getProxy(getProxyClassLoader());
}

这里创建代理工厂,然后选择是否需要直接代理目标类,然后装配增强器,然后调用JdkDynamicAopProxy或者CglibAopProxy创建代理。

4.缓存配置

在本篇第二节使用方式介绍的时候,我们引入了缓存依赖开启缓存能力就能直接使用缓存了,并没有引入或者配的

其他的缓存组件,那么问题就来了,为什么直接就能使用缓存了,如果应用架构基于Spring而不是Springboot,那么肯定是要自己配置CacheResolver或者CacheManager的,为什么这里不需要,这一切还是要归功于spring-boot-autoconfigure,我们使用Springboot作为基础框架时,一般都会显式或者间接把其引入。

image.pngimage.png

spring-boot-autoconfigure有个包叫cache,毫无以为这里就是springboot定义并自动开启缓存配置的地方,该包下基本都是*Configuration类型的类,也就是Springboot自带的缓存相关配置,我们简单分析一下CacheAutoConfiguration、CacheConfigurations、GenericCacheConfiguration、NoOpCacheConfiguration、SimpleCacheConfiguration、CaffeineCacheConfiguration和RedisCacheConfiguration这几个配置类。

CacheConfigurations

代码语言:txt复制
final class CacheConfigurations {
	private static final Map<CacheType, Class<?>> MAPPINGS;
	static {
		Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
		mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
		mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
		mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
		mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
		mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
		mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
		mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
		mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
		mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
		mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
		MAPPINGS = Collections.unmodifiableMap(mappings);
	}
	public static String getConfigurationClass(CacheType cacheType) {
		Class<?> configurationClass = MAPPINGS.get(cacheType);
		Assert.state(configurationClass != null, () -> "Unknown cache type "   cacheType);
		return configurationClass.getName();
	}
	public static CacheType getType(String configurationClassName) {
		for (Map.Entry<CacheType, Class<?>> entry : MAPPINGS.entrySet()) {
			if (entry.getValue().getName().equals(configurationClassName)) {
				return entry.getKey();
			}
		}
		throw new IllegalStateException(
				"Unknown configuration class "   configurationClassName);
	}
}

CacheConfigurations是一个缓存配置缓存,听起来比较拗口,这里定义了Springboot支持的缓存类型,一共10种,基本覆盖了所有流行的缓存类型,主要mappings也放入了NoOpCacheConfiguration无缓存配置,当不启用缓存时使用该配置,可以理解为一种特殊的缓存。

CacheAutoConfiguration

代码语言:txt复制
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
		HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public CacheManagerCustomizers cacheManagerCustomizers(
			ObjectProvider<CacheManagerCustomizer<?>> customizers) {
		return new CacheManagerCustomizers(
				customizers.orderedStream().collect(Collectors.toList()));
	}
	@Bean
	public CacheManagerValidator cacheAutoConfigurationValidator(
			CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
		return new CacheManagerValidator(cacheProperties, cacheManager);
	}
	@Configuration
	@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
	@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
	protected static class CacheManagerJpaDependencyConfiguration
			extends EntityManagerFactoryDependsOnPostProcessor {

		public CacheManagerJpaDependencyConfiguration() {
			super("cacheManager");
		}
	}
}

这个类也是Springboot默认缓存配置的入口,类名上有很多注解,限制了改配置的启动条件和装配规则等,@ConditionalOnClass(CacheManager.class)限制应用类路径中必须有CacheManager实现,@ConditionalOnBean(CacheAspectSupport.class)限制应用容器中必须有CacheAspectSupport或者子类实例,@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")限制应用容器中不能有类型为CacheManager切名称为cacheResolver的bean,如果用户自定义了那么该配置就失效,@EnableConfigurationProperties(CacheProperties.class)是表示启用缓存属性配置,@AutoConfigureAfter限制该类在CouchbaseAutoConfiguration、HazelcastAutoConfiguration、HibernateJpaAutoConfiguration和RedisAutoConfiguration之后配置,@Import(CacheConfigurationImportSelector.class)引入了内部定义的CacheConfigurationImportSelector配置;先看CacheConfigurationImportSelector:

代码语言:txt复制
/**
 * {@link ImportSelector} to add {@link CacheType} configuration classes.
 */
static class CacheConfigurationImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		CacheType[] types = CacheType.values();
		String[] imports = new String[types.length];
		for (int i = 0; i < types.length; i  ) {
			imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
		}
		return imports;
	}
}

该类会导入CacheType中定义的所有支持的缓存类型配置,CacheAutoConfiguration中还定义了几个bean,CacheManagerCustomizers是CacheManager容器,CacheManagerValidator在调用时检查CacheManager是否存在并给出自定义异常,CacheManagerJpaDependencyConfiguration是对CacheManager依赖Jpa相关属性的定义和后置处理。

接着我们对前边所说的几种常用的缓存配置做下分析:

NoOpCacheConfiguration

代码语言:txt复制
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class NoOpCacheConfiguration {
	@Bean
	public NoOpCacheManager cacheManager() {
		return new NoOpCacheManager();
	}
}

NoOpCacheConfiguration定义了NoOpCacheManager,实现CacheManager接口直接调用业务逻辑提取数据,其实是假缓存。

SimpleCacheConfiguration

代码语言:txt复制
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

	private final CacheProperties cacheProperties;

	private final CacheManagerCustomizers customizerInvoker;

	SimpleCacheConfiguration(CacheProperties cacheProperties,
			CacheManagerCustomizers customizerInvoker) {
		this.cacheProperties = cacheProperties;
		this.customizerInvoker = customizerInvoker;
	}
	@Bean
	public ConcurrentMapCacheManager cacheManager() {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = this.cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return this.customizerInvoker.customize(cacheManager);
	}
}

SimpleCacheConfiguration定义了使用ConcurrentMap作为本地缓存的CacheManager。CaffeineCacheConfiguration也类似,定义使用Caffeine作为本地缓存的bean。

GenericCacheConfiguration

代码语言:txt复制
@Configuration
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GenericCacheConfiguration {
	private final CacheManagerCustomizers customizers;
	GenericCacheConfiguration(CacheManagerCustomizers customizers) {
		this.customizers = customizers;
	}
	@Bean
	public SimpleCacheManager cacheManager(Collection<Cache> caches) {
		SimpleCacheManager cacheManager = new SimpleCacheManager();
		cacheManager.setCaches(caches);
		return this.customizers.customize(cacheManager);
	}
}

GenericCacheConfiguration注入了类型为SimpleCacheManager的bean,其继承AbstractCacheManager使用ConcurrentMap做本地缓存。

SimpleCacheManager.pngSimpleCacheManager.png

RedisCacheConfiguration

代码语言:txt复制
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
	private final CacheProperties cacheProperties;
	private final CacheManagerCustomizers customizerInvoker;
	private final org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration;
	RedisCacheConfiguration(CacheProperties cacheProperties,
			CacheManagerCustomizers customizerInvoker,
			ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration) {
		this.cacheProperties = cacheProperties;
		this.customizerInvoker = customizerInvoker;
		this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable();
	}
	@Bean
	public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
			ResourceLoader resourceLoader) {
		RedisCacheManagerBuilder builder = RedisCacheManager
				.builder(redisConnectionFactory)
				.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
		List<String> cacheNames = this.cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
		}
		return this.customizerInvoker.customize(builder.build());
	}
	private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
			ClassLoader classLoader) {
		if (this.redisCacheConfiguration != null) {
			return this.redisCacheConfiguration;
		}
		Redis redisProperties = this.cacheProperties.getRedis();
		org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
				.defaultCacheConfig();
		config = config.serializeValuesWith(SerializationPair
				.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
		if (redisProperties.getTimeToLive() != null) {
			config = config.entryTtl(redisProperties.getTimeToLive());
		}
		if (redisProperties.getKeyPrefix() != null) {
			config = config.prefixKeysWith(redisProperties.getKeyPrefix());
		}
		if (!redisProperties.isCacheNullValues()) {
			config = config.disableCachingNullValues();
		}
		if (!redisProperties.isUseKeyPrefix()) {
			config = config.disableKeyPrefix();
		}
		return config;
	}
}

RedisCacheConfiguration注入了RedisCacheManager类型的bean,该配置生效有几个条件:

  • 只有应用引入了redis依赖并且定义了RedisConnectionFactory
  • 没有定义其他类型的CacheManager
  • spring.cache.type属性为redis
  • 在RedisAutoConfiguration之后配置

redis类型的缓存配置稍微复杂,既然依赖了RedisAutoConfiguration配置,我们也简单看一下:

代码语言:txt复制
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
}

RedisAutoConfiguration依赖redis,并且导入了LettuceConnectionConfiguration和JedisConnectionConfiguration连接配置(此处不展开分析),定义了RedisTemplate和StringRedisTemplate两个bean供RedisCacheManager使用。

5.默认配置

前边我们把缓存的原理积分都分析完了,那么在前边的使用方式介绍中使用的是哪一种缓存呢?也就是说不做任何配置Springboot启用的是哪一种类型的缓存?回过头来我们再看一下CacheAutoConfiguration导入的配置CacheConfigurationImportSelector:

代码语言:txt复制
static class CacheConfigurationImportSelector implements ImportSelector {
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
		CacheType[] types = CacheType.values();
		String[] imports = new String[types.length];
		for (int i = 0; i < types.length; i  ) {
			imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
		}
		return imports;
	}
}

这里是从枚举类CacheType获取支持的缓存类型然后从缓存配置缓存CacheConfigurations中获取对应的配置并顺序导入,看一下支持的缓存类型:

代码语言:txt复制
public enum CacheType {
	/**
	 * Generic caching using 'Cache' beans from the context.
	 */
	GENERIC,

	/**
	 * JCache (JSR-107) backed caching.
	 */
	JCACHE,

	/**
	 * EhCache backed caching.
	 */
	EHCACHE,

	/**
	 * Hazelcast backed caching.
	 */
	HAZELCAST,

	/**
	 * Infinispan backed caching.
	 */
	INFINISPAN,

	/**
	 * Couchbase backed caching.
	 */
	COUCHBASE,

	/**
	 * Redis backed caching.
	 */
	REDIS,

	/**
	 * Caffeine backed caching.
	 */
	CAFFEINE,

	/**
	 * Simple in-memory caching.
	 */
	SIMPLE,

	/**
	 * No caching.
	 */
	NONE
}

缓存配置导入有三个条件:

  • 如果依赖三方包,需要导入并定义相关bean,比如Caffeine和Redis
  • 如果用户自定义spring.cache.type,满足该条件的优先
  • 自然顺序导入

综合我们的使用方式和缓存配置导入条件,很显然导入的是GenericCacheConfiguration配置,注入的CacheManager类型是SimpleCacheManager,使用的是ConcurrentMap类型的本地缓存,至于怎么去验证本篇不再展开介绍,感兴趣的可以自己验证。

四、总结

Spring的缓存套件极大地方便了应用引入缓存编程,只需要在启动类开启缓存并定义相关组件能力即可使用,而Springboot对缓存的进一步封装和配置更是简化了我们的接入步骤和配置,只需要定义缓存开启类型和三方组件的连接就可以使用缓存。

用户如果有自定义诉求,只需要自己实现CacheManager即可,但是也会失去Springboot帮我们定义的能力,这一点需要具体场景具体评估。

互联网发展到今天,特别是电商场景,对于缓存的需求和要求更高,有些业务场景需要同时使用本地缓存和远程缓存(redis)来应对突发流量和热点数据问题,也就是二级或者多级缓存,Springboot并没有对于该场景给出比较好的支持,也算是缺憾之一吧,如果有类似诉求可以基于Springboot的缓存套件基于其缓存配置规则做自定义扩展和实现。

0 人点赞