【Spring Cache】一 Cache CacheManager

前言

本系列主要想了解下 Spring Cache 的实现原理,结合部分源码及其细节,旨在使用 Spring Cache 时可以更加得心应手

先从 Cache CacheManager 了解起来

Cache

public interface Cache {

	// 缓存名称
	String getName();

	// 缓存真正负责缓存的对象
	Object getNativeCache();

	/**
	 * 获取 key 对应的 ValueWrapper
	 * 没有对应的 key 就返回 null
	 * key 对应的 v 是 null 的话返回的是 null 对应的 ValueWrapper
	 */
	@Nullable
	ValueWrapper get(Object key);

	/**
	 * 返回 key 对应 type 类型的 v
	 */
	@Nullable
	<T> T get(Object key, @Nullable Class<T> type);

	/**
	 * 如果有 key 对应的 v 就返回
	 * 如果没有就缓存 Callable::call 并返回
	 */
	@Nullable
	<T> T get(Object key, Callable<T> valueLoader);

	/**
	 * 缓存目标 kv(替换旧值),不保证实时性
	 */
	void put(Object key, @Nullable Object value);

	/**
	 * 立即插入缓存,默认基于先 get 不存在则 put 操作实现
	 */
	@Nullable
	default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
		ValueWrapper existingValue = get(key);
		if (existingValue == null) {
			put(key, value);
		}
		return existingValue;
	}

	/**
	 * 剔除缓存,不保证实时性
	 */
	void evict(Object key);

	/**
	 * 立即剔除缓存
	 * 返回 false 表示剔除前不存在指定 key 或不确定是否存在
	 * 返回 true 表示该 key 之前存在
	 */
	default boolean evictIfPresent(Object key) {
		evict(key);
		return false;
	}

	// 清除所有缓存,不保证实时性
	void clear();

	/**
	 * 立即清除所有缓存
	 * 返回 false 表示清除前没有缓存或不能确定是否有
	 * 返回 true 表示清除前有缓存
	 */
	default boolean invalidate() {
		clear();
		return false;
	}

	/**
	 * 缓存值的一个包装器接口
	 * 实现类 SimapleValueWrapper
	 */
	@FunctionalInterface
	interface ValueWrapper {

		@Nullable
		Object get();
	}


	// ...ValueRetrievalException

}

缓存的顶级接口,抽象了缓存的 get put evict 相关操作

NoOpCache

一个未作任何实际缓存操作的实现,应该主要是用来兼容不适应缓存的场景,细节略

AbstractValueAdaptingCache

public abstract class AbstractValueAdaptingCache implements Cache {

	// 是否允许 null 值
	private final boolean allowNullValues;

	protected AbstractValueAdaptingCache(boolean allowNullValues) {
		this.allowNullValues = allowNullValues;
	}

	// ...

	@Override
	@Nullable
	public ValueWrapper get(Object key) {

		// get 操作依赖 toValueWrapper
		return toValueWrapper(lookup(key));
	}

	@Override
	@SuppressWarnings("unchecked")
	@Nullable
	public <T> T get(Object key, @Nullable Class<T> type) {

		// 查询到的缓存值 fromStoreValue 转换
		Object value = fromStoreValue(lookup(key));

		// 转换后非 null 值无法类型转换则抛出异常
		if (value != null && type != null && !type.isInstance(value)) {
			throw new IllegalStateException(
					"Cached value is not of required type [" + type.getName() + "]: " + value);
		}
		return (T) value;
	}

	// 从缓存中获取 key 对应的 v,子类实现
	@Nullable
	protected abstract Object lookup(Object key);

	/**
	 * 对于从缓存中获取的值,允许为空且值为 NullValue 时,处理为 null
	 */
	@Nullable
	protected Object fromStoreValue(@Nullable Object storeValue) {
		if (this.allowNullValues && storeValue == NullValue.INSTANCE) {
			return null;
		}
		return storeValue;
	}

	/**
	 * 对于要插入缓存的 null 值,在允许 null 值的情况下处理为 NullValue
	 * 否则抛出异常 IllegalArgumentException
	 */
	protected Object toStoreValue(@Nullable Object userValue) {
		if (userValue == null) {
			if (this.allowNullValues) {
				return NullValue.INSTANCE;
			}
			throw new IllegalArgumentException(
					"Cache '" + getName() + "' is configured to not allow null values but null was provided");
		}
		return userValue;
	}

