SpringCache源码剖析

316 阅读5分钟

简介

本章内容基础读者了解springcache的基本使用的情况下进行编写,如有读者对springcache基本使用不了解的话,请点击SpringCache使用

@EnableCaching注解原理

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
   //是否开启cglib代理
   boolean proxyTargetClass() default false;

   //代理模式,PROXY=代理,ASPECT=
   AdviceMode mode() default AdviceMode.PROXY;

   int order() default Ordered.LOWEST_PRECEDENCE;

}

spring使用@EnableCaching注解来启用springcache功能,在EnableCaching中有以上几个参数,以及核心类CachingConfigurationSelector

参数说明:

参数说明
proxyTargetClass是否使用cglib代理,默认false
mode代理方式(PROXY(代理模式)、ASPECT(切面模式))
order多个代理器同时在一个类上作用时,该代理器的顺序

CachingConfigurationSelector介绍

该类在被@Import注解注释,表明CachingConfigurationSelector是一个配置类,spring会调用CachingConfigurationSelector中的selectImports方法来实例化查询出来的类,接下来以PROXY代理方式来介绍源码

public String[] selectImports(AdviceMode adviceMode) {
   return switch (adviceMode) {
      case PROXY -> getProxyImports();
      case ASPECTJ -> getAspectJImports();
   };
}
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);
}

在getProxyImports返回了AutoProxyRegistrar跟ProxyCachingConfiguration类名,代表了spring会对着2个类进行实例化,接下来我们来看一下这2个类具体做了什么。

AutoProxyRegistrar介绍

该类主要实现了注册aop所需要的核心bean,启用aop代理功能的核心类,比如开启事务注解也会注册这个类

ProxyCachingConfiguration介绍

ProxyCachingConfiguration类内容介绍

该类是SpringCache中一个核心类,其主要功能就是给把原始spring对象封装成SpringCache代理对象,让其方法可被SpringCache框架拦截从而实现缓存逻辑。里面做了3件事

  1. 初始化CacheOperationSource(用于根据类+方法获取方法上缓存注解的一些信息)
  2. 初始化CacheInterceptor,设置errorHandler(错误处理器), keyGenerator(key生成器), this.cacheResolver(缓存处理器), this.cacheManager(缓存实现方法)到CacheInterceptor,设置CacheOperationSource到CacheInterceptor中
  3. 初始化BeanFactoryCacheOperationSourceAdvisor对象,设置cacheOperationSource跟cacheInterceptor,用户spring注册bean的时候使用
/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cache.annotation;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.config.CacheManagementConfigUtils;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cache.interceptor.CacheOperationSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;

/**
 * {@code @Configuration} class that registers the Spring infrastructure beans necessary
 * to enable proxy-based annotation-driven cache management.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @since 3.1
 * @see EnableCaching
 * @see CachingConfigurationSelector
 */
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

   @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
         CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {

      BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
      //1.设置缓存操作类
      advisor.setCacheOperationSource(cacheOperationSource);
      //2.设置aop拦截器
      advisor.setAdvice(cacheInterceptor);
      if (this.enableCaching != null) {
         advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
      }
      return advisor;
   }

   /**
    * spring操作方法获取类,通过方法+类获取类上的注解
    */
   @Bean
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public CacheOperationSource cacheOperationSource() {
      // Accept protected @Cacheable etc methods on CGLIB proxies, as of 6.0.
      return new AnnotationCacheOperationSource(false);
   }

   /**
    * 拦截器
    */
   @Bean
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
      CacheInterceptor interceptor = new CacheInterceptor();
      interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
      interceptor.setCacheOperationSource(cacheOperationSource);
      return interceptor;
   }

}

ProxyCachingConfiguration注解逻辑介绍

在springbean初始化过程中该对象在org.springframework.cache.config.AnnotationDrivenCacheBeanDefinitionParser.SpringCachingConfigurer#registerCacheAdvisor中被获取以及初始化成BeanDefinition,然后随着spring一系列流程被初始化成spring的bean对象

@CacheInterceptor springcache方法拦截器介绍

该类是aop中的拦截器,主要作用是重写invoke方法实现调用方法先走缓存,再调用被代理方法

@CacheInterceptor继承图

CacheInterceptor.png

@CacheInterceptor执行逻辑说明

CacheInterceptor中invoke方法中主要逻辑实现在CacheAspectSupport的execute方法中

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

      Object target = invocation.getThis();
      Assert.state(target != null, "Target must not be null");
      try {
          //实现缓存代理方法
         return execute(aopAllianceInvoker, target, method, invocation.getArguments());
      }
      catch (CacheOperationInvoker.ThrowableWrapper th) {
         throw th.getOriginal();
      }
   }

}

CacheAspectSupport对象的execute方法说明

