Spring cache原理详解

您所在的位置:网站首页 spring框架工作原理 Spring cache原理详解

Spring cache原理详解

2023-09-27 20:39| 来源: 网络整理| 查看: 265

一、概述

从Spring3.1版本开始,Spring框架就支持显式地将缓存添加到现有的Spring应用程序中。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的侵入最小。 Spring缓存的实现在spring-context包,如果是基于springboot基础框架编程,在spring-boot-autoconfige 中有很多默认的配置和定义,能更大程度上让用户无感知开启cache能力,如果不需要三方套件提供的缓存能力,就不需要引入额外的依赖。本篇的分析是基于springboot2.1.3版本。 缓存使用原理大致如下图: image.png

二、使用方式

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

1.开启缓存能力

引入缓存依赖:

org.springframework.boot spring-boot-starter-cache 2.1.3.RELEASE

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

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

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

@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; }

模拟请求:

@GetMapping("/test_cache") public IResp testCache() { TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123"); return IResp.getSuccessResult(taskInfoDTO); }

连续发送两次查询请求:

curl http://localhost:port/test_cache

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

三、源码&原理解析

我们主要从spring-context和spring-boot-autoconfige两个包的cache模块分析cache原理。 开启缓存时序图: image.png 缓存通知配置时序图: image.png 缓存通知装配 image.png

1.开启缓存

@EnableCaching

@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类,我们看一下其实现:

public class CachingConfigurationSelector extends AdviceModeImportSelector { 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 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 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.png 父类AbstractCachingConfiguration实现:

@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 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配置实现:

@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.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.png AnnotationCacheOperationSource继承AbstractFallbackCacheOperationSource类实现CacheOperationSource接口,实现getCacheOperations方法将目标方法上缓存注解解析成缓存操作集合,AbstractFallbackCacheOperationSource.getCacheOperations:

public Collection getCacheOperations(Method method, @Nullable Class targetClass) { if (method.getDeclaringClass() == Object.class) { return null; } Object cacheKey = getCacheKey(method, targetClass); Collection cached = this.attributeCache.get(cacheKey); if (cached != null) { return (cached != NULL_CACHING_ATTRIBUTE ? cached : null); } else { Collection 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计算:

private Collection 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 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实现:

public AnnotationCacheOperationSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser()); } @Override @Nullable protected Collection findCacheOperations(Class clazz) { return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz)); } @Override @Nullable protected Collection findCacheOperations(Method method) { return determineCacheOperations(parser -> parser.parseCacheAnnotations(method)); } protected Collection determineCacheOperations(CacheOperationProvider provider) { Collection ops = null; for (CacheAnnotationParser annotationParser : this.annotationParsers) { Collection annOps = provider.getCacheOperations(annotationParser); if (annOps != null) { if (ops == null) { ops = annOps; } else { Collection combined = new ArrayList(ops.size() + annOps.size()); combined.addAll(ops); combined.addAll(annOps); ops = combined; } } } return ops; }

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

@Override @Nullable public Collection parseCacheAnnotations(Class type) { DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type); return parseCacheAnnotations(defaultConfig, type); } @Override @Nullable public Collection parseCacheAnnotations(Method method) { DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass()); return parseCacheAnnotations(defaultConfig, method); } @Nullable private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) { Collection ops = parseCacheAnnotations(cachingConfig, ae, false); if (ops != null && ops.size() > 1) { // More than one operation found -> local declarations override interface-declared ones... Collection localOps = parseCacheAnnotations(cachingConfig, ae, true); if (localOps != null) { return localOps; } } return ops; } @Nullable private Collection parseCacheAnnotations( DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) { Collection targetClass = getTargetClass(target); CacheOperationSource cacheOperationSource = getCacheOperationSource(); if (cacheOperationSource != null) { Collection 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上下文并调用重载方法:

public CacheOperationContexts(Collection 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:

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 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作为切入点展开分析缓存代理的装配逻辑.

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { private final Log logger = LogFactory.getLog(getClass()); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set 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提取属性,然后手动注册自动代理创建器:

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.png

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实现:

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 advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } protected List findEligibleAdvisors(Class beanClass, String beanName) { List candidateAdvisors = findCandidateAdvisors(); List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } protected List findCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans(); } protected List findAdvisorsThatCanApply( List 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增强器切入逻辑的类织入增强逻辑,也就是缓存能力,看一下方法实现:

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:

protected Object[] getAdvicesAndAdvisorsForBean( Class beanClass, String beanName, @Nullable TargetSource targetSource) { List advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }

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

protected List findEligibleAdvisors(Class beanClass, String beanName) { List candidateAdvisors = findCandidateAdvisors(); List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }

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

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

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

public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List 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检查是否符合条件,如果符合则加入返回列表:

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是否符合切入逻辑:

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> MAPPINGS; static { Map configurationClass = MAPPINGS.get(cacheType); Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType); return configurationClass.getName(); } public static CacheType getType(String configurationClassName) { for (Map.Entry> customizers) { return new CacheManagerCustomizers( customizers.orderedStream().collect(Collectors.toList())); } @Bean public CacheManagerValidator cacheAutoConfigurationValidator( CacheProperties cacheProperties, ObjectProvider 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:

/** * {@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

@Configuration @ConditionalOnMissingBean(CacheManager.class) @Conditional(CacheCondition.class) class NoOpCacheConfiguration { @Bean public NoOpCacheManager cacheManager() { return new NoOpCacheManager(); } }

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

@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 cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } return this.customizerInvoker.customize(cacheManager); } }

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

@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 caches) { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(caches); return this.customizers.customize(cacheManager); } }

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

@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 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 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配置,我们也简单看一下:

@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate 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:

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中获取对应的配置并顺序导入,看一下支持的缓存类型:

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的缓存套件基于其缓存配置规则做自定义扩展和实现。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3