Spring cache原理详解

一、概述

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

二、使用方式

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

1.开启缓存能力

引入缓存依赖:


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


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

@SpringBootApplication
@EnableCaching
public class Application {

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

2.使用缓存

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

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

模拟请求:

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

连续发送两次查询请求:

curl http://localhost:port/test_cache

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

三、源码&原理解析

我们主要从spring-context和spring-boot-autoconfige两个包的cache模块分析cache原理。 开启缓存时序图:

缓存通知配置时序图:

缓存通知装配

1.开启缓存

@EnableCaching

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

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

public class CachingConfigurationSelector extends AdviceModeImportSelector {

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

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

2.缓存通知配置

ProxyCachingConfiguration.png

父类AbstractCachingConfiguration实现:

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

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

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
    @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
        BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
        advisor.setCacheOperationSource(cacheOperationSource());
        advisor.setAdvice(cacheInterceptor());
        if (this.enableCaching != null) {
            advisor.setOrder(this.enableCaching.getNumber("order"));
        }
        return advisor;
    }
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheInterceptor cacheInterceptor() {
        CacheInterceptor interceptor = new CacheInterceptor();
        interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
        interceptor.setCacheOperationSource(cacheOperationSource());
        return interceptor;
    }
}

ProxyCachingConfiguration复用了父类的能力并且定了AOP的三个核心组件(Pointcut,Advice和Advisor),先看AnnotationCacheOperationSource(此时还不能被称作Pointcut):
AnnotationCacheOperationSource.png

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

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

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

private Collection computeCacheOperations(Method method, @Nullable Class targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }
    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    // First try is the method in the target class.
    Collection opDef = findCacheOperations(specificMethod);
    if (opDef != null) {
        return opDef;
    }
    // Second try is the caching operation on the target class.
    opDef = findCacheOperations(specificMethod.getDeclaringClass());
    if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
        return opDef;
    }
    if (specificMethod != method) {
        // Fallback is to look at the original method.
        opDef = findCacheOperations(method);
        if (opDef != null) {
            return opDef;
        }
        // Last fallback is the class of the original method.
        opDef = findCacheOperations(method.getDeclaringClass());
        if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
            return opDef;
        }
    }
    return null;
}

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

public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
    this.publicMethodsOnly = publicMethodsOnly;
    this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
