Spring cache原理詳解

一图甜、概述

從Spring3.1版本開(kāi)始,Spring框架就支持顯式地將緩存添加到現(xiàn)有的Spring應(yīng)用程序中鳖眼。與事務(wù)支持類(lèi)似黑毅,緩存抽象允許一致地使用各種緩存解決方案,而對(duì)代碼的侵入最小具帮。 Spring緩存的實(shí)現(xiàn)在spring-context包,如果是基于springboot基礎(chǔ)框架編程,在spring-boot-autoconfige 中有很多默認(rèn)的配置和定義,能更大程度上讓用戶(hù)無(wú)感知開(kāi)啟cache能力,如果不需要三方套件提供的緩存能力,就不需要引入額外的依賴(lài)博肋。本篇的分析是基于springboot2.1.3版本。 緩存使用原理大致如下圖:

二蜂厅、使用方式

Spring cache提供了開(kāi)箱即用的接入方式,只需要若干注解和緩存管理類(lèi)即可接入.

1.開(kāi)啟緩存能力

引入緩存依賴(lài):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    <version>2.1.3.RELEASE</version>
</dependency>

在應(yīng)用啟動(dòng)類(lèi)添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.使用緩存

在業(yè)務(wù)方法添加@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;
}

模擬請(qǐng)求:

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

連續(xù)發(fā)送兩次查詢(xún)請(qǐng)求:

curl http://localhost:port/test_cache

從日志中看到只打印了一次DB調(diào)用,也就是說(shuō)明第二次走了緩存匪凡。就這么簡(jiǎn)單我們就開(kāi)啟并使用了spring的緩存能力。

三掘猿、源碼&原理解析

我們主要從spring-context和spring-boot-autoconfige兩個(gè)包的cache模塊分析cache原理病游。 開(kāi)啟緩存時(shí)序圖:

緩存通知配置時(shí)序圖:

緩存通知裝配

1.開(kāi)啟緩存

@EnableCaching

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
    //是否直接代理目標(biāo)類(lèi)
    boolean proxyTargetClass() default false;
    //通知模式,默認(rèn)是代理
    AdviceMode mode() default AdviceMode.PROXY;
    //順序
    int order() default Ordered.LOWEST_PRECEDENCE;
}

這個(gè)注解和@EnableAsync注解特別像,說(shuō)明都是基于Aop和代理做了能力增強(qiáng),該類(lèi)導(dǎo)入了CachingConfigurationSelector類(lèi),我們看一下其實(shí)現(xiàn):

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

有個(gè)概念需要解釋一下,jsr107是關(guān)于java關(guān)于緩存的規(guī)范提案,使用的話(huà)需要引入javax.cache相關(guān)包,出場(chǎng)率不高。CachingConfigurationSelector類(lèi)的核心是selectImports方法,根據(jù)@EnableCaching配置的模式選擇不同的配置類(lèi)型,默認(rèn)是PROXY模式,導(dǎo)入AutoProxyRegistrar和ProxyCachingConfiguration兩個(gè)配置稠通。

2.緩存通知配置

ProxyCachingConfiguration.png

父類(lèi)AbstractCachingConfiguration實(shí)現(xiàn):

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

做了兩件事,首先把注解元數(shù)據(jù)屬性解析出來(lái),然后把用戶(hù)自定義的緩存組件裝配進(jìn)來(lái)(CacheManager,KeyGenerator和異常處理器)衬衬。 在看ProxyCachingConfiguration配置實(shí)現(xiàn):

@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復(fù)用了父類(lèi)的能力并且定了AOP的三個(gè)核心組件(Pointcut,Advice和Advisor),先看AnnotationCacheOperationSource(此時(shí)還不能被稱(chēng)作Pointcut):
AnnotationCacheOperationSource.png

AnnotationCacheOperationSource繼承AbstractFallbackCacheOperationSource類(lèi)實(shí)現(xiàn)CacheOperationSource接口,實(shí)現(xiàn)getCacheOperations方法將目標(biāo)方法上緩存注解解析成緩存操作集合,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構(gòu)造成的MethodClassKey,如果緩存中有緩存操作集合直接返回,否則調(diào)用computeCacheOperations計(jì)算:

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

