Spring缓存切面源码解析

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

}

可以看到还是一样的套路,里面通过@Import注解往spring容器中注册了一个CachingConfigurationSelector类

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

然后这个类又是一个ImportSelect组件,所以从上面的代码可以看出最终往spring容器中注册了AutoProxyRegistrar以及ProxyCachingConfiguration

往容器中注册缓存支持的组件

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {

   @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
      BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
      advisor.setCacheOperationSource(cacheOperationSource());
      advisor.setAdvice(cacheInterceptor());
      if (this.enableCaching != null) {
         advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
      }
      return advisor;
   }

   @Bean
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public CacheOperationSource cacheOperationSource() {
      return new AnnotationCacheOperationSource();
   }

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

}

这个类往spring容器中注册了三个bean,分别是BeanFactoryCacheOperationSourceAdvisor,CacheOperationSource以及CacheInterceptor,这看上去怎么和spring的事务源码实现差不多?没错,spring缓存切面的实现与spring事务的代理实现大同小异,同样的都是会往容器中导入三个bean,一个是advisor,一个是advice,一个是pointcut,而一个advisor中会有一个advice以及pointcut

注册生成代理的后置处理器

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> annTypes = importingClassMetadata.getAnnotationTypes();
      for (String annType : annTypes) {
         AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
         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;
               }
            }
         }
      }
      if (!candidateFound && logger.isInfoEnabled()) {
         String name = getClass().getSimpleName();
         logger.info(String.format("%s was imported but no annotations were found " +
               "having both 'mode' and 'proxyTargetClass' attributes of type " +
               "AdviceMode and boolean respectively. This means that auto proxy " +
               "creator registration and configuration may not have occurred as " +
               "intended, and components may not be proxied as expected. Check to " +
               "ensure that %s has been @Import'ed on the same class where these " +
               "annotations are declared; otherwise remove the import of %s " +
               "altogether.", name, name, name));
      }
   }

}
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
      BeanDefinitionRegistry registry, @Nullable Object source) {

   return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

AutoProxyRegistrar是一个ImportBeanDefinitionRegistrar组件,会通过registry去给容器中手动注册bd,与spring事务一样,最终都是通过AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)去给容器注册InfrastructureAdvisorAutoProxyCreator,InfrastructureAdvisorAutoProxyCreator这里就不细说了,之前在spring事务的代理实现中已经说过它就是一个BeanPostProcessor,并且会在bean初始化的时候在postProcessAfterInitialization方法中生成了代理对象,而负责生成代理对象的方法就在wrapIfNecessary方法中

在wrapIfNecessary方法中会去spring容器中寻找role=BeanDefinition.ROLE_INFRASTRUCTURE的advisor,也就是ProxyCachingConfiguration往容器中注册的BeanFactoryCacheOperationSourceAdvisor这个bean,并且会通过这个advisor的pointcut去给当前正在初始化的bean进行匹配,判断该bean的类上面或者方法上面是否有缓存注解,如果有缓存注解,那么这个bean就可以被生成代理对象,增强的逻辑就是advisor里面的advice,也就是CacheInterceptor

解析缓存注解

在生成代理对象之前,需要通过advisor的pointcut去判断这个advisor是否适用于当前的bean,判断的依据就是去解析这个bean中是否存在缓存注解,下面我们看下是如何去解析缓存注解的

org.springframework.cache.interceptor.CacheOperationSourcePointcut#matches

public boolean matches(Method method, Class<?> targetClass) {
   CacheOperationSource cas = getCacheOperationSource();
   return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}

在CacheOperationSourcePointcut这个pointcut中,当advisor去进行判断是否适用bean的时候就会调用这个pointcut的matches方法,其中又会去调用了CacheOperationSource的getCacheOperations方法,而这个CacheOperationSource是一个接口,具体就是在ProxyCachingConfiguration配置类中创建的AnnotationCacheOperationSource

org.springframework.cache.interceptor.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;
   }
}

org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource#computeCacheOperations