@Override
@Nullable
protected Collection findCacheOperations(Class clazz) {
    return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
@Override
@Nullable
protected Collection findCacheOperations(Method method) {
    return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}

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

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

@Override
@Nullable
public Collection parseCacheAnnotations(Class type) {
    DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
    return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection parseCacheAnnotations(Method method) {
    DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
    return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
    Collection ops = parseCacheAnnotations(cachingConfig, ae, false);
    if (ops != null && ops.size() > 1) {
        // More than one operation found -> local declarations override interface-declared ones...
        Collection localOps = parseCacheAnnotations(cachingConfig, ae, true);
        if (localOps != null) {
            return localOps;
        }
    }
    return ops;
}
@Nullable
private Collection parseCacheAnnotations(
        DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
    Collection anns = (localOnly ?
            AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
            AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
    if (anns.isEmpty()) {
        return null;
    }
    final Collection 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 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实现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 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 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 cachePutRequests = new LinkedList<>();
    if (cacheHit == null) {
        collectPutRequests(contexts.get(CacheableOperation.class),
                CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
    }
    Object cacheValue;
    Object returnValue;

    if (cacheHit != null && !hasCachePut(contexts)) {
        // If there are no put requests, just use the cache hit
        cacheValue = cacheHit.get();
        returnValue = wrapCacheValue(method, cacheValue);
    }
    else {
        // Invoke the method if we don't have a cache hit
        returnValue = invokeOperation(invoker);
        cacheValue = unwrapReturnValue(returnValue);
    }

    // Collect any explicit @CachePuts
    collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

    // Process any collected put requests, either from @CachePut or a @Cacheable miss
    for (CachePutRequest cachePutRequest : cachePutRequests) {
        cachePutRequest.apply(cacheValue);
    }

    // Process any late evictions
    processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

    return returnValue;
}

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

3.缓存代理装配

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

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

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

public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    return registerAutoProxyCreatorIfNecessary(registry, null);
}

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

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

手动注册了InfrastructureAdvisorAutoProxyCreator到容器中,看一下InfrastructureAdvisorAutoProxyCreator继承关系:
InfrastructureAdvisorAutoProxyCreator.png
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
    @Nullable
    private ConfigurableListableBeanFactory beanFactory;

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

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

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

AbstractAdvisorAutoProxyCreator定义了Advisor操作的工具方法,并且定义了Advisor提取适配器BeanFactoryAdvisorRetrievalHelperAdapter,委托给子类isEligibleAdvisorBean方法实现(InfrastructureAdvisorAutoProxyCreator)。 重点在于AbstractAdvisorAutoProxyCreator父类AbstractAutoProxyCreator实现的postProcessBeforeInstantiation方法,该方法在InstantiationAwareBeanPostProcessor接口定义,该方法在bean创建之前调用,如果该方法返回非null对象,那么bean的创建过程将会短路,此处的作用是为满足BeanFactoryCacheOperationSourceAdvisor增强器切入逻辑的类织入增强逻辑,也就是缓存能力,看一下方法实现:

public Object postProcessBeforeInstantiation(Class beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

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

    return null;
}

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

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

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

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

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

protected List findAdvisorsThatCanApply(
        List candidateAdvisors, Class beanClass, String beanName) {

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

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

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

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

public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn't have a pointcut so we assume it applies.
        return true;
    }
}

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

public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
    Set> 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创建代理。

4.缓存配置

在本篇第二节使用方式介绍的时候,我们引入了缓存依赖开启缓存能力就能直接使用缓存了,并没有引入或者配的 其他的缓存组件,那么问题就来了,为什么直接就能使用缓存了,如果应用架构基于Spring而不是Springboot,那么肯定是要自己配置CacheResolver或者CacheManager的,为什么这里不需要,这一切还是要归功于spring-boot-autoconfigure,我们使用Springboot作为基础框架时,一般都会显式或者间接把其引入。
image.png

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

final class CacheConfigurations {
    private static final Map> MAPPINGS;
    static {
        Map> 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> 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> customizers) {
        return new CacheManagerCustomizers(
                customizers.orderedStream().collect(Collectors.toList()));
    }
    @Bean
    public CacheManagerValidator cacheAutoConfigurationValidator(
            CacheProperties cacheProperties, ObjectProvider cacheManager) {
        return new CacheManagerValidator(cacheProperties, cacheManager);
    }
    @Configuration
    @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
    @ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
    protected static class CacheManagerJpaDependencyConfiguration
            extends EntityManagerFactoryDependsOnPostProcessor {

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

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

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

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

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

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

@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

    private final CacheProperties cacheProperties;

    private final CacheManagerCustomizers customizerInvoker;

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

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

@Configuration
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GenericCacheConfiguration {
    private final CacheManagerCustomizers customizers;
    GenericCacheConfiguration(CacheManagerCustomizers customizers) {
        this.customizers = customizers;
    }
    @Bean
    public SimpleCacheManager cacheManager(Collection caches) {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(caches);
        return this.customizers.customize(cacheManager);
    }
}

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

RedisCacheConfiguration

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

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

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

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

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

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

5.默认配置

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

static class CacheConfigurationImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        CacheType[] types = CacheType.values();
        String[] imports = new String[types.length];
        for (int i = 0; i < types.length; i++) {
            imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
        }
        return imports;
    }
}

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

public enum CacheType {
    /**
     * Generic caching using 'Cache' beans from the context.
     */
    GENERIC,

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

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

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

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

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

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

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

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

    /**
     * No caching.
     */
    NONE
}

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

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

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

四、总结

Spring的缓存套件极大地方便了应用引入缓存编程,只需要在启动类开启缓存并定义相关组件能力即可使用,而Springboot对缓存的进一步封装和配置更是简化了我们的接入步骤和配置,只需要定义缓存开启类型和三方组件的连接就可以使用缓存。 用户如果有自定义诉求,只需要自己实现CacheManager即可,但是也会失去Springboot帮我们定义的能力,这一点需要具体场景具体评估。 互联网发展到今天,特别是电商场景,对于缓存的需求和要求更高,有些业务场景需要同时使用本地缓存和远程缓存(redis)来应对突发流量和热点数据问题,也就是二级或者多级缓存,Springboot并没有对于该场景给出比较好的支持,也算是缺憾之一吧,如果有类似诉求可以基于Springboot的缓存套件基于其缓存配置规则做自定义扩展和实现。

你可能感兴趣的:(Spring cache原理详解)