@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
   //判断方法上的注解使用启用了sync
   if (contexts.isSynchronized()) {
      //获取缓存操作对象上下文
      CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
      //满足condition条件
      if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
         //生成缓存key
         Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
         //获取公共缓存操作方法接口
         Cache cache = context.getCaches().iterator().next();
         try {
            //执行方法业务方法获取返回值
            return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
         }
         catch (Cache.ValueRetrievalException ex) {
            // Directly propagate ThrowableWrapper from the invoker,
            // or potentially also an IllegalArgumentException etc.
            ReflectionUtils.rethrowRuntimeException(ex.getCause());
         }
      }
      else {
         // No caching required, only call the underlying method
         return invokeOperation(invoker);
      }
   }


   // 执行@CacheEvict注解的内容来使缓存失效
   processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
         CacheOperationExpressionEvaluator.NO_RESULT);

   // 获取命中的缓存
   Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

   // 处理@CachePut
   List<CachePutRequest> cachePutRequests = new ArrayList<>();
   if (cacheHit == null) {
       //手机需要更新的缓存操作到cachePutRequests
      collectPutRequests(contexts.get(CacheableOperation.class),
            CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
   }

   Object cacheValue;
   Object returnValue;
    
   if (cacheHit != null && !hasCachePut(contexts)) {
      // 如果没有put请求,就使用缓存命中
      cacheValue = cacheHit.get();
      returnValue = wrapCacheValue(method, cacheValue);
   }
   else {
      // 如果没有查询到缓存,就执行方法来获取结果
      returnValue = invokeOperation(invoker);
      cacheValue = unwrapReturnValue(returnValue);
   }

   // 再次获取需要处理的更新缓存操作,@CachePut,因为注解中有unless字段,需要根据返回值来判断是否满足
   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);
   }

   // 执行缓存失效的操作,由于unless字段需要重复执行该动作
   processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

   return returnValue;
}

processCacheEvicts方法简介

private void processCacheEvicts(
      Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
   //遍历所有的缓存失效操作对象上下文
   for (CacheOperationContext context : contexts) {
      CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
      //满足缓存失效条件
      if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
          //进行缓存失效操作
         performCacheEvict(context, operation, result);
      }
   }
}

private void performCacheEvict(
			CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {

		Object key = null;
                //循环方法对应的所有缓存失效的缓存器
		for (Cache cache : context.getCaches()) {
                        //
			if (operation.isCacheWide()) {
				logInvalidating(context, operation, null);
				doClear(cache, operation.isBeforeInvocation());
			}
			else {
				if (key == null) {
                                //获取key
					key = generateKey(context, result);
				}
                                //记录日志
				logInvalidating(context, operation, key);
                                //执行缓存失效方法
				doEvict(cache, key, operation.isBeforeInvocation());
			}
		}
	}
        
        protected void doEvict(Cache cache, Object key, boolean immediate) {
		try {
			if (immediate) {
                        //立即执行缓存失效
				cache.evictIfPresent(key);
			}
			else {
                        //可能是异步调用,有可能会延迟
                        //调用缓存失效方法接口,按照具体实现类执行对应方法
				cache.evict(key);
			}
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCacheEvictError(ex, cache, key);
		}
	}

collectPutRequests方法简介

private void collectPutRequests(Collection<CacheOperationContext> contexts,
			@Nullable Object result, Collection<CachePutRequest> putRequests) {
		for (CacheOperationContext context : contexts) {
                        //判断condition是否满足条件
			if (isConditionPassing(context, result)) {
                                //根据key生成器生成key
				Object key = generateKey(context, result);
                                //将CachePutRequest对象放到putRequests中
				putRequests.add(new CachePutRequest(context, key));
			}
		}
	}
        
protected boolean isConditionPassing(@Nullable Object result) {
			if (this.conditionPassing == null) {
				if (StringUtils.hasText(this.metadata.operation.getCondition())) {
					EvaluationContext evaluationContext = createEvaluationContext(result);
					this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
							this.metadata.methodKey, evaluationContext);
				}
				else {
					this.conditionPassing = true;
				}
			}
			return this.conditionPassing;
		}

CachePutRequest.apply()方法简介

public void apply(@Nullable Object result) {
    //调用canPutToCache判断是否需要执行动作,主要是unless字段条件的判断,
   if (this.context.canPutToCache(result)) {
      //获取方法对应所有的缓存器Cache对象集合
      for (Cache cache : this.context.getCaches()) {
         //调用接口Cache的put方法根据具体的实现方法来设置缓存
         doPut(cache, this.key, result);
      }
   }
}

protected void doPut(Cache cache, Object key, @Nullable Object result) {
		try {
                        //调用接口Cache的put方法根据具体的实现方法来设置缓存
			cache.put(key, result);
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCachePutError(ex, cache, key, result);
		}
	}
        
protected boolean canPutToCache(@Nullable Object value) {
			String unless = "";
			if (this.metadata.operation instanceof CacheableOperation) {
				unless = ((CacheableOperation) this.metadata.operation).getUnless();
			}
			else if (this.metadata.operation instanceof CachePutOperation) {
				unless = ((CachePutOperation) this.metadata.operation).getUnless();
			}
			if (StringUtils.hasText(unless)) {
				EvaluationContext evaluationContext = createEvaluationContext(value);
				return !evaluator.unless(unless, this.metadata.methodKey, evaluationContext);
			}
			return true;
		}