private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
   // Don't allow no-public methods as required.
   // 必须要是public的方法
   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;
}

AnnotationCacheOperationSource继承了AbstractFallbackCacheOperationSource,在父类AbstractFallbackCacheOperationSource的getCacheOperations中主要就是调用computeCacheOperations方法返回声明的缓存信息并且放在attributeCache这个map缓存中,findCacheOperations是一个抽象方法,它抽象了解析方法中什么样的缓存信息,怎样去解析这些缓存信息,而AnnotationCacheOperationSource这个子类实现的就是去解析缓存注解

org.springframework.cache.annotation.AnnotationCacheOperationSource#findCacheOperations(java.lang.reflect.Method)

protected Collection<CacheOperation> findCacheOperations(Method method) {
   return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}

通过解析器去对method进行解析,这里具体的解析器是SpringCacheAnnotationParser

org.springframework.cache.annotation.SpringCacheAnnotationParser#parseCacheAnnotations(java.lang.reflect.Method)

public Collection<CacheOperation> parseCacheAnnotations(Method method) {
   DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
   return parseCacheAnnotations(defaultConfig, method);
}

上面的方法中会通过method得到该method所属的class然后把这个class作为构造方法参数创建出了DefaultCacheConfig对象,这个对象有什么用?下面会说到

org.springframework.cache.annotation.SpringCacheAnnotationParser#parseCacheAnnotations(org.springframework.cache.annotation.SpringCacheAnnotationParser.DefaultCacheConfig, java.lang.reflect.AnnotatedElement, boolean)

private Collection<CacheOperation> parseCacheAnnotations(
      DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {

   // 找到方法上面所有的缓存注解(@Cacheable,@CacheEvict,@CachePut,@Caching)
   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);
   // 如果有@Cacheable注解,就把注解属性信息封装成CacheableOperation对象
   anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
         ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
   // 如果有@CacheEvict注解,就把注解属性信息封装成CacheEvictOperation对象
   anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
         ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
   // 如果有@CachePut注解,就把注解属性信息封装成CachePutOperation对象
   anns.stream().filter(ann -> ann instanceof CachePut).forEach(
         ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
   // 如果有@Caching注解,就解析@Caching,封装成上面三种对象放到集合中
   anns.stream().filter(ann -> ann instanceof Caching).forEach(
         ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
   return ops;
}

可以看到上面对各种缓存注解都做了不同的解析,我们就看下@Cacheable注解是如何解析的

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

通过构造器模式去从缓存注解中获取对应的属性,最后构造出CacheableOperation对象,这对象里面就封装了缓存注解的信息,其中还调用了一个defaultConfig.applyDefault(builder)方法,我们看下这个方法是干嘛的

private static class DefaultCacheConfig {

   private final Class<?> target;

   @Nullable
   private String[] cacheNames;

   @Nullable
   private String keyGenerator;

   @Nullable
   private String cacheManager;

   @Nullable
   private String cacheResolver;

   private boolean initialized = false;

   public DefaultCacheConfig(Class<?> target) {
      this.target = target;
   }

   /**
    * Apply the defaults to the specified {@link CacheOperation.Builder}.
    * @param builder the operation builder to update
    */
   public void applyDefault(CacheOperation.Builder builder) {
      if (!this.initialized) {
         // 找到方法所在的类上面的@CacheConfig注解,通过解析这个注解去拿到关于缓存组件的一些配置信息
         CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, CacheConfig.class);
         if (annotation != null) {
            // 缓存组件
            this.cacheNames = annotation.cacheNames();
            // key名称生成器
            this.keyGenerator = annotation.keyGenerator();
            // 缓存管理器
            this.cacheManager = annotation.cacheManager();
            // 缓存解析器
            this.cacheResolver = annotation.cacheResolver();
         }
         this.initialized = true;
      }

      // 如果缓存注解中没有声明上面的缓存配置,就使用@CacheConfig注解声明的缓存配置
      if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
         builder.setCacheNames(this.cacheNames);
      }
      if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) &&
            StringUtils.hasText(this.keyGenerator)) {
         builder.setKeyGenerator(this.keyGenerator);
      }

      if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
         // One of these is set so we should not inherit anything
      }
      else if (StringUtils.hasText(this.cacheResolver)) {
         builder.setCacheResolver(this.cacheResolver);
      }
      else if (StringUtils.hasText(this.cacheManager)) {
         builder.setCacheManager(this.cacheManager);
      }
   }
}

