当要使用 @Cacheable 注解时需要引入 @EnableCaching 注解开启缓存功能。为什么呢?现在就来看看为什么要加入 @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 注解来导入一个类 CachingConfigurationSelector,猜测下,这个类肯定是一个入口类,也可以说是个触发类。注意此处 mode() 默认是 AdviceMode.PROXY
。
进入 CachingConfigurationSelector 类,源码如下:
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);
}
}
从 getProxyImports() 方法可知导入两个类:AutoProxyRegistrar 类、ProxyCachingConfiguration 类。那么接下来就重点分析这两个类是用来干啥的?
看到 XxxRegistrar 就可以确定要注册一个类。进入核心源码如下:
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);
}
}
很明显在这注册了一个 InfrastructureAdvisorAutoProxyCreator 类,这个类和事务切面入口类就是同一个。而这个类并不会做什么逻辑,所有的逻辑都在其父类 AbstractAutoProxyCreator 抽象类中,该类仅仅充当一个入口类。
AbstractAutoProxyCreator 类又是一个 BeanPostProcessor 接口的应用。所以关注这个接口的两个方法。这里只需要关注第二个方法 postProcessAfterInitialization(),源码如下:
@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;
}
很明显在 AbstractAutoProxyCreator 抽象类中会调用 wrapIfNecessary() 方法去判断对当前 bean 是否需要创建代理对象。那么这里根据什么来判断呢?进入该方法,核心源码如下:
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;
}
在上面这段逻辑,很明显 getAdvicesAndAdvisorsForBean() 方法就是判断依据。如果有值就需要创建代理,反之不需要。那么重点关注该方法内部逻辑,核心源码如下:
@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();
}
继续进入 findEligibleAdvisors() 内部逻辑,核心源码如下:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
看到 findCandidateAdvisors() 方法,内部逻辑如下:
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;
}
从上述代码中可以得知,Spring 通过调用 beanNamesForTypeIncludingAncestors(Advisor.class) 方法来获取所有 Spring 容器中实现 Advsior 接口的实现类。
那么现在先看到 ProxyCachingConfiguration 类,源码如下:
@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;
}
// 先省略一部分...
}
很明显 BeanFactoryCacheOperationSourceAdvisor 实现了 Advisor 接口。那么在上面调用 beanNamesForTypeIncludingAncestors(Advisor.class) 方法时就可以获取到该实例。该实例就会被提添加到候选集合 advisors 中返回。
okay,看完 findCandidateAdvisors() 方法, 再看到 findAdvisorsThatCanApply() 方法,前一个方法获取到了 Spring 容器中所有实现了 Advisor 接口的实现。然后再调用 findAdvisorsThatCanApply() 方法,去判断有哪些 Advisor 适用于当前 bean。进入 findAdvisorsThatCanApply() 内部逻辑,核心源码如下:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
/** 没有切面,匹配个屁 */
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// tc_tag-96: 开始 for 循环 candidateAdvisors 每个增强器,看是否能使用与这个 bean
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
继续进入 canApply() 核心逻辑如下:
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;
}
从上面源码中放眼过去,就能看到两个熟悉的老朋友:ClassFilter 类、MethodMatcher 类,ClassFilter 类是用来判断当前 bean 所在的 Class 上面是否有标注 @Caching、@Cacheable、@CachePut、@CacheEvict 注解。MethodMatcher 类是用来判断当前 bean 的方法上是否有标注 @Caching、@Cacheable、@CachePut、@CacheEvict 注解。
那么这两个过滤器对象是在哪里创建的呢?
回到 ProxyCachingConfiguration 类,源码如下:
@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;
}
}
看到 BeanFactoryCacheOperationSourceAdvisor 类,我们知道一个 Advisor 必然是由 Advice 和 Pointcut 组成的。
而 Pointcut 肯定是由 ClassFilter 和 MethodMatcher 组成的。进入该类,源码如下:
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;
}
}
关注到 CacheOperationSourcePointcut 类,进入源码如下:
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));
}
}
}
从源码中可以看出 ClassFilter = CacheOperationSourceClassFilter,MethodMatcher = CacheOperationSourcePointcut。这里 ClassFilter 的 matches() 方法不对类进行过滤。这个类过滤放到了 MethodMatcher 的 matches() 方法中,和对方法的过滤集成在一起。
所以这里关注到 MethodMatcher#matches() 匹配方法,看看它是怎么进行匹配的。核心源码如下:
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)));
}
}
进入 getCacheOperations() 方法,核心源码如下:
@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;
}
}
发现这段代码和 @Transactional 注解匹配一模一样,其实就是用的同一套逻辑,只不过注解不同而已。匹配过程是一模一样的,看到 computeCacheOperations() 方法,核心源码如下:
@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;
}
从上面源码可以看到匹配逻辑,会去匹配当前这个 bean 的方法上是否有 @Cacheable 等注解。方法没找到,就找当前 bean 所在的类上,在没找到就找接口方法,在没找到,继续找接口类上,还灭找到直接返回 null。这个就是他的 MethodMatcher#matches() 匹配过程。
所以 canApply() 方法调用的底层就是调用 MethodMatcher 去进行匹配。findAdvisorsThatCanApply() 方法调用的就是 canApply(),就是这样一个匹配过程。如果能匹配成功,表示 Advisor 可以用于加强当前 bean。那么就需要去调用 createProxy() 方法创建代理对象。让代理对象去调用 Advisor 里面的增强逻辑,执行完之后再调用目标方法。返回到上层调用 getAdvicesAndAdvisorsForBean(),源码如下:
@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();
}
返回到最上层调用 wrapIfNecessary() ,源码如下:
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;
}
最后调用 createProxy() 创建代理对象。至此在 Spring 启动时创建代理对象环节完成。接下来要分析调用流程。
对于该类就是提供缓存需要的支持类。比如 Advisor,通过 @Bean 方式提前注册到 Spring 容器中。源码如下:
@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;
}
}
这里只需要关注 Advisor。而 CacheOperationSource 对象只是对 @Cacheable 等注解解析之后的属性封装而已。
Advisor = Advice + Pointcut
Advisor = BeanFactoryCacheOperationSourceAdvisor
Advice = CacheInterceptor
Pointcut = CacheOperationSourcePointcut
Pointcut = ClassFilter + MethodMatcher
ClassFilter = CacheOperationSourceClassFilter
MethodMatcher = CacheOperationSourcePointcut
调用流程请跳转到下篇 Spring 之 @Cacheable 源码解析(下)
最终你会发现 @EnableCaching 注解和 @EnableTransactionManagement 基本一模一样,只是换了个注解,把 @Transactional 换成了 @Cacheable 等注解。