SpringCache源码解析(一)

一、springCache如何实现自动装配

SpringBoot 确实是通过 spring.factories 文件实现自动配置的。Spring Cache 也是遵循这一机制来实现自动装配的。

具体来说,Spring Cache 的自动装配是通过 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration 这个类来实现的。这个类位于 spring-boot-autoconfigure 包下。

在 spring-boot-autoconfigure 包的 META-INF/spring.factories 文件中,可以找到 CacheAutoConfiguration 类的配置:
在这里插入图片描述

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

二、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 {

 /**
     * 创建CacheManagerCustomizers Bean
     * XxxCacheConfiguration中创建CacheManager时会用来装饰CacheManager
     */
	@Bean
	@ConditionalOnMissingBean
	public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) {
		return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
	}

/**
     * 创建CacheManagerValidator Bean
     * 实现了InitializingBean,在afterPropertiesSet方法中校验CacheManager
     */
	@Bean
	public CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties,
			ObjectProvider<CacheManager> cacheManager) {
		return new CacheManagerValidator(cacheProperties, cacheManager);
	}

 /**
     * 后置处理器
     * 内部类,动态声明EntityManagerFactory实例需要依赖"cacheManager"实例
     */
	@Configuration
	@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
	@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
	protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor {

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

	}

	/**
     * 缓存管理器校验器
     * 内部类,实现了InitializingBean接口,实现afterPropertiesSet用于校验缓存管理器
     */
	static class CacheManagerValidator implements InitializingBean {

		private final CacheProperties cacheProperties;

		private final ObjectProvider<CacheManager> cacheManager;

		CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) {
			this.cacheProperties = cacheProperties;
			this.cacheManager = cacheManager;
		}

 /**
         * 当依赖注入后处理
         */
		@Override
		public void afterPropertiesSet() {
			Assert.notNull(this.cacheManager.getIfAvailable(),
					() -> "No cache manager could " + "be auto-configured, check your configuration (caching "
							+ "type is '" + this.cacheProperties.getType() + "')");
		}

	}

	/**
     * 缓存配置导入选择器
     */
	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;
		}

	}

}

借助Conditional机制实现自动配置条件:

  1. CacheManager.class在类路径上存在;
  2. CacheAspectSupport实例存在(即CacheInterceptor);
  3. CacheManager实例不存在,且cacheResolver Bean不存在;
    当满足以上要求时,就会触发SpringCache的自动配置逻辑,CacheAutoConfiguration会引入其它的Bean,具体如下:
  4. 通过@EnableConfigurationProperties使CacheProperties生效;
  5. 借助Import机制导入内部类:CacheConfigurationImportSelector和CacheManagerEntityManagerFactoryDependsOnPostProcessor;
  6. 创建了CacheManagerCustomizers、CacheManagerValidator Bean;

2.1 关于的一些疑问

关于@ConditionalOnClass,我有一个疑问:

  • value值是类名,如果类找不到,会不会编译报错;
    ——会! 那value有什么意义?只要能编译过的,肯定都存在。
    ——@ConditionalOnClass注解之前一直让我感到困惑,类存不存在,编译器就会体现出来,还要这个注解干嘛?后来想了很久,感觉java语法并不是一定要寄生在idea上,所以语法上限制和工具上限制,这是两码事,理论上就应该彼此都要去做。

@ConditionalOnClass还可以用name属性:

@ConditionalOnClass(name="com.xxx.Component2")

可以看下这篇文章:
SpringBoot中的@ConditionOnClass注解

2.2 CacheProperties

// 接收以"spring.cache"为前缀的配置参数
@ConfigurationProperties(prefix = "spring.cache")
public class CacheProperties {

    // 缓存实现类型
    // 未指定,则由环境自动检测,从CacheConfigurations.MAPPINGS自上而下加载XxxCacheConfiguration
    // 加载成功则检测到缓存实现类型
    private CacheType type;

    // 缓存名称集合
    // 通常,该参数配置了则不再动态创建缓存
    private List<String> cacheNames = new ArrayList<>();

    private final Caffeine caffeine = new Caffeine();

    private final Couchbase couchbase = new Couchbase();

    private final EhCache ehcache = new EhCache();

    private final Infinispan infinispan = new Infinispan();

    private final JCache jcache = new JCache();

    private final Redis redis = new Redis();


    // getter and setter
    ...

    /**
     * Caffeine的缓存配置参数
     */
    public static class Caffeine {
        ...
    }

    /**
     * Couchbase的缓存配置参数
     */
    public static class Couchbase {
        ...
    }

    /**
     * EhCache的缓存配置参数
     */
    public static class EhCache {
        ...
    }

    /**
     * Infinispan的缓存配置参数
     */
    public static class Infinispan {
        ...
    }

    /**
     * JCache的缓存配置参数
     */
    public static class JCache {
        ...
    }

    /**
     * Redis的缓存配置参数
     */
    public static class Redis {