这个方法是SpringCacheAnnotationParser的内部类DefaultCacheConfig的一个方法,DefaultCacheConfig这个对象在上面也有提到,在解析缓存注解之前就会传入该方法所属的class作为targetClass创建出一个DefaultCacheConfig对象出来,而在它的applyDefault方法中会去判断这个targetClass上是否存在@CacheConfig注解,如果targetClass上存在@CacheConfig注解,那么当方法上面没有声明其余的缓存注解的时候就会使用@CacheConfig注解上面声明的缓存配置,也就是说如果方法上面声明了缓存注解,优先使用缓存注解作为缓存配置,否则就以@CacheConfig注解作为缓存配置

缓存切面的增强

具体缓存是怎样执行的,那么就是缓存增强器的逻辑了,我们直接看到CacheInterceptor

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

}

直接看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)) {
            // 如果方法上有缓存注解,那么就执行execute方法
            return execute(invoker, method,
                  // 把注解信息,方法,方法参数,目标对象,目标class封装成CacheOperationContexts对象
                  new CacheOperationContexts(operations, method, args, target, targetClass));
         }
      }
   }

   return invoker.invoke();
}

可以看到首先会去解析该方法上面的缓存注解,如果方法上面不存在缓存注解,那么就直接调用走到下一个拦截器,而如果方法存在缓存注解,就会调用另一个重载的execute方法,而在看这个重载的execute方法之前,还创建了一个CacheOperationContexts对象,我们看下这个对象的构造方法

public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
      Object[] args, Object target, Class<?> targetClass) {

   this.contexts = new LinkedMultiValueMap<>(operations.size());
   for (CacheOperation op : operations) {
      // 遍历所有的注解信息对象,把每一个注解信息对象与method, args, target, targetClass封装成一个CacheOperationContext对象
      // 所以contexts中就可以根据不同缓存注解信息对象的class去获取到对应的CacheOperationContext对象
      this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
   }
   this.sync = determineSyncFlag(method);
}

在CacheOperationContexts的构造方法中会去遍历方法上面的所有CacheOperation对象,然后对每一个CacheOperation对象都执行getOperationContext方法,返回一个CacheOperationContext对象,然后再把该CacheOperation对象与它对应产生的CacheOperationContext对象放到contexts这个map中对应缓存起来

protected CacheOperationContext getOperationContext(
      CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {

   CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
   return new CacheOperationContext(metadata, args, target);
}

在创建CacheOperationContext对象之前,会先去创建一个CacheOperationMetadata对象

protected CacheOperationMetadata getCacheOperationMetadata(
      CacheOperation operation, Method method, Class<?> targetClass) {

   CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);
   CacheOperationMetadata metadata = this.metadataCache.get(cacheKey);
   if (metadata == null) {
      KeyGenerator operationKeyGenerator;
      if (StringUtils.hasText(operation.getKeyGenerator())) {
         // 如果指定了key值生成器的名称,那么就根据名称去容器中找到KeyGenerator
         operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
      }
      else {
         // 如果没有指定key值生成器,那么就使用默认的key值生成器---SimpleKeyGeneratorSimpleKeyGenerator
         operationKeyGenerator = getKeyGenerator();
      }
      CacheResolver operationCacheResolver;
      if (StringUtils.hasText(operation.getCacheResolver())) {
         // 如果指定了缓存解析器的名称,那么就根据名称去从容器中找到具体的缓存解析器
         operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
      }
      else if (StringUtils.hasText(operation.getCacheManager())) {
         // 否则如果指定了缓存管理器的名称,那么就根据名称去从容器中找到具体的缓存管理器
         CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
         // 并且使用默认的缓存解析器---SimpleCacheResolver
         operationCacheResolver = new SimpleCacheResolver(cacheManager);
      }
      else {
         // 如果既没指定缓存解析器的名称,也没指定缓存管理器的名称,那么就取默认配置的缓存解析器---SimpleCacheResolver
         //
         operationCacheResolver = getCacheResolver();
         Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
      }
      metadata = new CacheOperationMetadata(operation, method, targetClass,
            operationKeyGenerator, operationCacheResolver);
      this.metadataCache.put(cacheKey, metadata);
   }
   return metadata;
}

