从Spring3.1版本开始,Spring框架就支持显式地将缓存添加到现有的Spring应用程序中。与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的侵入最小。
Spring缓存的实现在spring-context包,如果是基于springboot基础框架编程,在spring-boot-autoconfige
中有很多默认的配置和定义,能更大程度上让用户无感知开启cache能力,如果不需要三方套件提供的缓存能力,就不需要引入额外的依赖。本篇的分析是基于springboot2.1.3版本。
缓存使用原理大致如下图:
Spring cache提供了开箱即用的接入方式,只需要若干注解和缓存管理类即可接入.
引入缓存依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>2.1.3.RELEASE</version> </dependency>
在应用启动类添加@EnableCaching注解:
@SpringBootApplication @EnableCaching public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
在业务方法添加@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<TaskInfoDTO> testCache() { TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123"); return IResp.getSuccessResult(taskInfoDTO); }
连续发送两次查询请求:
curl http://localhost:port/test_cache
从日志中看到只打印了一次DB调用,也就是说明第二次走了缓存。就这么简单我们就开启并使用了spring的缓存能力。
我们主要从spring-context和spring-boot-autoconfige两个包的cache模块分析cache原理。
开启缓存时序图:
缓存通知配置时序图:
缓存通知装配
@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<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两个配置。
父类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<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配置实现:
@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继承AbstractFallbackCacheOperationSource类实现CacheOperationSource接口,实现getCacheOperations方法将目标方法上缓存注解解析成缓存操作集合,AbstractFallbackCacheOperationSource.getCacheOperations:
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计算:
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实现:
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,将注解分别解析成对应的操作:
@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实现Advice接口并继承了CacheAspectSupport,看一些实现:
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方法:
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:
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上下文并调用重载方法:
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:
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)。
前边讲述了缓存配置和工作流程,那么上述的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<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提取属性,然后手动注册自动代理创建器:
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继承关系:
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<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增强器切入逻辑的类织入增强逻辑,也就是缓存能力,看一下方法实现:
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<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
该方法在子类AbstractAdvisorAutoProxyCreator中实现,接着调用了findEligibleAdvisors方法:
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方法筛选出对当前代理类适用的增强器:
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方法处理:
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检查是否符合条件,如果符合则加入返回列表:
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<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创建代理:
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创建代理。
在本篇第二节使用方式介绍的时候,我们引入了缓存依赖开启缓存能力就能直接使用缓存了,并没有引入或者配的
其他的缓存组件,那么问题就来了,为什么直接就能使用缓存了,如果应用架构基于Spring而不是Springboot,那么肯定是要自己配置CacheResolver或者CacheManager的,为什么这里不需要,这一切还是要归功于spring-boot-autoconfigure,我们使用Springboot作为基础框架时,一般都会显式或者间接把其引入。
spring-boot-autoconfigure有个包叫cache,毫无以为这里就是springboot定义并自动开启缓存配置的地方,该包下基本都是*Configuration类型的类,也就是Springboot自带的缓存相关配置,我们简单分析一下CacheAutoConfiguration、CacheConfigurations、GenericCacheConfiguration、NoOpCacheConfiguration、SimpleCacheConfiguration、CaffeineCacheConfiguration和RedisCacheConfiguration这几个配置类。
CacheConfigurations
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
@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:
/** * {@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<String> 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<Cache> caches) { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(caches); return this.customizers.customize(cacheManager); } }
GenericCacheConfiguration注入了类型为SimpleCacheManager的bean,其继承AbstractCacheManager使用ConcurrentMap做本地缓存。
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<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类型的缓存配置稍微复杂,既然依赖了RedisAutoConfiguration配置,我们也简单看一下:
@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使用。
前边我们把缓存的原理积分都分析完了,那么在前边的使用方式介绍中使用的是哪一种缓存呢?也就是说不做任何配置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 }
缓存配置导入有三个条件:
综合我们的使用方式和缓存配置导入条件,很显然导入的是GenericCacheConfiguration配置,注入的CacheManager类型是SimpleCacheManager,使用的是ConcurrentMap类型的本地缓存,至于怎么去验证本篇不再展开介绍,感兴趣的可以自己验证。
Spring的缓存套件极大地方便了应用引入缓存编程,只需要在启动类开启缓存并定义相关组件能力即可使用,而Springboot对缓存的进一步封装和配置更是简化了我们的接入步骤和配置,只需要定义缓存开启类型和三方组件的连接就可以使用缓存。
用户如果有自定义诉求,只需要自己实现CacheManager即可,但是也会失去Springboot帮我们定义的能力,这一点需要具体场景具体评估。
互联网发展到今天,特别是电商场景,对于缓存的需求和要求更高,有些业务场景需要同时使用本地缓存和远程缓存(redis)来应对突发流量和热点数据问题,也就是二级或者多级缓存,Springboot并没有对于该场景给出比较好的支持,也算是缺憾之一吧,如果有类似诉求可以基于Springboot的缓存套件基于其缓存配置规则做自定义扩展和实现。
注册了 域名 不备案可以吗?可以的。 注册域名 并不是一定要备案的,只有搭 建网...
??提到慕尼黑,大家第一个想到总是啤酒节,其实慕尼黑的文化同样闻名世界。慕尼...
3月24日,腾讯发布2020年Q4及全年财报,其中金融科技及企业服务第四季收入385亿...
排查思路 无法通过远程桌面连接裸金属服务器时,我们推荐您按照以下思路排查问题...
客户简介 全民直播是一家涵盖游戏、娱乐、户外等多领域泛娱乐的直播平台。2015年...
客户简介 趣医网(quyiyuan.com)创立于2014年,为京颐集团重要成员企业之一,是...
案例背景 高校健康打卡项目发起于北京大学软件与微电子学院,是该学院张齐勋老师...
API风格说明 当前ECS服务对外开放两类风格的API: ECS服务自定义规范的API(以下...
公司介绍 我们公司是全球法律服务整合平台,已有的4万多名律师遍布全国359个城市...
云虚拟主机 可以干什么?云 虚拟主机 可以是搭 建网站 的重要产品,可用来存放网...