        // 过期时间,默认永不过期
        private Duration timeToLive;

        // 支持缓存空值标识,默认支持
        private boolean cacheNullValues = true;

        // 缓存KEY前缀
        private String keyPrefix;

        // 使用缓存KEY前缀标识,默认使用
        private boolean useKeyPrefix = true;

        // 启用缓存统计标识
        private boolean enableStatistics;

        // getter and setter
        ...
    }
}

2.3 CacheConfigurationImportSelector

CacheAutoConfiguration的静态内部类,实现了ImportSelector接口的selectImports方法,导入Cache配置类;

/**
 * 挑选出CacheType对应的配置类
 */
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    CacheType[] types = CacheType.values();
    String[] imports = new String[types.length];
    // 遍历CacheType
    for (int i = 0; i < types.length; i++) {
        // 获取CacheType的配置类
        imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
    }
    return imports;
}

CacheConfigurationImportSelector,把所有缓存的配置类拿出来,加入到spring中被加载。

有的开发者可能就会问:
为什么要重载所有的配置类?而不是配置了哪种缓存类型就加载哪种缓存类型?
——这里只是加载所有的配置类,比如Redis,只是加载RedisCacheConfiguration.class配置类,后续会根据条件判断具体加载到哪个配置,如下图:
在这里插入图片描述

2.4 CacheConfigurations

维护了CacheType和XxxCacheConfiguration配置类的映射关系;

/**
 * CacheType和配置类的映射关系集合
 * 无任何配置条件下,从上而下,默认生效为SimpleCacheConfiguration
 */
static {
    Map<CacheType, String> mappings = new EnumMap<>(CacheType.class);
    mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class.getName());
    mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class.getName());
    mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class.getName());
    mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class.getName());
    mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class.getName());
    mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class.getName());
    mappings.put(CacheType.REDIS, RedisCacheConfiguration.class.getName());
    mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class.getName());
    mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName());
    mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName());
    MAPPINGS = Collections.unmodifiableMap(mappings);
}

/**
 * 根据CacheType获取配置类
 */
static String getConfigurationClass(CacheType cacheType) {
    String configurationClassName = MAPPINGS.get(cacheType);
    Assert.state(configurationClassName != null, () -> "Unknown cache type " + cacheType);
    return configurationClassName;
}

/**
 * 根据配置类获取CacheType
 */
static CacheType getType(String configurationClassName) {
    for (Map.Entry<CacheType, String> entry : MAPPINGS.entrySet()) {
        if (entry.getValue().equals(configurationClassName)) {
            return entry.getKey();
        }
    }
    throw new IllegalStateException("Unknown configuration class " + configurationClassName);
}

2.5 CacheType

缓存类型的枚举,按照优先级定义;

GENERIC     // Generic caching using 'Cache' beans from the context.
JCACHE      // JCache (JSR-107) backed caching.
EHCACHE     // EhCache backed caching.
HAZELCAST   // Hazelcast backed caching.
INFINISPAN  // Infinispan backed caching.
COUCHBASE   // Couchbase backed caching.
REDIS       // Redis backed caching.
CAFFEINE    // Caffeine backed caching.
SIMPLE      // Simple in-memory caching.
NONE        // No caching.

2.4 CacheCondition

它是所有缓存配置使用的通用配置条件;

2.4.1 准备工作

介绍之前先看一些CacheCondition类使用的地方,可以看到是各种缓存类型类型的配置类使用了它。
在这里插入图片描述

@Conditional注解的作用是什么?
——请看这篇文章@Conditional 注解有什么用?

说穿了,就是根据注解参数Condition实现类(这里是CacheCondition类)的matches方法返回,来判断是否加载这个配置类。

2.4.2 CacheCondition核心代码

/**
 * 匹配逻辑
 */
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    String sourceClass = "";
    if (metadata instanceof ClassMetadata) {
        sourceClass = ((ClassMetadata) metadata).getClassName();
    }
    ConditionMessage.Builder message = ConditionMessage.forCondition("Cache", sourceClass);
    Environment environment = context.getEnvironment();
    try {
        // 获取配置文件中指定的CacheType
        BindResult<CacheType> specified = Binder.get(environment).bind("spring.cache.type", CacheType.class);
        if (!specified.isBound()) {
            // 不存在则根据CacheConfiguration.mappings自上而下匹配合适的CacheConfiguration
            return ConditionOutcome.match(message.because("automatic cache type"));
        }
        CacheType required = CacheConfigurations.getType(((AnnotationMetadata) metadata).getClassName());
        // 存在则比较CacheType是否一致
        if (specified.get() == required) {
            return ConditionOutcome.match(message.because(specified.get() + " cache type"));
        }
    }
    catch (BindException ex) {
    }
    return ConditionOutcome.noMatch(message.because("unknown cache type"));
}

三、常见的CacheConfiguration