該方法是從目標(biāo)類(lèi)和目標(biāo)方法上(優(yōu)先方法維度)解析緩存注解裝配成緩存操作(@Cacheable -> CacheableOperation),看子類(lèi) AnnotationCacheOperationSource實(shí)現(xiàn):

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默認(rèn)構(gòu)造器使用的是SpringCacheAnnotationParser解析器,解析操作最終委托給SpringCacheAnnotationParser.parseCacheAnnotations,將注解分別解析成對(duì)應(yīng)的操作:

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

CacheInterceptor實(shí)現(xiàn)Advice接口并繼承了CacheAspectSupport,看一些實(shí)現(xiàn):

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

當(dāng)攔截到調(diào)用時(shí),將調(diào)用封裝成CacheOperationInvoker并交給父類(lèi)執(zhí)行,父類(lèi)CacheAspectSupport實(shí)現(xiàn)了SmartInitializingSingleton接口,在單例初始化后容器會(huì)調(diào)用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;
}

檢查有沒(méi)有合適的CacheManager,并且將initialized設(shè)置為true,繼續(xù)看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目標(biāo)類(lèi)和方法上的緩存注解解析成操作集合,然后構(gòu)造CacheOperationContexts上下文并調(diào)用重載方法:

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

將每個(gè)操作包裝對(duì)應(yīng)上下文映射關(guān)系,并檢查是否是同步操作(@Cacheable獨(dú)有屬性),繼續(xù)看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特性),如果是且滿(mǎn)足條件調(diào)用緩存獲取邏輯并返回,否則返回業(yè)務(wù)邏輯免緩存調(diào)用invokeOperation,然后執(zhí)行@CacheEvict的前置清除(beforeInvocation=true),接著檢查@Cacheable是否命中緩存,如果沒(méi)有命中則放入需要執(zhí)行CachePutRequest列表暫存,然后檢查是否緩存命中且沒(méi)有需要更新的緩存,如果滿(mǎn)足則返回結(jié)果使用緩存結(jié)果,否則使用業(yè)務(wù)查詢(xún)結(jié)果作為返回結(jié)果,并且填充需要緩存的結(jié)果,然后收集@CachePut操作,把@CachePut和@Cacheable未命中的請(qǐng)求同步到緩存,最后清理@CacheEvict的緩存(beforeInvocation=false)。

3.緩存代理裝配

前邊講述了緩存配置和工作流程,那么上述的Aop配置什么時(shí)候生效?在哪里生效?如何生效改橘?接下來(lái)我們將從AutoProxyRegistrar作為切入點(diǎn)展開(kāi)分析緩存代理的裝配邏輯.

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實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,registerBeanDefinitions會(huì)從啟用緩存注解@EnableCaching提取屬性,然后手動(dòng)注冊(cè)自動(dòng)代理創(chuàng)建器:

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

手動(dòng)注冊(cè)了InfrastructureAdvisorAutoProxyCreator到容器中,看一下InfrastructureAdvisorAutoProxyCreator繼承關(guān)系:
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類(lèi)實(shí)現(xiàn)了BeanFactory初始化和isEligibleAdvisorBean方法,繼續(xù)看父類(lèi)AbstractAdvisorAutoProxyCreator實(shí)現(xiàn):

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,委托給子類(lèi)isEligibleAdvisorBean方法實(shí)現(xiàn)(InfrastructureAdvisorAutoProxyCreator)滋尉。 重點(diǎn)在于AbstractAdvisorAutoProxyCreator父類(lèi)AbstractAutoProxyCreator實(shí)現(xiàn)的postProcessBeforeInstantiation方法,該方法在InstantiationAwareBeanPostProcessor接口定義,該方法在bean創(chuàng)建之前調(diào)用,如果該方法返回非null對(duì)象,那么bean的創(chuàng)建過(guò)程將會(huì)短路,此處的作用是為滿(mǎn)足BeanFactoryCacheOperationSourceAdvisor增強(qiáng)器切入邏輯的類(lèi)織入增強(qiáng)邏輯,也就是緩存能力,看一下方法實(shí)現(xiàn):

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創(chuàng)建過(guò)程并織入增強(qiáng)邏輯,這里是自動(dòng)生成代理類(lèi)并且將緩存邏輯織入進(jìn)去。這里也是往上說(shuō)的自動(dòng)代理實(shí)現(xiàn)APC的核心邏輯飞主。該方法前半段是從緩存中獲取目標(biāo)類(lèi)是否被代理過(guò),如果被代理過(guò)直接把增強(qiáng)邏輯織入,避免重復(fù)創(chuàng)建代理,后半段就是生成代理的邏輯,創(chuàng)建代理過(guò)程我們之前分析過(guò),此處不再分析,重點(diǎn)分析一下從候選增強(qiáng)器中獲取增強(qiáng)邏輯的方法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();
}

