一、JCache

在Java编程中,有一套基于缓存的规范,名为JSR 107(JCACHE - Java Temporary Caching API),该规范主要定义了以下几个关键类型


1、javax.cache.Cache<K, V>

缓存是一种类似 Map 的数据结构,用于临时存储应用程序数据。

  • 类似Map,Cache存储键值对,每个键值对称为Cache.Entry
  • 允许使用 Java 泛型来提高应用程序类型安全性
  • Iterable类型,即可被迭代

每个Cache实例都有一个名字,以及许多操作Cache中数据的方法,例如:

方法

含义

boolean containsKey(K key)

判断Cache中是否存在指定的key

V get(K key)

从Cache中获取指定key的数据

void put(K key, V value)

将缓存数据存入Cache

boolean putIfAbsent(K key, V value)

如果key不存在则将数据存入Cache

boolean remove(K key)

从Cache中移除指定key的数据

boolean replace(K key, V oldValue, V newValue)

以原子方式替换键的条目

void clear()

清除缓存的内容,而不通知侦听器或 CacheWriters

void close()

关闭缓存会向生成或拥有缓存的 CacheManager 发出信号,表明不应再管理该缓存

String getName()

Cache的名称

void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener completionListener)

使用为给定键配置的 CacheLoader 将指定的条目异步加载到缓存中


2、javax.cache.CacheManager

CacheManager 提供了一种建立、配置、获取、关闭和销毁唯一命名的缓存的方法。

由 CacheManager 生成和拥有的缓存通常共享公共基础结构,例如,公共 ClassLoader 和特定于实现的属性。

CacheManager 的实现还可以在所管理的缓存之间提供和共享外部资源,例如,托管缓存的内容可以存储在同一集群中。

方法

含义

<K, V, C extends Configuration<K, V>> Cache<K, V> createCache(String cacheName, C configuration)

创建Cache

<K, V> Cache<K, V> getCache(String cacheName)

获取指定名称的Cache

void destroyCache(String cacheName)

销毁cache

void close()



3、javax.cache.spi.CachingProvider

方法

含义

CacheManager getCacheManager(URI uri, ClassLoader classLoader, Properties properties)

获取Cachemanager

CacheManager getCacheManager(URI uri, ClassLoader classLoader)

获取Cachemanager

CacheManager getCacheManager()

获取Cachemanager

void close()



4、javax.cache.Caching

该类通过SPI方式加载CachingProvider实现类,并通过静态方法返回CachingProvider


5、缓存配置

Java中的缓存框架_spring


private Cache<String,String> createCache(String cacheName){
		CachingProvider provider = Caching.getCachingProvider() ;
		CacheManager cacheManager = provider.getCacheManager() ;
		return cacheManager.createCache(cacheName,configuration()) ;
	}

	private MutableConfiguration<String,String> configuration(){
		MutableConfiguration<String,String> configuration = new MutableConfiguration<>() ;
		configuration.setTypes(String.class, String.class);
		configuration.setExpiryPolicyFactory(AccessedExpiryPolicy.factoryOf(Duration.ONE_HOUR));
		return configuration ;
	}

参考:


二、Spring Cache


1、Spring对缓存的抽象

Java中的缓存框架_ide_02


2、缓存管理器

Java中的缓存框架_ide_03


3、缓存切面

Java中的缓存框架_spring_04


4、切面介入

Java中的缓存框架_ide_05

通过注解@EnableCaching可以开启spring的缓存配置。

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

}

CachingConfigurationSelector

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

	private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
			"org.springframework.cache.jcache.config.ProxyJCacheConfiguration";

	private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.cache.aspectj.AspectJCachingConfiguration";

	private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
			"org.springframework.cache.aspectj.AspectJJCacheConfiguration";


	private static final boolean jsr107Present;

	private static final boolean jcacheImplPresent;

	static {
		ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
		jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
		jcacheImplPresent = ClassUtils.isPresent(PROXY_JCACHE_CONFIGURATION_CLASS, classLoader);
	}


	/**
	 * Returns {@link ProxyCachingConfiguration} or {@code AspectJCachingConfiguration}
	 * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()},
	 * respectively. Potentially includes corresponding JCache configuration as well.
	 */
	@Override
	public String[] selectImports(AdviceMode adviceMode) {
		return switch (adviceMode) {
			case PROXY -> getProxyImports();
			case ASPECTJ -> getAspectJImports();
		};
	}

	/**
	 * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
	 * <p>Take care of adding the necessary JSR-107 import if it is available.
	 */
	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);
	}

	/**
	 * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#ASPECTJ}.
	 * <p>Take care of adding the necessary JSR-107 import if it is available.
	 */
	private String[] getAspectJImports() {
		List<String> result = new ArrayList<>(2);
		result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		if (jsr107Present && jcacheImplPresent) {
			result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
		}
		return StringUtils.toStringArray(result);
	}

}