3.1 RedisCacheConfiguration

它是Redis缓存配置;

// Bean方法不被代理
@Configuration(proxyBeanMethods = false)
// RedisConnectionFactory在类路径上存在
@ConditionalOnClass(RedisConnectionFactory.class)
// 在RedisAutoConfiguration之后自动配置
@AutoConfigureAfter(RedisAutoConfiguration.class)
// RedisConnectionFactory实例存在
@ConditionalOnBean(RedisConnectionFactory.class)
// CacheManager实例不存在
@ConditionalOnMissingBean(CacheManager.class)
// 根据CacheCondition选择导入
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

    /**
     * 创建RedisCacheManager
     */
    @Bean
    RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
            ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
            ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
            RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
        // 使用RedisCachemanager
        RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
                determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            // 设置CacheProperties配置的值
            builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
        }
        if (cacheProperties.getRedis().isEnableStatistics()) {
            // 设置CacheProperties配置的值
            builder.enableStatistics();
        }
        // 装饰redisCacheManagerBuilder
        redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
        // 装饰redisCacheManager
        return cacheManagerCustomizers.customize(builder.build());
    }

    /**
     * 如果redisCacheConfiguration不存在则使用默认值
     */
    private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
            CacheProperties cacheProperties,
            ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
            ClassLoader classLoader) {
        return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));
    }

    /**
     * 根据CacheProperties创建默认RedisCacheConfigutation
     */
    private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(
            CacheProperties cacheProperties, ClassLoader classLoader) {
        Redis redisProperties = cacheProperties.getRedis();
        org.springframework.data.redis.cache.RedisCacheConfiguration config =     org.springframework.data.redis.cache.RedisCacheConfiguration
                .defaultCacheConfig();
        config = config.serializeValuesWith(
                SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));

        // 优先使用CacheProperties的值,若存在的话
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixCacheNameWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }
}

3.2 SimpleCacheConfiguration

// Bean方法不被代理
@Configuration(proxyBeanMethods = false)
// 不存在CacheManager实例
@ConditionalOnMissingBean(CacheManager.class)
// 根据CacheCondition选择导入
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

    /**
     * 创建CacheManager
     */
    @Bean
    ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
            CacheManagerCustomizers cacheManagerCustomizers) {
        // 使用ConcurrentMapCacheManager
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        List<String> cacheNames = cacheProperties.getCacheNames();
        // 如果CacheProperties配置了cacheNames,则使用
        if (!cacheNames.isEmpty()) {
            // 配置了cacheNames,则不支持动态创建缓存了
            cacheManager.setCacheNames(cacheNames);
        }
        // 装饰ConcurrentMapCacheManager
        return cacheManagerCustomizers.customize(cacheManager);
    }
}

Spring Cache 中,我们可以通过使用注解来声明缓存操作。例如,@Cacheable 注解用于表示方法的结果应该被缓存,@CacheEvict 注解用于表示缓存应该被清除,@CachePut 注解用于表示方法的结果应该被缓存,等等。这些注解都是在运行时被解析的,Spring 通过解析这些注解来确定缓存操作应该被执行的方式。 在 Spring 中,注解解析器是用于解析注解的组件。在 Spring Cache 中,注解解析器的作用是解析 @Cacheable、@CachePut、@CacheEvict 和 @Caching 注解,并将它们转换为缓存操作的定义。Spring Cache 提供了 AnnotationCacheOperationSource 类来实现这个功能。 AnnotationCacheOperationSource 类实现了 CacheOperationSource 接口,它的主要作用是解析注解并返回缓存操作的定义。在 Spring Cache 中,每个缓存操作的定义都表示为CacheOperation 对象。AnnotationCacheOperationSource 类的 getCacheOperations() 方法返回个 List<CacheOperation> 对象,表示指定方法上的所有缓存操作的定义。 AnnotationCacheOperationSource 类的核心方法是 buildCacheOperations() 方法。这个方法接收个 MethodInvocation 对象,它描述了要执行的方法及其参数。buildCacheOperations() 方法首先检查给定的方法是否具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解。如果有这些注解,它将解析它们并返回个包含缓存操作定义的 List<CacheOperation> 对象。如果没有这些注解,它将返回个空的 List<CacheOperation> 对象。 解析注解的过程非常复杂,AnnotationCacheOperationSource 类使用了多个辅助类来完成这个过程。这些辅助类包括 AnnotationCacheAspect、CacheAnnotationParser、CacheAnnotationParserUtils 和 CacheAspectSupport。AnnotationCacheAspect 是个切面,它拦截所有具有 @Cacheable、@CachePut、@CacheEvict 或 @Caching 注解的方法。CacheAnnotationParser 类用于解析注解并返回相应的缓存操作定义。CacheAnnotationParserUtils 类包含些通用的注解解析方法。CacheAspectSupport 类是个抽象类,它提供了些通用的缓存操作方法,它的子类可以继承这些方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值