	/**
	 * get 操作基于此完成
	 * 查询到缓存值非 null 则 fromStoreValue 转换后包装成 SimpleValueWrapper 返回
	 */
	@Nullable
	protected Cache.ValueWrapper toValueWrapper(@Nullable Object storeValue) {
		return (storeValue != null ? new SimpleValueWrapper(fromStoreValue(storeValue)) : null);
	}

}

核心的抽象基类实现,主要抽象了对 NULL 值的处理逻辑:

  • boolean allowNullValues:属性 allowNullValues 定义是否允许处理 NULL 值缓存
  • fromStoreValue 方法主要处理 NULL 值的 get 操作相关,在允许 NULL 值缓存的情况下,处理 NullValuenull
  • toStoreValue 方法主要处理 NULL 值的 put 操作相关,在允许 NULL 值缓存的情况下,处理 nullNullValue,否则抛异常
  • toValueWrapper 方法主要提供 get 的默认实现,将缓存中读取缓存后 fromStoreValue 转换后包装成 SimpleValueWrapper 返回
  • ValueWrapper get(Object key)T get(Object key, @Nullable Class<T> type) 方法基于上述方法实现
  • 提供抽象方法 lookup 交给子类来获取真正的缓存值

ConcurrentMapCache

public class ConcurrentMapCache extends AbstractValueAdaptingCache {

	private final String name;

	// 基于 ConcurrentMap 缓存
	private final ConcurrentMap<Object, Object> store;

	// 如果要缓存的是值对象的 copy,则由此序列化代理类处理
	@Nullable
	private final SerializationDelegate serialization;

	// ...

	// 默认允许处理 null
	public ConcurrentMapCache(String name) {
		this(name, new ConcurrentHashMap<>(256), true);
	}

	// 默认 serialization = null
	public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {
		this(name, store, allowNullValues, null);
	}

	// serialization 不为空是缓存值对象的 copy
	public final boolean isStoreByValue() {
		return (this.serialization != null);
	}

	// ...

	// 实现 lookup:store#get
	@Override
	@Nullable
	protected Object lookup(Object key) {
		return this.store.get(key);
	}

	/**
	 * 基于 ConcurrentMap::computeIfAbsent 方法实现
	 * get 和 put 的值由 fromStoreValue 和 toStoreValue 处理 NULL
	 */
	@SuppressWarnings("unchecked")
	@Override
	@Nullable
	public <T> T get(Object key, Callable<T> valueLoader) {
		return (T) fromStoreValue(this.store.computeIfAbsent(key, k -> {
			try {
				return toStoreValue(valueLoader.call());
			}
			catch (Throwable ex) {
				throw new ValueRetrievalException(key, valueLoader, ex);
			}
		}));
	}

	// ...

}

Spring 内置的基于 ConcurrentMap 的缓存实现

  • 支持对缓存值对象 copy 的缓存,由 SerializationDelegate serialization 处理序列化,默认为 null 即基于引用的缓存
  • 缓存相关操作基于基类 AbstractValueAdaptingCachenull 值处理,默认允许为 null
demo
public class ConcurrentMapCacheDemo {

    @Test
    public void test() {
        ConcurrentMapCache cache = new ConcurrentMapCache("test");
        cache.put("1", "a");
        cache.get("2", () -> null);
        System.out.println(cache.get("1").get());
        System.out.println(cache.get("2").get());

        cache.clear();
        System.out.println(cache.get("1"));
        System.out.println(cache.get("2"));
    }
}

写个 demo 加深印象

CacheManager

public interface CacheManager {

	@Nullable
	// 获取指定 name 的 Cache,可能延迟创建
	Cache getCache(String name);

	// 获取当前 CacheManager 下的 cache name 集合
	Collection<String> getCacheNames();

}

CacheManager 基于 name 管理一组 Cache

NoOpCacheManager

基于 name 维护一组 NoOpCache,细节略

ConcurrentMapCacheManager