該方法在子類(lèi)AbstractAdvisorAutoProxyCreator中實(shí)現(xiàn),接著調(diào)用了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;
}

先通過(guò)前邊定義的BeanFactoryAdvisorRetrievalHelper獲取候選增強(qiáng)器,然后調(diào)用findAdvisorsThatCanApply方法篩選出對(duì)當(dāng)前代理類(lèi)適用的增強(qiáng)器:

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工具類(lèi)的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中增強(qiáng)器的定義來(lái)看,BeanFactoryCacheOperationSourceAdvisor是PointcutAdvisor類(lèi)型,方法前半段IntroductionAdvisor邏輯跳過(guò),通過(guò)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;
    }
}

直接進(jìn)入第二個(gè)條件分支,檢查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;
}

這個(gè)方法也不復(fù)雜,其實(shí)就是檢查目標(biāo)類(lèi)和方法上是否有緩存相關(guān)注解(@Cacheable,@CachePut,@CacheEvict等)狮惜,如果有說(shuō)明增強(qiáng)器對(duì)目標(biāo)代理類(lèi)適用,然后找到合適的增強(qiáng)器列表在APC中調(diào)用createProxy創(chuàng)建代理:

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

這里創(chuàng)建代理工廠,然后選擇是否需要直接代理目標(biāo)類(lèi),然后裝配增強(qiáng)器,然后調(diào)用JdkDynamicAopProxy或者CglibAopProxy創(chuàng)建代理。

4.緩存配置

在本篇第二節(jié)使用方式介紹的時(shí)候,我們引入了緩存依賴(lài)開(kāi)啟緩存能力就能直接使用緩存了,并沒(méi)有引入或者配的 其他的緩存組件,那么問(wèn)題就來(lái)了,為什么直接就能使用緩存了,如果應(yīng)用架構(gòu)基于Spring而不是Springboot,那么肯定是要自己配置CacheResolver或者CacheManager的,為什么這里不需要,這一切還是要?dú)w功于spring-boot-autoconfigure,我們使用Springboot作為基礎(chǔ)框架時(shí),一般都會(huì)顯式或者間接把其引入碌识。
image.png

spring-boot-autoconfigure有個(gè)包叫cache,毫無(wú)以為這里就是springboot定義并自動(dòng)開(kāi)啟緩存配置的地方,該包下基本都是*Configuration類(lèi)型的類(lèi),也就是Springboot自帶的緩存相關(guān)配置,我們簡(jiǎn)單分析一下CacheAutoConfiguration碾篡、CacheConfigurations、GenericCacheConfiguration筏餐、NoOpCacheConfiguration开泽、SimpleCacheConfiguration、CaffeineCacheConfiguration和RedisCacheConfiguration這幾個(gè)配置類(lèi)魁瞪。 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是一個(gè)緩存配置緩存,聽(tīng)起來(lái)比較拗口,這里定義了Springboot支持的緩存類(lèi)型,一共10種,基本覆蓋了所有流行的緩存類(lèi)型,主要mappings也放入了NoOpCacheConfiguration無(wú)緩存配置,當(dāng)不啟用緩存時(shí)使用該配置,可以理解為一種特殊的緩存穆律。 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");
        }
    }
}

