Análisis del código fuente @Cacheable de Spring (Parte 1)

1. Análisis del código fuente de @EnableCaching

Cuando desee utilizar la anotación @Cacheable, debe introducir la anotación @EnableCaching para habilitar la función de almacenamiento en caché. ¿por qué? Ahora echemos un vistazo a por qué necesitamos agregar la anotación @EnableCaching para habilitar el aspecto de almacenamiento en caché. El código fuente es el siguiente:

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

Se puede ver que una clase CachingConfigurationSelector se importa a través de la anotación @Import, supongo que esta clase debe ser una clase de entrada o una clase de activación. Tenga en cuenta que mode() aquí tiene como valor predeterminado AdviceMode.PROXY.

Ingrese a la clase CachingConfigurationSelector, el código fuente es el siguiente:

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
    
    

	@Override
	public String[] selectImports(AdviceMode adviceMode) {
    
    
		switch (adviceMode) {
    
    
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}
	
	private String[] getProxyImports() {
    
    
		List<String> result = new ArrayList<>(3);
		result.add(AutoProxyRegistrar.class.getName());
		result.add(ProxyCachingConfiguration.class.getName());
		if (jsr107Present && jcacheImplPresent) {
    
    
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		return StringUtils.toStringArray(result);
	}
}

Desde el método getProxyImports(), podemos saber que se importan dos clases: la clase AutoProxyRegistrar y la clase ProxyCachingConfiguration . Entonces centrémonos en analizar ¿para qué se utilizan estas dos clases?

1、AutoProxyRegistrar

Puede estar seguro de registrar una clase consultando XxxRegistrar. Ingrese el código fuente principal de la siguiente manera:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    
    

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    
		AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
	}

	@Nullable
	public static BeanDefinition registerAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    
		// 缓存和事物都是通过这个 InfrastructureAdvisorAutoProxyCreator 基础增强类来生成代理对象
		return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
	}
}

Obviamente, aquí se registra una clase InfrastructureAdvisorAutoProxyCreator, que es la misma que la clase de entrada del aspecto de transacción. Esta clase no tiene ninguna lógica. Toda la lógica está en su clase principal, AbstractAutoProxyCreator, que solo actúa como una clase de entrada.

La clase AbstractAutoProxyCreator es otra aplicación de la interfaz BeanPostProcessor . Así que concéntrese en los dos métodos de esta interfaz. Aquí solo debes prestar atención al segundo método postProcessAfterInitialization(), cuyo código fuente es el siguiente:

	@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    
    
		if (bean != null) {
    
    
			/** 获取缓存 key */
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    
    
				/** 是否有必要创建代理对象 */
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

Obviamente, se llamará al método wrapIfNecessary() en la clase abstracta AbstractAutoProxyCreator para determinar si es necesario crear un objeto proxy para el bean actual. Entonces, ¿cuál es la base para juzgar aquí? Al ingresar a este método, el código fuente principal es el siguiente:

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    
    

		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

		 /** 合适的通知不为空 */
		if (specificInterceptors != DO_NOT_PROXY) {
    
    
		
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			return proxy;
		}
		
		return bean;
	}

En la lógica anterior, es obvio que el método getAdvicesAndAdvisorsForBean() es la base para el juicio. Si hay un valor, debe crear un proxy; de lo contrario, no es necesario. Luego concéntrese en la lógica interna del método: el código fuente principal es el siguiente:

	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
    

		/** tc_tag-99: 查找适合这个类的 advisors 切面通知 */
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
    
    
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