public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware {

	private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);

	// 是否动态创建缺省 Cache
	private boolean dynamic = true;

	// 是否允许空值
	private boolean allowNullValues = true;

	// 是否缓存值对象的副本
	private boolean storeByValue = false;

	// 序列化辅助类
	@Nullable
	private SerializationDelegate serialization;

	public ConcurrentMapCacheManager() {
	}

	/**
	 * 如果指定一个非 null 的 cacheNames
	 * 这里就会基于此创建对应的 静态缓存,即不会再创建新的缺省缓存了
	 */
	public ConcurrentMapCacheManager(String... cacheNames) {
		setCacheNames(Arrays.asList(cacheNames));
	}

	// 基于传入的 cacheNames 创建静态缓存,之后就不允许创建缺省缓存了
	public void setCacheNames(@Nullable Collection<String> cacheNames) {
		if (cacheNames != null) {
			for (String name : cacheNames) {
				this.cacheMap.put(name, createConcurrentMapCache(name));
			}
			this.dynamic = false;
		}
		else {
			this.dynamic = true;
		}
	}

	// ...

	@Override
	public Collection<String> getCacheNames() {
		return Collections.unmodifiableSet(this.cacheMap.keySet());
	}

	@Override
	@Nullable
	public Cache getCache(String name) {
		Cache cache = this.cacheMap.get(name);

		/**
		 * 如果允许创建缺省缓存,则基于 createConcurrentMapCache 创建
		 */
		if (cache == null && this.dynamic) {
			synchronized (this.cacheMap) {
				cache = this.cacheMap.get(name);
				if (cache == null) {
					cache = createConcurrentMapCache(name);
					this.cacheMap.put(name, cache);
				}
			}
		}
		return cache;
	}

	// 基于配置属性创建对应的 ConcurrentMapCache
	protected Cache createConcurrentMapCache(String name) {
		SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
		return new ConcurrentMapCache(name, new ConcurrentHashMap<>(256), isAllowNullValues(), actualSerialization);
	}

}

根据类名可以看出,它是用来管理一组 ConcurrentMapCacheCacheManager

  • 它提供了一个有意思的属性 boolean dynamic,意味着是否允许创建缺省缓存,即目标 name 的缓存不存在时创建对应的 ConcurrentMapCache
  • 提供了构造方法 ConcurrentMapCacheManager(String... cacheNames),意味着基于传入的 cacheNames 构造一组静态缓存,此后就不能在创建缺省缓存了
  • getCache 方法允许在获取不到指定缓存的情况下,根据属性 boolean dynamic 决定是否创建缺省缓存
  • 创建缺省缓存方法 createConcurrentMapCache:基于配置属性创建对应的 ConcurrentMapCache

demo

public class ConcurrentMapCacheManagerDemo {

    @Test
    public void test() {
        ConcurrentMapCacheManager cacheManager
                = new ConcurrentMapCacheManager("test1", "test2");
        cacheManager.getCache("test1")
                .get("1", () -> "a");
        
        // 指定了 cacheNames,无法创建缺省缓存,NPE
        cacheManager.getCache("test3")
                .get("1", () -> "b");
        
    }
}

示例中指定了 cacheNames,因此无法创建缺省缓存

CompositeCacheManager

Spring 常用的 设计模式 —— 组合模式,其下可以管理一组 CacheManager,其 getCache 方法返回查到的第一个 Cache,细节略

AbstractCacheManager

public abstract class AbstractCacheManager implements CacheManager, InitializingBean {

	// 基于 ConcurrentMap 管理缓存
	private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);

	// 基于 Set 管理 cache name
	private volatile Set<String> cacheNames = Collections.emptySet();

	// 是个 InitializingBean,生命周期后期调用 afterPropertiesSet 方法
	@Override
	public void afterPropertiesSet() {
		initializeCaches();
	}

	public void initializeCaches() {

		// 加载所有 Cache,由子类实现
		Collection<? extends Cache> caches = loadCaches();

		/**
		 * 基于上述 Cache 维护
		 * cacheMap:name -> cache
		 * cacheNames
		 */
		synchronized (this.cacheMap) {
			this.cacheNames = Collections.emptySet();
			this.cacheMap.clear();
			Set<String> cacheNames = new LinkedHashSet<>(caches.size());
			for (Cache cache : caches) {
				String name = cache.getName();
				this.cacheMap.put(name, decorateCache(cache));
				cacheNames.add(name);
			}
			this.cacheNames = Collections.unmodifiableSet(cacheNames);
		}
	}

	// 加载缓存由子类实现
	protected abstract Collection<? extends Cache> loadCaches();

	@Override
	@Nullable
	public Cache getCache(String name) {
	
		Cache cache = this.cacheMap.get(name);
		if (cache != null) {
			return cache;
		}

		/**
		 * 如果没找到对应的 cache,这个地方允许基于 getMissingCache
		 * 		构造一个对应的缺省 cache
		 */
		Cache missingCache = getMissingCache(name);
		if (missingCache != null) {
			// double check 同步检查
			synchronized (this.cacheMap) {
				cache = this.cacheMap.get(name);
				if (cache == null) {
					cache = decorateCache(missingCache);
					this.cacheMap.put(name, cache);
					updateCacheNames(name);
				}
			}
		}
		return cache;
	}

	// 构造缺省 Cache
	@Nullable
	protected Cache getMissingCache(String name) {
		return null;
	}

	// ...

}