可以看到这个方法中主要就是对在缓存注解中声明的缓存配置信息去得到相应的缓存组件,比如说key值生成器,缓存解析器,缓存管理器,这三个组件都可以根据我们在缓存注解中指定的名称从容器中去获取,如果没有指定的话就会用spring默认提供的实现,然后把最终得到的key值生成器,缓存解析器,缓存管理器这三个组件都放到CacheOperationMetadata对象中去,再把CacheOperationCacheKey与这个CacheOperationMetadata对象放到map中缓存起来,下次再调用该方法的时候就不用再去走一遍上面的过程了

创建完了CacheOperationMetadata对象之后就把这个对象作为CacheOperationContext构造方法的参数创建出CacheOperationContext对象,所以在调用exectute方法之前,这个流程应该就是创建一个CacheOperationContexts对象,这个对象里面有一个map分别存着每一个CacheOperation对象以及对应的CacheOperationContext对象,而CacheOperationContext对象中又存着一个CacheOperationMetadata对象,CacheOperationMetadata对象里面存的分别就是key值生成器,缓存解析器,缓存管理器这三个缓存组件

org.springframework.cache.interceptor.CacheAspectSupport#execute(org.springframework.cache.interceptor.CacheOperationInvoker, java.lang.reflect.Method, org.springframework.cache.interceptor.CacheAspectSupport.CacheOperationContexts)

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, 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注解对应的CacheOperationContext对象
   // 如果@CacheEvict注解里面beforeInvocation属性等于true,那么就把缓存清除
   processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
         CacheOperationExpressionEvaluator.NO_RESULT);

   // 从缓存中拿到缓存值
   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<>();
   // 如果缓存中没有值,那么就把context以及key封装成一个CachePutRequest对象放到集合中
   // 该集合会把需要put的东西都准备好
   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();
      // 如果方法返回值是Optional类型并且从缓存中拿出来的值不是Optional类型的,那么就包装为Optional类型的对象返回
      returnValue = wrapCacheValue(method, cacheValue);
   }
   else {
      // 如果缓存没有命中了,那么就执行目标方法得到返回值
      returnValue = invokeOperation(invoker);
      // 如果返回值是Optional类型的,那么就从Optional中拿到真正的结果值作为要缓存的值
      cacheValue = unwrapReturnValue(returnValue);
   }

   // 处理@CachePut注解完善put集合
   collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

   // 把方法返回结果放入缓存,针对两种情况
   // 一是如果@Cacheable注解一进来查询不到缓存
   // 二是@CachePut注解在方法执行完成之后这两种情况,都会最终把方法返回值放到缓存
   for (CachePutRequest cachePutRequest : cachePutRequests) {
      cachePutRequest.apply(cacheValue);
   }

   // 最后如果@CacheEvicts注解的beforeInvocation属性等于false的话就清空缓存
   processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

   return returnValue;
}

上面我们就来到了execute方法,可以看到会先去调用processCacheEvicts方法去处理@CacheEvict注解

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

如果@CacheEvict注解中的beforeInvocation属性设置了true的话,那么就表示每一次进来该方法都需要清除缓存,具体调用了performCacheEvict方法

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

   Object key = null;
   for (Cache cache : context.getCaches()) {
      // 判断是否删除缓存中所有的key-value
      if (operation.isCacheWide()) {
         logInvalidating(context, operation, null);
         // 删除缓存中所有的key-value
         doClear(cache, operation.isBeforeInvocation());
      }
      else {
         if (key == null) {
            key = generateKey(context, result);
         }
         logInvalidating(context, operation, key);
         // 从缓存中删除指定的key
         doEvict(cache, key, operation.isBeforeInvocation());
      }
   }
}