5、KeyGenerator

Java中的缓存框架_ide_06


三、Springboot中使用缓存


1、CacheAutoConfiguration

spring-boot-autoconfigure-3.0.8.jar!org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

//在指定的这些配置类后执行. 这些配置类中会创建连接,例如:RedisConnectionFactory
@AutoConfiguration(after = { CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class,
		HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
//如果缺少名为cacheResolver,类型为CacheManager的Bean时
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
//spring cache的配置
@EnableConfigurationProperties(CacheProperties.class)
//导入配置
@Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class CacheAutoConfiguration {
  
  /**
	 * {@link ImportSelector} to add {@link CacheType} configuration classes.
	 */
	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;
		}

	}
}


2、CacheType

该枚举中定义了spring框架中支持的缓存组件

package org.springframework.boot.autoconfigure.cache;

/**
 * Supported cache types (defined in order of precedence).
 *
 * @author Stephane Nicoll
 * @author Phillip Webb
 * @author Eddú Meléndez
 * @since 1.3.0
 */
public enum CacheType {

	/**
	 * Generic caching using 'Cache' beans from the context.
	 */
	GENERIC,

	/**
	 * JCache (JSR-107) backed caching.
	 */
	JCACHE,

	/**
	 * Hazelcast backed caching.
	 */
	HAZELCAST,

	/**
	 * Couchbase backed caching.
	 */
	COUCHBASE,

	/**
	 * Infinispan backed caching.
	 */
	INFINISPAN,

	/**
	 * Redis backed caching.
	 */
	REDIS,

	/**
	 * Cache2k backed caching.
	 */
	CACHE2K,

	/**
	 * Caffeine backed caching.
	 */
	CAFFEINE,

	/**
	 * Simple in-memory caching.
	 */
	SIMPLE,

	/**
	 * No caching.
	 */
	NONE

}

3、CacheConfigurations

该类中记录了各种缓存组件对应的配置类

/**
 * Mappings between {@link CacheType} and {@code @Configuration}.
 *
 * @author Phillip Webb
 * @author Eddú Meléndez
 * @author Sebastien Deleuze
 */
final class CacheConfigurations {

	private static final Map<CacheType, String> MAPPINGS;

	static {
    // 所有支持缓存组件对应的配置类
		Map<CacheType, String> mappings = new EnumMap<>(CacheType.class);
		mappings.put(CacheType.GENERIC, GenericCacheConfiguration.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.CACHE2K, Cache2kCacheConfiguration.class.getName());
		mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class.getName());
		mappings.put(CacheType.NONE, NoOpCacheConfiguration.class.getName());
		MAPPINGS = Collections.unmodifiableMap(mappings);
	}

	private CacheConfigurations() {}

  // 根据缓存组件类型获取配置类的全路径
	static String getConfigurationClass(CacheType cacheType) {
		String configurationClassName = MAPPINGS.get(cacheType);
		Assert.state(configurationClassName != null, () -> "Unknown cache type " + cacheType);
		return configurationClassName;
	}

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

}

接在了很多配置,这里以JCacheCacheConfiguration为例


4、JCacheCacheConfiguration

Java中的缓存框架_缓存_07

package org.springframework.boot.autoconfigure.cache;