這個(gè)類(lèi)也是Springboot默認(rèn)緩存配置的入口,類(lèi)名上有很多注解,限制了改配置的啟動(dòng)條件和裝配規(guī)則等,@ConditionalOnClass(CacheManager.class)限制應(yīng)用類(lèi)路徑中必須有CacheManager實(shí)現(xiàn),@ConditionalOnBean(CacheAspectSupport.class)限制應(yīng)用容器中必須有CacheAspectSupport或者子類(lèi)實(shí)例,@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")限制應(yīng)用容器中不能有類(lèi)型為CacheManager切名稱(chēng)為cacheResolver的bean,如果用戶(hù)自定義了那么該配置就失效,@EnableConfigurationProperties(CacheProperties.class)是表示啟用緩存屬性配置,@AutoConfigureAfter限制該類(lèi)在CouchbaseAutoConfiguration、HazelcastAutoConfiguration佩番、HibernateJpaAutoConfiguration和RedisAutoConfiguration之后配置,@Import(CacheConfigurationImportSelector.class)引入了內(nèi)部定義的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;
    }
}

該類(lèi)會(huì)導(dǎo)入CacheType中定義的所有支持的緩存類(lèi)型配置,CacheAutoConfiguration中還定義了幾個(gè)bean,CacheManagerCustomizers是CacheManager容器,CacheManagerValidator在調(diào)用時(shí)檢查CacheManager是否存在并給出自定義異常,CacheManagerJpaDependencyConfiguration是對(duì)CacheManager依賴(lài)Jpa相關(guān)屬性的定義和后置處理众旗。 接著我們對(duì)前邊所說(shuō)的幾種常用的緩存配置做下分析: NoOpCacheConfiguration

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

NoOpCacheConfiguration定義了NoOpCacheManager,實(shí)現(xiàn)CacheManager接口直接調(diào)用業(yè)務(wù)邏輯提取數(shù)據(jù),其實(shí)是假緩存。 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也類(lèi)似,定義使用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注入了類(lèi)型為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<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類(lèi)型的bean,該配置生效有幾個(gè)條件:

  • 只有應(yīng)用引入了redis依賴(lài)并且定義了RedisConnectionFactory
  • 沒(méi)有定義其他類(lèi)型的CacheManager
  • spring.cache.type屬性為redis
  • 在RedisAutoConfiguration之后配置

redis類(lèi)型的緩存配置稍微復(fù)雜,既然依賴(lài)了RedisAutoConfiguration配置,我們也簡(jiǎn)單看一下:

@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依賴(lài)redis,并且導(dǎo)入了LettuceConnectionConfiguration和JedisConnectionConfiguration連接配置(此處不展開(kāi)分析),定義了RedisTemplate和StringRedisTemplate兩個(gè)bean供RedisCacheManager使用赋秀。

5.默認(rèn)配置

前邊我們把緩存的原理積分都分析完了,那么在前邊的使用方式介紹中使用的是哪一種緩存呢利朵?也就是說(shuō)不做任何配置Springboot啟用的是哪一種類(lèi)型的緩存?回過(guò)頭來(lái)我們?cè)倏匆幌翪acheAutoConfiguration導(dǎo)入的配置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;
    }
}

這里是從枚舉類(lèi)CacheType獲取支持的緩存類(lèi)型然后從緩存配置緩存CacheConfigurations中獲取對(duì)應(yīng)的配置并順序?qū)?看一下支持的緩存類(lèi)型:

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
}

緩存配置導(dǎo)入有三個(gè)條件:

  • 如果依賴(lài)三方包,需要導(dǎo)入并定義相關(guān)bean,比如Caffeine和Redis
  • 如果用戶(hù)自定義spring.cache.type,滿(mǎn)足該條件的優(yōu)先
  • 自然順序?qū)?/li>

綜合我們的使用方式和緩存配置導(dǎo)入條件,很顯然導(dǎo)入的是GenericCacheConfiguration配置,注入的CacheManager類(lèi)型是SimpleCacheManager,使用的是ConcurrentMap類(lèi)型的本地緩存,至于怎么去驗(yàn)證本篇不再展開(kāi)介紹,感興趣的可以自己驗(yàn)證猎莲。

四绍弟、總結(jié)