Continúe ingresando a la lógica interna de findEligibleAdvisors (). El código fuente principal es el siguiente:

	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    
    

		List<Advisor> candidateAdvisors = findCandidateAdvisors();

		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

		extendAdvisors(eligibleAdvisors);

		if (!eligibleAdvisors.isEmpty()) {
    
    
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

Consulte el método findCandidateAdvisors(), la lógica interna es la siguiente:

	protected List<Advisor> findCandidateAdvisors() {
    
    
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}
	
	public List<Advisor> findAdvisorBeans() {
    
    
		String[] advisorNames = this.cachedAdvisorBeanNames;
		if (advisorNames == null) {
    
    
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		}
		
		List<Advisor> advisors = new ArrayList<>();

		for (String name : advisorNames) {
    
    
			if (isEligibleBean(name)) {
    
    
				advisors.add(this.beanFactory.getBean(name, Advisor.class));
			}
		}
		return advisors;
	}

Como se puede ver en el código anterior, Spring obtiene todas las clases de implementación que implementan la interfaz Advsior en el contenedor Spring llamando al método beanNamesForTypeIncludeAncestors (Advisor.class).

Ahora veamos la clase ProxyCachingConfiguration, el código fuente es el siguiente:

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

	// cache_tag: 缓存方法增强器
	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
			CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
    
    

		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		// 缓存用来解析一些属性封装的对象 CacheOperationSource
		advisor.setCacheOperationSource(cacheOperationSource);
		// 缓存拦截器执行对象
		advisor.setAdvice(cacheInterceptor);
		if (this.enableCaching != null) {
    
    
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}
	// 先省略一部分...
}

Es obvio que BeanFactoryCacheOperationSourceAdvisor implementa la interfaz Advisor. Luego puede obtener la instancia cuando llama al método beanNamesForTypeIncludeAncestors(Advisor.class) anterior. La instancia se agregará a los asesores de colección de candidatos y se devolverá.

Bien, después de leer el método findCandidateAdvisors() y luego observar el método findAdvisorsThatCanApply(), el método anterior obtiene todas las implementaciones de la interfaz Advisor en el contenedor Spring. Luego llame al método findAdvisorsThatCanApply() para determinar qué asesores son aplicables al bean actual. Ingrese la lógica interna de findAdvisorsThatCanApply (), el código fuente principal es el siguiente:

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    
    

		/** 没有切面,匹配个屁 */
		if (candidateAdvisors.isEmpty()) {
    
    
			return candidateAdvisors;
		}

		List<Advisor> eligibleAdvisors = new ArrayList<>();
		for (Advisor candidate : candidateAdvisors) {
    
    
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
    
    
				eligibleAdvisors.add(candidate);
			}
		}
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {
    
    
			if (candidate instanceof IntroductionAdvisor) {
    
    
				// already processed
				continue;
			}
			// tc_tag-96: 开始 for 循环 candidateAdvisors 每个增强器,看是否能使用与这个 bean
			if (canApply(candidate, clazz, hasIntroductions)) {
    
    
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

Continúe ingresando canApply (), la lógica central es la siguiente:

	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    
    

		/**
		 * 通过 Pointcut 获取到 ClassFilter 类的匹配器
		 * 然后匹配 targetClass 是否在 Pointcut 配置的包路径下面么?具体实现看 AspectJExpressionPointcut
		 */
		if (!pc.getClassFilter().matches(targetClass)) {
    
    
			return false;
		}
		/**
		 * 通过 Pointcut 获取到 MethodMatcher 类的匹配器
		 * 然后判断这个类下面的方法是否在 Pointcut 配置的包路径下面么?
		 * 或者是这个方法上是否标注了 @Transactional、@Cacheable等注解呢?
		 */
		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
    
    
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
    
    
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
    
    
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
		for (Class<?> clazz : classes) {
    
    
			/**
			 * tc_tag1: 获取这个 targetClass 类下所有的方法,开始挨个遍历是否满 Pointcut 配置的包路径下面么?
			 * 或者是这个方法上是否标注了 @Transactional、@Cacheable等注解呢?
			 */
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
    
    
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						/**
						 * tc_tag2: 注意对于 @Transactional 注解的 Pointcut 匹配还是比较复杂的,匹配逻辑在 TransactionAttributeSourcePointcut
						 */
						methodMatcher.matches(method, targetClass)) {
    
    
					return true;
				}
			}
		}

		return false;
	}

