JetCache的配置加载主要是在jetcache-autoconfigure模块中完成的,其中加载配置的核心类是JetCacheAutoConfiguration,主要是用于创建全局性的一些Bean,例如全局缓存配置类GlobalCacheConfig,AutoConfigureBeans和SpringConfigProvider等,源码如下:
@Configuration
@ConditionalOnClass(GlobalCacheConfig.class)
@ConditionalOnMissingBean(GlobalCacheConfig.class)
@EnableConfigurationProperties(JetCacheProperties.class)
@Import({RedisAutoConfiguration.class,
CaffeineAutoConfiguration.class,
MockRemoteCacheAutoConfiguration.class,
LinkedHashMapAutoConfiguration.class,
RedisLettuceAutoConfiguration.class,
RedisSpringDataAutoConfiguration.class,
RedissonAutoConfiguration.class})
public class JetCacheAutoConfiguration {
public static final String GLOBAL_CACHE_CONFIG_NAME = "globalCacheConfig";
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean
public SpringConfigProvider springConfigProvider(
@Autowired ApplicationContext applicationContext,
@Autowired GlobalCacheConfig globalCacheConfig,
@Autowired(required = false) EncoderParser encoderParser,
@Autowired(required = false) KeyConvertorParser keyConvertorParser,
@Autowired(required = false) Consumer metricsCallback) {
return new JetCacheBaseBeans().springConfigProvider(applicationContext, globalCacheConfig,
encoderParser, keyConvertorParser, metricsCallback);
}
@Bean(name = "jcCacheManager",destroyMethod = "close")
@ConditionalOnMissingBean
public SimpleCacheManager cacheManager(@Autowired SpringConfigProvider springConfigProvider) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCacheBuilderTemplate(springConfigProvider.getCacheBuilderTemplate());
return cacheManager;
}
@Bean
@ConditionalOnMissingBean
public AutoConfigureBeans autoConfigureBeans() {
return new AutoConfigureBeans();
}
@Bean
public static BeanDependencyManager beanDependencyManager() {
return new BeanDependencyManager();
}
@Bean(name = GLOBAL_CACHE_CONFIG_NAME)
public GlobalCacheConfig globalCacheConfig(AutoConfigureBeans autoConfigureBeans, JetCacheProperties props) {
GlobalCacheConfig _globalCacheConfig = new GlobalCacheConfig();
_globalCacheConfig = new GlobalCacheConfig();
_globalCacheConfig.setHiddenPackages(props.getHiddenPackages());
_globalCacheConfig.setStatIntervalMinutes(props.getStatIntervalMinutes());
_globalCacheConfig.setAreaInCacheName(props.isAreaInCacheName());
_globalCacheConfig.setPenetrationProtect(props.isPenetrationProtect());
_globalCacheConfig.setEnableMethodCache(props.isEnableMethodCache());
_globalCacheConfig.setLocalCacheBuilders(autoConfigureBeans.getLocalCacheBuilders());
_globalCacheConfig.setRemoteCacheBuilders(autoConfigureBeans.getRemoteCacheBuilders());
return _globalCacheConfig;
}
}
无论是使用内存缓存LinkedHashMap和caffeine,亦或是通过lettuce、redisson和spring-data-redis来操作Redis服务缓存数据,其自动加载配置的操作基本上都是相似的。
例如LinkedHashMapAutoConfiguration完成LinkedHashMap类型的缓存初始化操作,源码如下:
/**
* LinkedHashMap自动配置类
*/
@Component
@Conditional(LinkedHashMapAutoConfiguration.LinkedHashMapCondition.class)
public class LinkedHashMapAutoConfiguration extends EmbeddedCacheAutoInit {
public LinkedHashMapAutoConfiguration() {
super("linkedhashmap");
}
@Override
/**
* 初始化缓存
* @param ct 配置树
* @param cacheAreaWithPrefix 缓存区域名称(包括前缀)
* @return 初始化后的缓存
*/
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
LinkedHashMapCacheBuilder builder = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder();
parseGeneralConfig(builder, ct);
return builder;
}
/**
* LinkedHashMap条件类
*/
public static class LinkedHashMapCondition extends JetCacheCondition {
public LinkedHashMapCondition() {
super("linkedhashmap");
}
}
}
其中LinkedHashMapCondition是一个自定义的条件类,如果spring boot项目的application.yml文件中jetcache.local.${areaName} .type或jetcache.remote.${areaName}.type为linkedhashmap时,就会构建LinkedHashMapAutoConfiguration对应的bean,JetCacheCondition类的源码如下:
public abstract class JetCacheCondition extends SpringBootCondition {
/**
* 缓存类型数组
*/
private String[] cacheTypes;
/**
* 构造方法
* @param cacheTypes 缓存类型数组
*/
protected JetCacheCondition(String... cacheTypes) {
Objects.requireNonNull(cacheTypes, "cacheTypes can't be null");
Assert.isTrue(cacheTypes.length > 0, "cacheTypes length is 0");
this.cacheTypes = cacheTypes;
}
/**
* 判断条件是否匹配
* @param conditionContext 条件上下文
* @param annotatedTypeMetadata 注解类型元数据
* @return 匹配结果
*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
ConfigTree ct = new ConfigTree((ConfigurableEnvironment) conditionContext.getEnvironment(), "jetcache.");
if (match(ct, "local.") || match(ct, "remote.")) {
return ConditionOutcome.match();
} else {
return ConditionOutcome.noMatch("no match for " + cacheTypes[0]);
}
}
/**
* 判断是否匹配指定前缀
* @param ct 配置树
* @param prefix 前缀
* @return 是否匹配
*/
private boolean match(ConfigTree ct, String prefix) {
Map m = ct.subTree(prefix).getProperties();
Set cacheAreaNames = m.keySet().stream().map((s) -> s.substring(0, s.indexOf('.'))).collect(Collectors.toSet());
final List cacheTypesList = Arrays.asList(cacheTypes);
return cacheAreaNames.stream().anyMatch((s) -> cacheTypesList.contains(m.get(s + ".type")));
}
}
同理,如果想要使用redisson或lettuce中间件来进行远程缓存的处理,则对应的自动化配置类的源码如下:
@Configuration
@Conditional(RedissonAutoConfiguration.RedissonCondition.class)
public class RedissonAutoConfiguration {
private static final String CACHE_TYPE = "redisson";
public static class RedissonCondition extends JetCacheCondition {
public RedissonCondition() {
super(CACHE_TYPE);
}
}
@Bean
public RedissonAutoInit redissonAutoInit() {
return new RedissonAutoInit();
}
}
在上面的RedissonAutoConfiguration.RedissonCondition条件类中对应的cacheTypes为redisson,即当spring boot项目的application.yml文件中jetcache.local.${areaName} .type或jetcache.remote.${areaName}.type为redisson时,就会构建RedissonAutoConfiguration对应的bean。
同理,加载lettuce对应的自动化加载的源码如下:
@Configuration
@Conditional(RedisLettuceAutoConfiguration.RedisLettuceCondition.class)
public class RedisLettuceAutoConfiguration {
public static final String AUTO_INIT_BEAN_NAME = "redisLettuceAutoInit";
public static class RedisLettuceCondition extends JetCacheCondition {
public RedisLettuceCondition() {
super("redis.lettuce");
}
}
@Bean(name = {AUTO_INIT_BEAN_NAME})
public RedisLettuceAutoInit redisLettuceAutoInit() {
return new RedisLettuceAutoInit();
}
}
上面介绍的都是各种缓存方式的自动化配置的类,可以发现当自动化配置的Bean被创建后,都会创建AbstractCacheAutoInit对应的Bean。JetCache的自动化的类图如下:
上面所有类型的缓存初始化类最终都继承了InitializingBean,InitializingBean是Spring框架中的一个接口,它定义了一个方法 afterPropertiesSet()。当一个bean实例化后,Spring容器会自动调用该方法来完成一些初始化操作。
抽象类AbstractCacheAutoInit在实现InitializingBean接口时,重写了afterPropertiesSet() 方法来实现一些特定的初始化逻辑。在 Spring 容器完成AbstractCacheAutoInit类型的bean的属性注入后,会调用AbstractCacheAutoInit类的afterPropertiesSet() 方法来执行这些初始化逻辑。
我们先看一下AbstractCacheAutoInit类的afterPropertiesSet()方法,源码如下:
/**
* 在属性设置之后调用的方法
*/
@Override
public void afterPropertiesSet() {
// 如果还未初始化
if (!inited) {
// 上锁
reentrantLock.lock();
try{
// 如果还未初始化
if (!inited) {
// 处理本地缓存构建器
process("jetcache.local.", autoConfigureBeans.getLocalCacheBuilders(), true);
// 处理远程缓存构建器
process("jetcache.remote.", autoConfigureBeans.getRemoteCacheBuilders(), false);
// 设置已初始化标志为true
inited = true;
}
}finally {
// 解锁
reentrantLock.unlock();
}
}
}
上面的代码相对比较简单,首先会判断是否已经进行初始化了,如果没有就先用锁,然后处理本地和远程缓存的处理器。实例autoConfigureBeans中的方法getLocalCacheBuilders()和getRemoteCacheBuilders()是用于获取本地和远程的缓存构建者,类AutoConfigureBeans的源码很简单,我们简单的看一下即可,不做详细介绍。AutoConfigureBeans源码如下:
public class AutoConfigureBeans {
private Map localCacheBuilders = new HashMap<>();
private Map remoteCacheBuilders = new HashMap<>();
private Map customContainer = Collections.synchronizedMap(new HashMap<>());
public Map getLocalCacheBuilders() {
return localCacheBuilders;
}
public void setLocalCacheBuilders(Map localCacheBuilders) {
this.localCacheBuilders = localCacheBuilders;
}
public Map getRemoteCacheBuilders() {
return remoteCacheBuilders;
}
public void setRemoteCacheBuilders(Map remoteCacheBuilders) {
this.remoteCacheBuilders = remoteCacheBuilders;
}
public Map getCustomContainer() {
return customContainer;
}
public void setCustomContainer(Map customContainer) {
this.customContainer = customContainer;
}
}
其中AutoConfigureBeans类的Bean是在JetCacheAutoConfiguration类中被创建的。我们继续看AbstractCacheAutoInit类中process方法的逻辑,其源码如下:
/**
* 处理缓存区域
*
* @param prefix 缓存前缀
* @param cacheBuilders 缓存构建器的映射
* @param local 是否为本地缓存
*/
private void process(String prefix, Map cacheBuilders, boolean local) {
// 创建配置树解析器
ConfigTree resolver = new ConfigTree(environment, prefix);
// 获取缓存区域的属性集合
Map m = resolver.getProperties();
// 获取缓存区域的名称集合
Set cacheAreaNames = resolver.directChildrenKeys();
// 遍历缓存区域名称集合
for (String cacheArea : cacheAreaNames) {
// 获取缓存区域类型
final Object configType = m.get(cacheArea + ".type");
// 判断缓存区域类型是否匹配指定的类型名称
boolean match = Arrays.stream(typeNames).anyMatch((tn) -> tn.equals(configType));
// 如果匹配失败,则继续下个循环
if (!match) {
continue;
}
// 获取缓存区域的子树
ConfigTree ct = resolver.subTree(cacheArea + ".");
// 初始化缓存区域的缓存
logger.info("init cache area {} , type= {}", cacheArea, typeNames[0]);
CacheBuilder c = initCache(ct, local ? "local." + cacheArea : "remote." + cacheArea);
// 将缓存构建器添加到映射中
cacheBuilders.put(cacheArea, c);
}
}
这个函数用于处理缓存区域。首先根据给定的前缀和环境创建一个配置树解析器,并获取缓存区域的属性和名称集合。然后遍历缓存区域名称集合,获取每个区域的类型,并与给定的类型名称进行比较。如果类型匹配,则初始化该缓存区域的缓存,并将缓存构建器添加到给定的映射中。
上面的方法会针对每一个areaName分别去调用initCache方法进行初始化操作,接下来,我们先看一下initCache方法的源码:
protected abstract CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix);
AbstractCacheAutoInit类的initCache方法是一个抽象方法,这就需要在子类中实现该方法,我们接下来就开始分析主要的几个内存和Redis缓存的自动初始化类的源码了。
LinkedHashMapAutoConfiguration类是基于LinkedHashMap来创建的内存缓存自动配置类,该类继承自EmbeddedCacheAutoInit抽象类,该类重写了initCache方法,用于内存缓存的初始化操作,该方法会创建用于创建LinkedHashMap类型的内存缓存的创建者,并调用parseGeneralConfig方法解析JetCache内存缓存的配置。
为了便于理解,我们先看一下JetCache中关于内存缓存的配置信息:
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
hidePackages: com.alibaba
local:
default:
type: caffeine
limit: 100
keyConvertor: fastjson2 #其他可选:fastjson/jackson
expireAfterWriteInMillis: 100000
otherArea:
type: linkedhashmap
limit: 100
keyConvertor: none
expireAfterWriteInMillis: 100000
配置通用说明如下:
属性 | 默认值 | 说明 |
---|---|---|
jetcache.statIntervalMinutes | 0 | 统计间隔,0表示不统计 |
jetcache.areaInCacheName | true(2.6-) false(2.7+) | jetcache-anno把cacheName作为远程缓存key前缀,2.4.3以前的版本总是把areaName加在cacheName中,因此areaName也出现在key前缀中。2.4.4以后可以配置,为了保持远程key兼容默认值为true,但是新项目的话false更合理些,2.7默认值已改为false。 |
jetcache.hiddenPackages | 无 | @Cached和@CreateCache自动生成name的时候,为了不让name太长,hiddenPackages指定的包名前缀被截掉 |
jetcache.local.${area}.type | 无 | 缓存类型。tair、redis为当前支持的远程缓存;linkedhashmap、caffeine为当前支持的本地缓存类型 |
jetcache.local.${area}.keyConvertor | fastjson2 | key转换器的全局配置,2.6.5+已经支持的keyConvertor:fastjson2 /jackson ;2.6.5-只有一个已经实现的keyConvertor: fastjson 。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none ,此时通过equals方法来识别key。方法缓存必须指定keyConvertor |
jetcache.local.${area}.limit | 100 | 每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100 |
jetcache.local.${area}.expireAfterWriteInMillis | 无穷大 | 以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis) |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。 |
内存缓存中的常用的配置主要是type、keyConvertor、limit、expireAfterWriteInMillis和expireAfterAccessInMillis。由于系统所在的服务器对每个服务的占用内存比较敏感,所以需要对内存缓存中的数据量进行限制,这就需要配置limit,如果不配置则每个缓存实例就默认是100个,一旦某个缓存实例的缓存达到limit的限制后,新的缓存数据就无法保存到内存缓存中了,所以合理配置limit的值很重要,鉴于可以在查询函数中通过配置@Cached注解中的localLimit来设置每个缓存实例的limit,且@Cached注解中的localLimit优先于全局配置中的limit。keyConvertor可以使用默认的配置,即linkedhashmap类型的缓存使用none,caffeine类型的缓存使用fastjson2。@Cached注解中的keyConvertor来设置每个缓存实例的keyConvertor,且@Cached注解中的keyConvertor优先于全局配置中的keyConvertor。
initCache方法的源码如下:
/**
* 重写方法,用于初始化缓存
* @param ct 缓存的配置树
* @param cacheAreaWithPrefix 缓存区服(包括前缀)
* @return 返回构建好的缓存
*/
@Override
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
LinkedHashMapCacheBuilder builder = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder();
parseGeneralConfig(builder, ct);
return builder;
}
创建LinkerHashMap类型的缓存构建者的源码如下:
public class LinkedHashMapCacheBuilder> extends EmbeddedCacheBuilder {
/**
* LinkedHashMapCacheBuilder的内部类
*/
public static class LinkedHashMapCacheBuilderImpl extends LinkedHashMapCacheBuilder {
}
/**
* 创建一个LinkedHashMapCacheBuilderImpl实例
*
* @return 返回LinkedHashMapCacheBuilderImpl实例
*/
public static LinkedHashMapCacheBuilderImpl createLinkedHashMapCacheBuilder() {
return new LinkedHashMapCacheBuilderImpl();
}
/**
* 私有构造方法
*/
protected LinkedHashMapCacheBuilder() {
buildFunc((c) -> new LinkedHashMapCache((EmbeddedCacheConfig) c));
}
}
在上面构建的LinkedHashMapCacheBuilder实例时,LinkedHashMapCacheBuilder的构造函数会初始化相应的缓存类LinkedHashMapCache。这里紧急简单介绍一下,后面会详细就缓存构建者进行描述。
CaffeineAutoConfiguration类是基于Caffeine来创建的内存缓存自动配置类,该类继承自EmbeddedCacheAutoInit抽象类,该类重写了initCache方法,该方法会创建用于创建Caffeine类型的内存缓存的创建者,并调用parseGeneralConfig方法解析JetCache的配置。源码如下:
@Override
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
CaffeineCacheBuilder builder = CaffeineCacheBuilder.createCaffeineCacheBuilder();
parseGeneralConfig(builder, ct);
return builder;
}
同样,关于构建CaffeineCacheBuilder相应的代码逻辑也放在后面再进行介绍。
内存缓存LinkedHashMap和caffeine的自动初始化类都是继承自EmbeddedCacheAutoInit抽象类,主要是解析配置的内存缓存的最大缓存数量。EmbeddedCacheAutoInit类的源码如下:
public abstract class EmbeddedCacheAutoInit extends AbstractCacheAutoInit {
public EmbeddedCacheAutoInit(String... cacheTypes) {
super(cacheTypes);
}
@Override
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
super.parseGeneralConfig(builder, ct);
EmbeddedCacheBuilder ecb = (EmbeddedCacheBuilder) builder;
ecb.limit(Integer.parseInt(ct.getProperty("limit", String.valueOf(CacheConsts.DEFAULT_LOCAL_LIMIT))));
}
}
解析配置文件的逻辑放到下面的JetCache配置解析章节进行讲述,这里不做过多描述。
我们上面提到内存缓存的自动化配置类的父类都是EmbeddedCacheAutoInit,所以这里我们需要先来看一下类EmbeddedCacheAutoInit的源码。
public abstract class EmbeddedCacheAutoInit extends AbstractCacheAutoInit {
public EmbeddedCacheAutoInit(String... cacheTypes) {
super(cacheTypes);
}
@Override
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
super.parseGeneralConfig(builder, ct);
EmbeddedCacheBuilder ecb = (EmbeddedCacheBuilder) builder;
ecb.limit(Integer.parseInt(ct.getProperty("limit", String.valueOf(CacheConsts.DEFAULT_LOCAL_LIMIT))));
}
}
上面的parseGeneralConfig方法会先调用父类的parseGeneralConfig方法解析常用配置,然后将配置中的limit同步到EmbeddedCacheBuilder实例的limit属性中。
接下来,我们再来看一下EmbeddedCacheAutoInit的父类AbstractCacheAutoInit的parseGeneralConfig方法的源码如下:
// 解析通用配置
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
// 获得缓存构建器
AbstractCacheBuilder acb = (AbstractCacheBuilder) builder;
// 设置key转换器
acb.keyConvertor(new ParserFunction(ct.getProperty("keyConvertor", KeyConvertor.FASTJSON2)));
// 获取expireAfterWriteInMillis配置
String expireAfterWriteInMillis = ct.getProperty("expireAfterWriteInMillis");
// 保持与2.1版本的兼容性,如果未找到expireAfterWriteInMillis,尝试获取defaultExpireInMillis配置
if (expireAfterWriteInMillis == null) {
expireAfterWriteInMillis = ct.getProperty("defaultExpireInMillis");
}
// 如果expireAfterWriteInMillis配置存在,则将其转换为long型并设置到acb对象中
if (expireAfterWriteInMillis != null) {
acb.setExpireAfterWriteInMillis(Long.parseLong(expireAfterWriteInMillis));
}
// 获取expireAfterAccessInMillis配置
String expireAfterAccessInMillis = ct.getProperty("expireAfterAccessInMillis");
// 如果expireAfterAccessInMillis配置存在,则将其转换为long型并设置到acb对象中
if (expireAfterAccessInMillis != null) {
acb.setExpireAfterAccessInMillis(Long.parseLong(expireAfterAccessInMillis));
}
}
这个函数用于解析通用配置信息。首先,将传入的builder对象转换为AbstractCacheBuilder类型,并设置keyConvertor为通过ct对象获取的属性值或默认值。然后,从ct对象获取expireAfterWriteInMillis属性值,如果该属性值为空,则使用defaultExpireInMillis属性值。最后,将获取到的expireAfterWriteInMillis和expireAfterAccessInMillis属性值转换为long类型,并设置到acb对象中。
在RedissonAutoConfiguration类中redissonAutoInit()函数会创建RedissonAutoInit对应的bean;在RedisLettuceAutoConfiguration类中redisLettuceAutoInit()函数会创建RedisLettuceAutoInit对应的bean。同理RedisSpringDataAutoConfiguration类中springDataRedisAutoInit()函数会创建SpringDataRedisAutoInit对应的bean。根据上面的JetCache的自动化的类图可以发现,RedissonAutoInit、RedisLettuceAutoInit和SpringDataRedisAutoInit都是ExternalCacheAutoInit的子类,所以主要是初始化函数initCache方法的实现会有所不同,我们在下面会着重的就RedissonAutoInit、RedisLettuceAutoInit和SpringDataRedisAutoInit的initCache方法进行详细的介绍一下。
鉴于下面的初始化方法中会获取JetCache的配置创建Redis连接,所以我们优先看一下JetCache中关于远程缓存的配置说明,会便于下面的源码的理解。
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
hidePackages: com.alibaba
remote:
default:
type: redis.lettuce
keyConvertor: fastjson2
broadcastChannel: projectA
mode: cluster #redis模式,cluster:集群模式
#readFrom: slavePreferred
uri:
- redis://127.0.0.1:7000 #redis服务的//IP:Port
- redis://127.0.0.1:7001
- redis://127.0.0.1:7002
otherArea:
type: redis.lettuce
keyConvertor: fastjson2 #其他可选:fastjson/jackson
broadcastChannel: projectA
valueEncoder: java #其他可选:kryo/kryo5
valueDecoder: java #其他可选:kryo/kryo5
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: ${redis.host}
port: ${redis.port}
配置通用说明如下
属性 | 默认值 | 说明 |
---|---|---|
jetcache.remote.${area}.type | 无 | 缓存类型。tair、redis为当前支持的远程缓存;linkedhashmap、caffeine为当前支持的本地缓存类型 |
jetcache.remote.${area}.keyConvertor | fastjson2 | key转换器的全局配置,2.6.5+已经支持的keyConvertor:fastjson2 /jackson ;2.6.5-只有一个已经实现的keyConvertor: fastjson 。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none ,此时通过equals方法来识别key。方法缓存必须指定keyConvertor |
jetcache.remote.${area}.valueEncoder | java | 序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java /kryo /kryo5 ;2.6-可选java /kryo |
jetcache.remote.${area}.valueDecoder | java | 序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java /kryo /kryo5 ;2.6-可选java /kryo |
jetcache.remote.${area}.expireAfterWriteInMillis | 无穷大 | 以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis) |
jetcache.remote.${area}.broadcastChannel | 无 | jetcahe2.7的两级缓存支持更新以后失效其他JVM中的local cache,但多个服务共用redis同一个channel可能会造成广播风暴,需要在这里指定channel,你可以决定多个不同的服务是否共用同一个channel。如果没有指定则不开启。 |
jetcache.remote.${area}.mode | 无 | 缓存模式,cluster:集群模式 |
jetcache.remote.${area}.uri | 无 | 缓存服务的URI,可以支持多个 |
jetcache.remote.${area}.ip | 无 | 缓存服务的IP地址 |
jetcache.remote.${area}.port | 无 | 缓存服务的端口 |
上表中${area}对应@Cached和@CreateCache的area属性。注意如果注解上没有指定area,默认值是"default"。
RedissonAutoInit继承自抽象类ExternalCacheAutoInit,在initCache方法中主要是获取所有RedissonClient的bean映射,如果找到了多个RedissonClient的bean,则根据配置决定使用哪个RedissonClient。解析spring boot项目的application.yml文件中jetcache.remote .${areaName}中的Redis配置,创建用于创建Redisson类型的内存缓存的创建者RedissonCacheBuilder,并调用parseGeneralConfig方法解析JetCache的配置。源码如下:
/**
* 初始化缓存
*
* @param ct 缓存配置树
* @param cacheAreaWithPrefix 缓存区域名称(包括前缀)
* @return 缓存构建器
*/
@Override
protected CacheBuilder initCache(final ConfigTree ct, final String cacheAreaWithPrefix) {
// 获取RedissonClient的bean
final Map beans = this.context.getBeansOfType(RedissonClient.class);
if (beans.isEmpty()) {
throw new CacheConfigException("no RedissonClient in spring context");
}
RedissonClient client = beans.values().iterator().next();
if (beans.size() > 1) {
// 获取配置树中的redissonClient属性值
final String redissonClientName = ct.getProperty("redissonClient");
if (Objects.isNull(redissonClientName) || redissonClientName.isEmpty()) {
throw new CacheConfigException("redissonClient is required, because there is multiple RedissonClient in Spring context");
}
if (!beans.containsKey(redissonClientName)) {
throw new CacheConfigException("there is no RedissonClient named " + redissonClientName + " in Spring context");
}
client = beans.get(redissonClientName);
}
// 创建外部缓存构建器并设置RedissonClient
final ExternalCacheBuilder> builder = RedissonCacheBuilder.createBuilder().redissonClient(client);
// 解析通用配置并添加到构建器中
parseGeneralConfig(builder, ct);
return builder;
}
函数的功能是初始化缓存,具体实现如下:
- 获取所有RedissonClient的bean映射,如果找到了多个RedissonClient的bean,则根据配置决定使用哪个RedissonClient。
- 创建一个ExternalCacheBuilder对象,并将RedissonClient设置为其属性。
- 解析通用配置并添加到CacheBuilder中。
RedisLettuceAutoInit继承自抽象类ExternalCacheAutoInit,在initCache方法相较于RedissonAutoInit类的initCache方法方法的逻辑也比较复杂,解析spring boot项目的application.yml文件中jetcache.remote .${areaName}中的Redis配置,创建Redis连接,如果配置了集群模式,则需要创建集群模式的Redis连接;如果配置的是单节点模式,则会创建单节点的Redis连接,创建用于创建Lettuce类型的内存缓存的创建者RedisLettuceCacheBuilder,并调用parseGeneralConfig方法解析JetCache的配置。最后将客户端和连接对象存入自动配置的自定义容器中,并返回一个包含相关配置的ExternalCacheBuilder对象。源码如下:
@Override
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
// 获取Lettuce缓存的URI配置
Map map = ct.subTree("uri"/*there is no dot*/).getProperties();
// 获取读取模式
String readFromStr = ct.getProperty("readFrom");
String mode = ct.getProperty("mode");
// 获取异步结果超时时间
long asyncResultTimeoutInMillis = ct.getProperty("asyncResultTimeoutInMillis", CacheConsts.ASYNC_RESULT_TIMEOUT.toMillis());
// 判断是否启用广播通道
boolean enablePubSub = parseBroadcastChannel(ct) != null;
ReadFrom readFrom = null;
// 根据读取模式字符串获取ReadFrom枚举类型
if (readFromStr != null) {
readFrom = ReadFrom.valueOf(readFromStr.trim());
}
AbstractRedisClient client;
StatefulConnection connection;
StatefulRedisPubSubConnection pubSubConnection = null;
// 判断是否配置了URI
if (map == null || map.size() == 0) {
// 如果未配置URI,则抛出异常
throw new CacheConfigException("lettuce uri is required");
} else {
// 将配置的URI转换为RedisURI对象列表
List uriList = map.values().stream()
.map((k) -> RedisURI.create(URI.create(k.toString())))
.collect(Collectors.toList());
if ("Cluster".equalsIgnoreCase(mode)) {
// 配置为集群模式
client = RedisClusterClient.create(uriList);
// 链接集群节点并获取连接对象
connection = clusterConnection(ct, readFrom, (RedisClusterClient) client, false);
if (enablePubSub) {
// 如果启用了广播通道,则获取与集群节点的连接对象
pubSubConnection = (StatefulRedisPubSubConnection) clusterConnection(ct, readFrom, (RedisClusterClient) client, true);
}
} else {
// 配置为单节点模式
client = RedisClient.create();
((RedisClient) client).setOptions(ClientOptions.builder().
disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS).build());
// 链接单节点并获取连接对象
StatefulRedisMasterReplicaConnection c = MasterReplica.connect(
(RedisClient) client, new JetCacheCodec(), uriList);
if (readFrom != null) {
// 如果指定了读取模式,则设置连接对象的读取模式
c.setReadFrom(readFrom);
}
connection = c;
if (enablePubSub) {
// 如果启用了广播通道,则获取与单节点的连接对象
pubSubConnection = ((RedisClient) client).connectPubSub(new JetCacheCodec(), uriList.get(0));
}
}
}
// 创建外部缓存构建器对象
ExternalCacheBuilder externalCacheBuilder = RedisLettuceCacheBuilder.createRedisLettuceCacheBuilder()
.connection(connection)
.pubSubConnection(pubSubConnection)
.redisClient(client)
.asyncResultTimeoutInMillis(asyncResultTimeoutInMillis);
// 解析通用配置
parseGeneralConfig(externalCacheBuilder, ct);
// eg: "remote.default.client"
// 将Redis客户端对象存入自动配置的自定义容器中
autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".client", client);
// 获取Lettuce连接管理器对象
LettuceConnectionManager m = LettuceConnectionManager.defaultManager();
// 初始化Lettuce连接管理器
m.init(client, connection);
// 将Lettuce连接管理器中的连接对象存入自动配置的自定义容器中
autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".connection", m.connection(client));
// 将Lettuce连接管理器中的命令对象存入自动配置的自定义容器中
autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".commands", m.commands(client));
// 将Lettuce连接管理器中的异步命令对象存入自动配置的自定义容器中
autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".asyncCommands", m.asyncCommands(client));
// 将Lettuce连接管理器中的反应式命令对象存入自动配置的自定义容器中
autoConfigureBeans.getCustomContainer().put(cacheAreaWithPrefix + ".reactiveCommands", m.reactiveCommands(client));
// 返回外部缓存构建器对象
return externalCacheBuilder;
}
在上面的方法中,如果配置的Redis配置是集群模式则会调用clusterConnection方法建立集群模式的Redis连接,相应的源码如下:
/**
* 创建一个与Redis集群连接的状态保持连接对象。
* @param ct 配置树对象,用于获取配置信息
* @param readFrom 读取来源,用于设置连接的读取来源
* @param client Redis集群客户端对象
* @param pubsub 是否创建一个订阅连接
* @return 状态保持连接对象
*/
private StatefulConnection clusterConnection(ConfigTree ct, ReadFrom readFrom, RedisClusterClient client, boolean pubsub) {
int enablePeriodicRefresh = ct.getProperty("enablePeriodicRefresh", 60);
boolean enableAllAdaptiveRefreshTriggers = ct.getProperty("enableAllAdaptiveRefreshTriggers", true);
ClusterTopologyRefreshOptions.Builder topologyOptionBuilder = ClusterTopologyRefreshOptions.builder();
if (enablePeriodicRefresh > 0) {
topologyOptionBuilder.enablePeriodicRefresh(Duration.ofSeconds(enablePeriodicRefresh));
}
if (enableAllAdaptiveRefreshTriggers) {
topologyOptionBuilder.enableAllAdaptiveRefreshTriggers();
}
ClusterClientOptions options = ClusterClientOptions.builder()
.topologyRefreshOptions(topologyOptionBuilder.build())
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
.build();
client.setOptions(options);
if (pubsub) {
return client.connectPubSub(new JetCacheCodec());
} else {
StatefulRedisClusterConnection c = client.connect(new JetCacheCodec());
if (readFrom != null) {
c.setReadFrom(readFrom);
}
return c;
}
}
SpringDataRedisAutoInit继承自抽象类ExternalCacheAutoInit,initCache方法的实现比较简单,先是获取RedisConnectionFactory的Bean,如果有多个就获取jetCache配置中connectionFactory来决定使用哪一个RedisConnectionFactory实例,创建用于创建Lettuce类型的内存缓存的创建者RedisLettuceCacheBuilder,并调用parseGeneralConfig方法解析JetCache的配置。最后将客户端和连接对象存入自动配置的自定义容器中,并返回一个包含相关配置的ExternalCacheBuilder对象。源码如下:
/**
* 初始化缓存
*
* @param ct 配置树
* @param cacheAreaWithPrefix 缓存区域名称(包括前缀)
* @return 缓存构建器
*/
@Override
protected CacheBuilder initCache(ConfigTree ct, String cacheAreaWithPrefix) {
// 从应用上下文中获取 RedisConnectionFactory 的 bean 映射
Map beans = applicationContext.getBeansOfType(RedisConnectionFactory.class);
// 如果 bean 映射为空或为空,抛出异常
if (beans == null || beans.isEmpty()) {
throw new CacheConfigException("no RedisConnectionFactory in spring context");
}
// 获取一个可用的 RedisConnectionFactory 实例
RedisConnectionFactory factory = beans.values().iterator().next();
// 如果 Spring 上下文中有多个 RedisConnectionFactory,抛出异常
if (beans.size() > 1) {
String connectionFactoryName = ct.getProperty("connectionFactory");
// 如果 connectionFactoryName 为空,抛出异常
if (connectionFactoryName == null) {
throw new CacheConfigException(
"connectionFactory is required, because there is multiple RedisConnectionFactory in Spring context");
}
// 检查 bean 映射中是否包含指定的 connectionFactoryName,如果不包含,抛出异常
if (!beans.containsKey(connectionFactoryName)) {
throw new CacheConfigException("there is no RedisConnectionFactory named "
+ connectionFactoryName + " in Spring context");
}
// 使用指定的 connectionFactoryName 获取 RedisConnectionFactory 实例
factory = beans.get(connectionFactoryName);
}
// 创建一个 ExternalCacheBuilder 实例,并使用 RedisSpringDataCacheBuilder 工具类创建构建器,设置 connectionFactory
ExternalCacheBuilder builder = RedisSpringDataCacheBuilder.createBuilder().connectionFactory(factory);
// 解析通用配置到构建器中
parseGeneralConfig(builder, ct);
// 返回构建器
return builder;
}
类ExternalCacheAutoInit作为Redis缓存自动化初始类的父类,实现逻辑还是比较简单的,源码如下:
/**
* 外部缓存自动初始化抽象类
*/
public abstract class ExternalCacheAutoInit extends AbstractCacheAutoInit {
public ExternalCacheAutoInit(String... cacheTypes) {
super(cacheTypes);
}
/**
* 解析通用配置
*
* @param builder 缓存构建器
* @param ct 配置树
*/
@Override
protected void parseGeneralConfig(CacheBuilder builder, ConfigTree ct) {
super.parseGeneralConfig(builder, ct);
ExternalCacheBuilder ecb = (ExternalCacheBuilder) builder;
ecb.setKeyPrefix(ct.getProperty("keyPrefix"));
ecb.setBroadcastChannel(parseBroadcastChannel(ct));
ecb.setValueEncoder(new ParserFunction(ct.getProperty("valueEncoder", CacheConsts.DEFAULT_SERIAL_POLICY)));
ecb.setValueDecoder(new ParserFunction(ct.getProperty("valueDecoder", CacheConsts.DEFAULT_SERIAL_POLICY)));
}
/**
* 解析广播通道
*
* @param ct 配置树
* @return 解析后的广播通道,若为空则返回null
*/
protected String parseBroadcastChannel(ConfigTree ct) {
String broadcastChannel = ct.getProperty("broadcastChannel");
if (broadcastChannel != null && !"".equals(broadcastChannel.trim())) {
return broadcastChannel.trim();
} else {
return null;
}
}
}