Spring的緩存套件極大地方便了應(yīng)用引入緩存編程,只需要在啟動(dòng)類(lèi)開(kāi)啟緩存并定義相關(guān)組件能力即可使用,而Springboot對(duì)緩存的進(jìn)一步封裝和配置更是簡(jiǎn)化了我們的接入步驟和配置,只需要定義緩存開(kāi)啟類(lèi)型和三方組件的連接就可以使用緩存。 用戶(hù)如果有自定義訴求,只需要自己實(shí)現(xiàn)CacheManager即可,但是也會(huì)失去Springboot幫我們定義的能力,這一點(diǎn)需要具體場(chǎng)景具體評(píng)估著洼。 互聯(lián)網(wǎng)發(fā)展到今天,特別是電商場(chǎng)景,對(duì)于緩存的需求和要求更高,有些業(yè)務(wù)場(chǎng)景需要同時(shí)使用本地緩存和遠(yuǎn)程緩存(redis)來(lái)應(yīng)對(duì)突發(fā)流量和熱點(diǎn)數(shù)據(jù)問(wèn)題,也就是二級(jí)或者多級(jí)緩存,Springboot并沒(méi)有對(duì)于該場(chǎng)景給出比較好的支持,也算是缺憾之一吧,如果有類(lèi)似訴求可以基于Springboot的緩存套件基于其緩存配置規(guī)則做自定義擴(kuò)展和實(shí)現(xiàn)樟遣。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末而叼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子豹悬,更是在濱河造成了極大的恐慌葵陵,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞻佛,死亡現(xiàn)場(chǎng)離奇詭異脱篙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)伤柄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)绊困,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人适刀,你說(shuō)我怎么就攤上這事秤朗。” “怎么了蔗彤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵川梅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我然遏,道長(zhǎng)贫途,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任待侵,我火速辦了婚禮丢早,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秧倾。我一直安慰自己怨酝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布那先。 她就那樣靜靜地躺著农猬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪售淡。 梳的紋絲不亂的頭發(fā)上斤葱,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音揖闸,去河邊找鬼揍堕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛汤纸,可吹牛的內(nèi)容都是我干的衩茸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贮泞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼楞慈!你這毒婦竟也來(lái)了幔烛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤囊蓝,失蹤者是張志新(化名)和其女友劉穎说贝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體慎颗,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年言询,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俯萎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡运杭,死狀恐怖夫啊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辆憔,我是刑警寧澤撇眯,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站虱咧,受9級(jí)特大地震影響熊榛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腕巡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一玄坦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绘沉,春花似錦煎楣、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至另玖,卻和暖如春困曙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背日矫。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工赂弓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哪轿。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓盈魁,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窃诉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杨耙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 隨著時(shí)間的積累赤套,應(yīng)用的使用用戶(hù)不斷增加,數(shù)據(jù)規(guī)模也越來(lái)越大珊膜,往往數(shù)據(jù)庫(kù)查詢(xún)操作會(huì)成為影響用戶(hù)使用體驗(yàn)的瓶頸容握,此時(shí)使...
    程序猿DD閱讀 3,370評(píng)論 1 51
  • 緩存的基本思想其實(shí)是以空間換時(shí)間。我們知道车柠,IO的讀寫(xiě)速度相對(duì)內(nèi)存來(lái)說(shuō)是非常比較慢的剔氏,通常一個(gè)web應(yīng)用的瓶頸就出...
    Lemonrel閱讀 307評(píng)論 0 0
  • Spring Cache本身是一個(gè)緩存體系的抽象實(shí)現(xiàn),并沒(méi)有具體的緩存能力竹祷,要使用Spring Cache還需要具...
    陳菲TW閱讀 549評(píng)論 0 1
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月谈跛,有人笑有人哭,有人歡樂(lè)有人憂(yōu)愁塑陵,有人驚喜有人失落感憾,有的覺(jué)得收獲滿(mǎn)滿(mǎn)有...
    陌忘宇閱讀 8,536評(píng)論 28 53
  • 信任包括信任自己和信任他人 很多時(shí)候,很多事情令花,失敗阻桅、遺憾、錯(cuò)過(guò)兼都,源于不自信嫂沉,不信任他人 覺(jué)得自己做不成,別人做不...
    吳氵晃閱讀 6,190評(píng)論 4 8