判断@CacheEvict注解中的allEntries属性是否等于true,如果等于true表示需要删除缓存中所有的数据,否则会删除指定key的缓存,最后都会去调用cache的清除缓存方法,那么这些cache是从哪里来的呢?我们跟踪到CacheOperationContext的构造方法

public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
   this.metadata = metadata;
   this.args = extractArgs(metadata.method, args);
   this.target = target;
   this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
   this.cacheNames = createCacheNames(this.caches);
}

可以看到所有的cache都是通过 CacheAspectSupport.this.getCaches(this, metadata.cacheResolver)这句代码初始化的

protected Collection<? extends Cache> getCaches(
      CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) {

   Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);
   if (caches.isEmpty()) {
      throw new IllegalStateException("No cache could be resolved for '" +
            context.getOperation() + "' using resolver '" + cacheResolver +
            "'. At least one cache should be provided per cache operation.");
   }
   return caches;
}

而最终是根据指定的CacheResolver调用resolveCaches返回的,我们通过上面的分析可以知道如果我们没有给spring容器注册一个CacheResolver,那么spring会默认使用SimpleCacheResolver,我们继续来到SimpleCacheResolver的resovlerCache方法

public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
   // 拿到缓存注解中声明的cache名称
   Collection<String> cacheNames = getCacheNames(context);
   if (cacheNames == null) {
      return Collections.emptyList();
   }
   Collection<Cache> result = new ArrayList<>(cacheNames.size());
   for (String cacheName : cacheNames) {
      // 从缓存管理器中根据cache名称去拿到具体的cache组件
      Cache cache = getCacheManager().getCache(cacheName);
      if (cache == null) {
         throw new IllegalArgumentException("Cannot find cache named '" +
               cacheName + "' for " + context.getOperation());
      }
      result.add(cache);
   }
   return result;
}

这个方法里面就是拿到所有我们在缓存注解中声明的cache的名称,然后再通过这个cache名称去从CacheManager中拿到具体的Cache对象(CacheManager对象我们一定要给spring容器提供,不然会报错)

我们回到execute方法,接下来就是调用findCachedItem方法处理@Cacheable注解了

private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
   Object result = CacheOperationExpressionEvaluator.NO_RESULT;
   for (CacheOperationContext context : contexts) {
      // 判断condition条件是否成立,如果不成立直接返回
      if (isConditionPassing(context, result)) {
         // 生成key值
         Object key = generateKey(context, result);
         // 根据key值从缓存中找到对应的value值
         Cache.ValueWrapper cached = findInCaches(context, key);
         if (cached != null) {
            return cached;
         }
         else {
            if (logger.isTraceEnabled()) {
               logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
            }
         }
      }
   }
   return null;
}
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
   // 遍历所有的cache缓存组件
   for (Cache cache : context.getCaches()) {
      // 返回value
      Cache.ValueWrapper wrapper = doGet(cache, key);
      // 如果能够从一个cache组件中得到缓存值,那么就直接返回出去了
      if (wrapper != null) {
         if (logger.isTraceEnabled()) {
            logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
         }
         return wrapper;
      }
   }
   return null;
}
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
   try {
      return cache.get(key);
   }
   catch (RuntimeException ex) {
      getErrorHandler().handleCacheGetError(ex, cache, key);
      return null;  // If the exception is handled, return a cache miss
   }
}

上面的代码也很简单,就是首先会去通过condition属性判断下这个key是否需要被忽略,如果不被忽略的话就继续执行下面的代码,下面的代码会先通过key值生成器得到key值,然后通过这个这个key值去从cache对象中查询得到缓存

collectPutRequests(contexts.get(CacheableOperation.class),
      CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);