Mirando el código fuente anterior, puede ver dos viejos amigos familiares: la clase ClassFilter y la clase MethodMatcher. La clase ClassFilter se usa para determinar si la clase donde se encuentra el bean actual está marcada con @Caching, @Cacheable , @CachePut . y anotación @CacheEvict . _ La clase MethodMatcher se utiliza para determinar si el método de bean actual está anotado con las anotaciones @Caching , @Cacheable , @CachePut y @CacheEvict .

Entonces, ¿dónde se crean estos dos objetos de filtro?

Volviendo a la clase ProxyCachingConfiguration, el código fuente es el siguiente:

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

	// cache_tag: 缓存方法增强器
	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
			CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
    
    

		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		// 缓存用来解析一些属性封装的对象 CacheOperationSource
		advisor.setCacheOperationSource(cacheOperationSource);
		// 缓存拦截器执行对象
		advisor.setAdvice(cacheInterceptor);
		if (this.enableCaching != null) {
    
    
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}
}

Consulte la clase BeanFactoryCacheOperationSourceAdvisor,Sabemos que un Asesor debe estar compuesto por Advice y Pointcut.
Pointcut debe estar compuesto por ClassFilter y MethodMatcher
. Ingrese a esta clase, el código fuente es el siguiente:

public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    
    

	@Nullable
	private CacheOperationSource cacheOperationSource;

	private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
    
    

		@Nullable
		protected CacheOperationSource getCacheOperationSource() {
    
    
			return cacheOperationSource;
		}
	};

	public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
    
    
		this.cacheOperationSource = cacheOperationSource;
	}

	public void setClassFilter(ClassFilter classFilter) {
    
    
		this.pointcut.setClassFilter(classFilter);
	}

	@Override
	public Pointcut getPointcut() {
    
    
		return this.pointcut;
	}
}

Preste atención a la clase CacheOperationSourcePointcut e ingrese el código fuente de la siguiente manera:

abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    
    

	protected CacheOperationSourcePointcut() {
    
    
		setClassFilter(new CacheOperationSourceClassFilter());
	}

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

	@Nullable
	protected abstract CacheOperationSource getCacheOperationSource();

	private class CacheOperationSourceClassFilter implements ClassFilter {
    
    

		@Override
		public boolean matches(Class<?> clazz) {
    
    
			if (CacheManager.class.isAssignableFrom(clazz)) {
    
    
				return false;
			}
			CacheOperationSource cas = getCacheOperationSource();
			return (cas == null || cas.isCandidateClass(clazz));
		}
	}
}

Se puede ver en el código fuente que ClassFilter = CacheOperationSourceClassFilter, MethodMatcher = CacheOperationSourcePointcut. El método match() de ClassFilter aquí no filtra clases. Este filtrado de clases se coloca en el métodomatches() de MethodMatcher y está integrado con el filtrado de métodos.

Así que aquí nos centramos en el método de coincidencia MethodMatcher#matches() para ver cómo coincide. El código fuente principal es el siguiente:

abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    
    

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

Ingrese el método getCacheOperations (), el código fuente principal es el siguiente:

	@Override
	@Nullable
	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 {
    
    
			// cache_tag: 计算缓存注解上面的配置的值,然后封装成 CacheOperation 缓存属性对象,基本和事物的一样
			// 注意每个缓存注解对应一种不同的解析处理
			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;
		}
	}