AbstractCacheManager ,通用的实现基类,它可以维护一组不同的 Cache

  • 它是一个 InitializingBean,在 afterPropertiesSet 阶段加载所有的 Cache(子类实现),基于此维护对应的 cacheMapcacheNames
  • loadCaches 方法由子类实现,加载对应的 Caches
  • getCache 方法在获取不到对应的 Cache 时,允许基于 getMissingCache 方法创建缺省 Cache(默认 null

SimpleCacheManager

public class SimpleCacheManager extends AbstractCacheManager {

	private Collection<? extends Cache> caches = Collections.emptySet();

	public void setCaches(Collection<? extends Cache> caches) {
		this.caches = caches;
	}
	
	@Override
	protected Collection<? extends Cache> loadCaches() {
		return this.caches;
	}

}

AbstractCacheManager 的默认实现,loadCaches 方法返回指定的 Cache 集合

demo
public class ExtendSimpleCacheManagerDemo {

    // 拓展 SimpleCacheManager 提供了缺省缓存实现:new ConcurrentMapCache(name)
    public static class ExtendSimpleCacheManager extends SimpleCacheManager {

        @Override
        protected Cache getMissingCache(String name) {

            return new ConcurrentMapCache(name);
        }
    }

    @Configuration
    public static class Config {

        @Bean
        public CacheManager cacheManager() {
            ExtendSimpleCacheManager cacheManager
                    = new ExtendSimpleCacheManager();

            // 创建一个默认缓存
            ConcurrentMapCache cache = new ConcurrentMapCache("test");
            cache.put("1", "a");
            List<Cache> caches = new ArrayList<Cache>() {{
                add(cache);
                // ...
            }};
            cacheManager.setCaches(caches);
            
            return cacheManager;
        }
    }

    @Test
    public void test() {
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(Config.class);
        CacheManager bean = applicationContext.getBean(CacheManager.class);

        // 第一个缓存是被 loadCaches 的
        System.out.println(bean.getCache("test")
                .get("1").get());

        // 创建了缺省缓存
        bean.getCache("test2")
                .put("1", "b");
        
        bean.getCacheNames()
                .forEach(System.out::println);
    }
}
  • 该示例中拓展了 SimpleCacheManagergetMissingCache 方法来提供一个缺省 Cache
  • 容器中注册该实例,并提供一个默认缓存 test
  • 测试代码读取 test 的缓存值并创建缺省缓存 test2

总结

源码结合示例,了解了 Spring Cache 定义的两个基类接口

  • Cache,抽象缓存相关的操作
  • CacheManager,管理一组 Caches

下一篇:【Spring Cache】二 Spring 缓存操作相关注解及属性

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringCacheSpring框架中用于缓存数据的一个功能模块。在Spring 3.1版本之后,Spring引入了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术,并且支持使用JCache(JSR-107)注解来简化开发。 SpringCache支持多种类型的缓存,可以轻松地与SpringBoot集成,只需引入spring-boot-starter-cache依赖即可。 在使用SpringCache时,可以使用自动配置或手动配置来配置缓存管理器。自动配置可以通过RedisCacheConfiguration类来自动配好缓存管理器,配置示例如下: ```java @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader())); List<String> cacheNames = this.cacheProperties.getCacheNames(); if (!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet<>(cacheNames)); } return this.customizerInvoker.customize(builder.build()); } ``` 总结起来,SpringCache是一个用于缓存数据的功能模块,可以方便地与Spring框架和SpringBoot集成,并提供了自动配置和手动配置两种方式来配置缓存管理器。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [SpringCache](https://blog.csdn.net/weixin_47409774/article/details/123546325)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值