然后再去把需要执行的缓存请求调用collectPutRequests方法放到一个集合中,因为@Cacheable注解的作用是先从缓存中查询,如果缓存没有的话那么执行完目标方法把返回值放到缓存中,所以就需要放一个缓存请求作为标识,等执行完目标方法之后,就可以直接根据这个缓存请求去把方法返回结果放到缓存中

if (cacheHit != null && !hasCachePut(contexts)) {
   // If there are no put requests, just use the cache hit
   // 如果缓存命中了,后面直接返回缓存值
   cacheValue = cacheHit.get();
   // 如果方法返回值是Optional类型并且从缓存中拿出来的值不是Optional类型的,那么就包装为Optional类型的对象返回
   returnValue = wrapCacheValue(method, cacheValue);
}
else {
   // 如果缓存没有命中了,那么就执行目标方法得到返回值
   returnValue = invokeOperation(invoker);
   // 如果返回值是Optional类型的,那么就从Optional中拿到真正的结果值作为要缓存的值
   cacheValue = unwrapReturnValue(returnValue);
}

然后接着就是去判断缓存是否命中了,如果没有命中的话就需要执行目标方法得到返回值

// 处理@CachePut注解完善put集合
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

然后再去处理@CachePut注解,把缓存请求放到集合中

// 把方法返回结果放入缓存,针对两种情况
// 一是如果@Cacheable注解一进来查询不到缓存
// 二是@CachePut注解在方法执行完成之后
// 这两种情况,都会最终把方法返回值放到缓存
for (CachePutRequest cachePutRequest : cachePutRequests) {
   cachePutRequest.apply(cacheValue);
}
public void apply(@Nullable Object result) {
   // 判断该返回值是否符合unless条件,如果符合就放到缓存
   if (this.context.canPutToCache(result)) {
      for (Cache cache : this.context.getCaches()) {
         // 把方法返回结果放到缓存
         doPut(cache, this.key, result);
      }
   }
}

然后最终就会针对缓存请求集合最终把数据放到缓存中,而此时我们可以知道这个缓存请求结合存放的缓存请求是可以来自两方面的,一个是@Cacheable注解进来查询不到缓存的时候会创建一个缓存请求,二是@CachePut注解创建的缓存请求

// 最后如果@CacheEvicts注解的beforeInvocation属性等于false的话就清空缓存
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

然后最后还是调用了processCacheEvicts这个方法,不过这一次是针对当@CacheEvict注解的beforeInvocation属性等于false的时候,表示在目标方法执行完成之后才会清除缓存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的Spring切面的Java代码示例: ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice() { System.out.println("Before advice executed!"); } } ``` 在上述示例中,我们定义了一个名为`LoggingAspect`的切面类。通过使用`@Aspect`注解和`@Component`注解,我们将该类声明为一个切面,并使其成为Spring的bean。 `@Before`注解用于定义前置通知。在示例中,我们将前置通知应用于`com.example.service`包中的所有方法。这里使用了AspectJ表达式`execution(* com.example.service.*.*(..))`来指定切点。该表达式指定了所有返回类型任意、位于`com.example.service`包中、类名任意、方法名任意、参数任意的方法。 在前置通知方法`beforeAdvice()`中,我们可以执行任何希望在目标方法执行之前执行的操作。在此示例中,我们简单地打印出一条消息。 要使上述切面生效,需要在Spring配置文件中进行配置。例如,在XML配置中,可以添加以下内容: ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example.aspect"/> ``` 其中,第一行启用了Spring的自动代理机制,以便自动检测和应用切面。第二行指定了切面类所在的包。 这样,在应用程序运行时,当`com.example.service`包中的任何方法被调用时,切面中的前置通知方法将在目标方法执行之前被调用,并打印出相应的消息。 请注意,此示例仅用于演示Spring切面的基本用法。在实际应用中,您可能需要根据需求编写更复杂的切面,并添加其他类型的通知(如后置通知、环绕通知等)来实现更丰富的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值