Descubrí que este código coincide exactamente con la anotación @Transactional. De hecho, utiliza el mismo conjunto de lógica, pero las anotaciones son diferentes. El proceso de coincidencia es exactamente el mismo. Consulte el método ComputeCacheOperations (). El código fuente principal es el siguiente:

	@Nullable
	private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
    
    
		if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    
    
			return null;
		}

		Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

		Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
		if (opDef != null) {
    
    
			return opDef;
		}

		opDef = findCacheOperations(specificMethod.getDeclaringClass());
		if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
    
    
			return opDef;
		}

		if (specificMethod != 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;
	}
	
	@Nullable
	private Collection<CacheOperation> parseCacheAnnotations(
			DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
    
    

		Collection<? extends Annotation> anns = (localOnly ?
				AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
				AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
		if (anns.isEmpty()) {
    
    
			return null;
		}
		// cache_tag: 熟悉不能再熟悉的缓存注解 Cacheable/CacheEvict/CachePut/Caching
		// 注意每一种类型的注解解析是不太一样的哦,具体看 parseCacheableAnnotation() 解析方法
		final Collection<CacheOperation> ops = new ArrayList<>(1);
		anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
				ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
		anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
				ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
		anns.stream().filter(ann -> ann instanceof CachePut).forEach(
				ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
		anns.stream().filter(ann -> ann instanceof Caching).forEach(
				ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
		return ops;
	}

En el código fuente anterior, puede ver que la lógica de coincidencia coincidirá si hay anotaciones como @Cacheable en el método bean actual. Si no se encuentra el método, buscará la clase donde se encuentra el bean actual, si no lo encuentra, buscará el método de interfaz, si no lo encuentra, continuará buscando la clase de interfaz. Si no se encuentra, devolverá nulo directamente. Este es su proceso de coincidencia MethodMatcher#matches().

Entonces, la capa inferior de la llamada al método canApply() es llamar a MethodMatcher para realizar la coincidencia. El método findAdvisorsThatCanApply () llama a canApply (), que es un proceso de coincidencia. Si la coincidencia es exitosa, significa que el Asesor se puede utilizar para mejorar el bean actual. Luego necesitas llamar al método createProxy() para crear un objeto proxy. Deje que el objeto proxy llame a la lógica mejorada en el Asesor y luego llame al método de destino después de la ejecución. Regrese a la capa superior y llame a getAdvicesAndAdvisorsForBean (), el código fuente es el siguiente:

	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
    

		/** tc_tag-99: 查找适合这个类的 advisors 切面通知 */
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
    
    
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

Regrese a la capa superior y llame a wrapIfNecessary (), el código fuente es el siguiente:

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    
    

		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

		 /** 合适的通知不为空 */
		if (specificInterceptors != DO_NOT_PROXY) {
    
    
		
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			return proxy;
		}
		
		return bean;
	}

Finalmente, se llama a createProxy() para crear el objeto proxy. En este punto, se completa la creación del objeto proxy durante el inicio de Spring. A continuación, debemos analizar el proceso de llamada.

2、Configuración de ProxyCaching

Esta clase proporciona la clase de soporte necesaria para el almacenamiento en caché. Por ejemplo, Advisor se registra en el contenedor Spring por adelantado mediante el método @Bean. El código fuente es el siguiente:

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

	// cache_tag: 缓存方法增强器
	@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
			CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
    
    

		BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
		// 缓存用来解析一些属性封装的对象 CacheOperationSource
		advisor.setCacheOperationSource(cacheOperationSource);
		// 缓存拦截器执行对象
		advisor.setAdvice(cacheInterceptor);
		if (this.enableCaching != null) {
    
    
			advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
		}
		return advisor;
	}

	// cache_tag: Cache 注解解析器
	@Bean
	@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
	public CacheOperationSource cacheOperationSource() {
    
    
		return new AnnotationCacheOperationSource();
	}

	// cache_tag: 缓存拦截器执行器
	@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;
	}

}

Aquí solo necesitas prestar atención al Asesor. El objeto CacheOperationSource es solo una encapsulación de propiedades de @Cacheable y otras anotaciones después de analizarlas.

Asesor = Consejo + Pointcut

Asesor = BeanFactoryCacheOperationSourceAdvisor

Consejo = CacheInterceptor

Pointcut = CacheOperationSourcePointcut

Punto de corte = ClassFilter + MethodMatcher

ClassFilter = CacheOperationSourceClassFilter

MethodMatcher = CacheOperationSourcePointcut

Para conocer el proceso de llamada, vaya al siguiente artículo Análisis del código fuente de @Cacheable de Spring (Parte 2)

Con el tiempo, encontrará que la anotación @EnableCaching es básicamente la misma que @EnableTransactionManagement, excepto que la anotación se cambia, reemplazando @Transactional con @Cacheable y otras anotaciones.

Supongo que te gusta

Origin blog.csdn.net/qq_35971258/article/details/128727311
Recomendado
Clasificación