/**
 * Cache configuration for JSR-107 compliant providers.
 *
 * @author Stephane Nicoll
 * @author Madhura Bhave
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Caching.class, JCacheCacheManager.class })
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
@Conditional({ CacheCondition.class, JCacheCacheConfiguration.JCacheAvailableCondition.class })
@Import(HazelcastJCacheCustomizationConfiguration.class)
class JCacheCacheConfiguration implements BeanClassLoaderAware {

	private ClassLoader beanClassLoader;

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.beanClassLoader = classLoader;
	}

  // 重点:将javax.cache.CacheManager类型的Bean
  // 包装成org.springframework.cache.jcache.JCacheCacheManager
	@Bean
	JCacheCacheManager cacheManager(CacheManagerCustomizers customizers, CacheManager jCacheCacheManager) {
		JCacheCacheManager cacheManager = new JCacheCacheManager(jCacheCacheManager);
		return customizers.customize(cacheManager);
	}

	@Bean
	@ConditionalOnMissingBean
	CacheManager jCacheCacheManager(CacheProperties cacheProperties,
			ObjectProvider<javax.cache.configuration.Configuration<?, ?>> defaultCacheConfiguration,
			ObjectProvider<JCacheManagerCustomizer> cacheManagerCustomizers,
			ObjectProvider<JCachePropertiesCustomizer> cachePropertiesCustomizers) throws IOException {
		CacheManager jCacheCacheManager = createCacheManager(cacheProperties, cachePropertiesCustomizers);
		List<String> cacheNames = cacheProperties.getCacheNames();
		if (!CollectionUtils.isEmpty(cacheNames)) {
			for (String cacheName : cacheNames) {
				jCacheCacheManager.createCache(cacheName,
						defaultCacheConfiguration.getIfAvailable(MutableConfiguration::new));
			}
		}
		cacheManagerCustomizers.orderedStream().forEach((customizer) -> customizer.customize(jCacheCacheManager));
		return jCacheCacheManager;
	}

	private CacheManager createCacheManager(CacheProperties cacheProperties,
			ObjectProvider<JCachePropertiesCustomizer> cachePropertiesCustomizers) throws IOException {
		CachingProvider cachingProvider = getCachingProvider(cacheProperties.getJcache().getProvider());
		Properties properties = createCacheManagerProperties(cachePropertiesCustomizers, cacheProperties);
		Resource configLocation = cacheProperties.resolveConfigLocation(cacheProperties.getJcache().getConfig());
		if (configLocation != null) {
			return cachingProvider.getCacheManager(configLocation.getURI(), this.beanClassLoader, properties);
		}
		return cachingProvider.getCacheManager(null, this.beanClassLoader, properties);
	}

	private CachingProvider getCachingProvider(String cachingProviderFqn) {
		if (StringUtils.hasText(cachingProviderFqn)) {
			return Caching.getCachingProvider(cachingProviderFqn);
		}
		return Caching.getCachingProvider();
	}

	private Properties createCacheManagerProperties(
			ObjectProvider<JCachePropertiesCustomizer> cachePropertiesCustomizers, CacheProperties cacheProperties) {
		Properties properties = new Properties();
		cachePropertiesCustomizers.orderedStream()
			.forEach((customizer) -> customizer.customize(cacheProperties, properties));
		return properties;
	}

	/**
	 * Determine if JCache is available. This either kicks in if a provider is available
	 * as defined per {@link JCacheProviderAvailableCondition} or if a
	 * {@link CacheManager} has already been defined.
	 */
	@Order(Ordered.LOWEST_PRECEDENCE)
	static class JCacheAvailableCondition extends AnyNestedCondition {

		JCacheAvailableCondition() {
			super(ConfigurationPhase.REGISTER_BEAN);
		}

		@Conditional(JCacheProviderAvailableCondition.class)
		static class JCacheProvider {}

		@ConditionalOnSingleCandidate(CacheManager.class)
		static class CustomJCacheCacheManager {}
	}

	/**
	 * Determine if a JCache provider is available. This either kicks in if a default
	 * {@link CachingProvider} has been found or if the property referring to the provider
	 * to use has been set.
	 */
	@Order(Ordered.LOWEST_PRECEDENCE)
	static class JCacheProviderAvailableCondition extends SpringBootCondition {

		@Override
		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
			ConditionMessage.Builder message = ConditionMessage.forCondition("JCache");
			String providerProperty = "spring.cache.jcache.provider";
			if (context.getEnvironment().containsProperty(providerProperty)) {
				return ConditionOutcome.match(message.because("JCache provider specified"));
			}
			Iterator<CachingProvider> providers = Caching.getCachingProviders().iterator();
			if (!providers.hasNext()) {
				return ConditionOutcome.noMatch(message.didNotFind("JSR-107 provider").atAll());
			}
			providers.next();
			if (providers.hasNext()) {
				return ConditionOutcome.noMatch(message.foundExactly("multiple JSR-107 providers"));
			}
			return ConditionOutcome.match(message.foundExactly("single JSR-107 provider"));
		}
	}
}

5、创建javax.cache.CacheManager类型Bean

接下来我们可以进行缓存的配置,如下所示:

@Configuration
@EnableCaching
public class CacheConfiguration {
    
    // 注意:其它的配置此处忽略
    @Bean
    public javax.cache.CacheManager jCacheCacheManager(RedissonClient redissonClient){
        javax.cache.CacheManager jCacheCacheManager = Caching.getCachingProvider("org.redisson.jcache.JCachingProvider").getCacheManager(null, this.beanClassLoader, new Properties());
        // 自定义扩展,根据配置的缓存名创建Cache
        cacheManagerCustomizer(redissonClient, jcacheConfiguration(redissonClient)).customize(jCacheCacheManager);
        return jCacheCacheManager;
    }
}

可见,通过JCacheCacheConfiguration可完成将javax.cache.CacheManager包装成org.springframework.cache.CacheManager,从而使用Spring框架的提供的缓存注解。


6、缓存配置框架

spring:
  cache: 
    type: generic #Generic/JCache/Hazelcast/Couchbase/Infinispan/Redis/Cache2k/Caffeine/Simple/none
    caffeine:
    couchbase:
    infinispan:
    jcache:
    redis:
      timeToLive:
      cacheNullValues: true
      keyPrefix:
      useKeyPrefix: true
      enableStatistics:


7、常用Redis的装配

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
//要生效则需要创建RedisConnectionFactory类型
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

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

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

	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();
    //默认使用JDK的序列化
		config = config
			.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
		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;
	}

}

我们可以自己创建RedisConnectionFactory,也可是直接使用springboot自带的方式(RedisAutoConfiguration)

@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
//内部创建连接-RedisConnectionFactory
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
		return new StringRedisTemplate(redisConnectionFactory);
	}
}

使用lettuce连接redis配置

/**
 * Redis connection configuration using Lettuce.
 *
 * @author Mark Paluch
 * @author Andy Wilkinson
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisClient.class)
@ConditionalOnProperty(name = "spring.data.redis.client-type", havingValue = "lettuce", matchIfMissing = true)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {

	LettuceConnectionConfiguration(RedisProperties properties,
			ObjectProvider<RedisStandaloneConfiguration> standaloneConfigurationProvider,
			ObjectProvider<RedisSentinelConfiguration> sentinelConfigurationProvider,
			ObjectProvider<RedisClusterConfiguration> clusterConfigurationProvider) {
		super(properties, standaloneConfigurationProvider, sentinelConfigurationProvider, clusterConfigurationProvider);
	}
  
	@Bean
	@ConditionalOnMissingBean(RedisConnectionFactory.class)
	LettuceConnectionFactory redisConnectionFactory(
			ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
			ClientResources clientResources) {
		LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
				getProperties().getLettuce().getPool());
		return createLettuceConnectionFactory(clientConfig);
	}
  
  // ... ...
}

配置项示例

spring:
  data:
    redis:
      database: 0
      url:
      host:
      username:
      password:
      port: 6379
      ssl:
      timeout:
      connectTimeout:
      clientName:
      clientType: lettuce  #lettuce,jedis
      sentinel:
        master:
        nodes:
        username:
        password:
      cluster:
        nodes:
        maxRedirects:
      jedis:
        pool:
          enabled:
          maxIdle: 8
          minIdle: 0
          maxActive: 8
          maxWait: -1
          timeBetweenEvictionRuns: 
      lettuce:
        pool:
        cluster:


四、基于缓存的分布式锁

主要还是使用Redisson提供的分布式锁功能来实现

@Configuration
public class RedissonConfig {

    // redissonCodes.  如:JsonJacksonCodec
    @Bean
    public RedissonClient redissonClient(Codec redissonCodec) {
    	Config config = new Config();
        // 排查config TODO
        
        config.setCodec(redissonCodec);
        return Redisson.create(config);
    }
}

使用示例

public void test(RedissonClient client){
 	String lockName = "xxxxx" ;
 	RLock lock = redissonClient.getLock(lockName);
  lock.lock();
  try {
    // TODO 
  }finally {
    lock.unlock();
  }
}


说明:可以通过redisson中提供的RedissonSpringCacheManager将redisson融入到spring cache中

 @Bean
 CacheManager cacheManager(RedissonClient redissonClient) throws IOException {
   return new RedissonSpringCacheManager(redissonClient, "classpath:/cache-config.json");
 }