spring-cache
spring 提供了spring-cache上层接口给大家实现,其中有一些方便操作缓存的注解,诸如@Cacheable、@CacheEvict等等。今天就来学习一下redis的实现 spring-data-redis.
配置缓存需要配置一个CacheManager
public interface CacheManager {
/**
* Get the cache associated with the given name.
* <p>Note that the cache may be lazily created at runtime if the
* native provider supports it.
* @param name the cache identifier (must not be {@code null})
* @return the associated cache, or {@code null} if such a cache
* does not exist or could be not created
*/
@Nullable
Cache getCache(String name);
/**
* Get a collection of the cache names known by this manager.
* @return the names of all caches known by the cache manager
*/
Collection<String> getCacheNames();
}
这个顶层接口有一个抽象类AbstractCacheManager
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
// Early cache initialization on startup
@Override
public void afterPropertiesSet() {
initializeCaches();
}
/**
* Initialize the static configuration of caches.
* <p>Triggered on startup through {@link #afterPropertiesSet()};
* can also be called to re-initialize at runtime.
* @since 4.2.2
* @see #loadCaches()
*/
public void initializeCaches() {
Collection<? extends Cache> caches = loadCaches();
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);
}
}
/**
* Load the initial caches for this cache manager.
* <p>Called by {@link #afterPropertiesSet()} on startup.
* The returned collection may be empty but must not be {@code null}.
*/
protected abstract Collection<? extends Cache> loadCaches();
// Lazy cache initialization on access
@Override
@Nullable
public Cache getCache(String name) {
// Quick check for existing cache...
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// The provider may support on-demand cache creation...
Cache missingCache = getMissingCache(name);
if (missingCache != null) {
// Fully synchronize now for missing cache registration
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = decorateCache(missingCache);
this.cacheMap.put(name, cache);
updateCacheNames(name);
}
}
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return this.cacheNames;
}
// Common cache initialization delegates for subclasses
/**
* Check for a registered cache of the given name.
* In contrast to {@link #getCache(String)}, this method does not trigger
* the lazy creation of missing caches via {@link #getMissingCache(String)}.
* @param name the cache identifier (must not be {@code null})
* @return the associated Cache instance, or {@code null} if none found
* @since 4.1
* @see #getCache(String)
* @see #getMissingCache(String)
*/
@Nullable
protected final Cache lookupCache(String name) {
return this.cacheMap.get(name);
}
/**
* Dynamically register an additional Cache with this manager.
* @param cache the Cache to register
* @deprecated as of Spring 4.3, in favor of {@link #getMissingCache(String)}
*/
@Deprecated
protected final void addCache(Cache cache) {
String name = cache.getName();
synchronized (this.cacheMap) {
if (this.cacheMap.put(name, decorateCache(cache)) == null) {
updateCacheNames(name);
}
}
}
/**
* Update the exposed {@link #cacheNames} set with the given name.
* <p>This will always be called within a full {@link #cacheMap} lock
* and effectively behaves like a {@code CopyOnWriteArraySet} with
* preserved order but exposed as an unmodifiable reference.
* @param name the name of the cache to be added
*/
private void updateCacheNames(String name) {
Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames);
cacheNames.add(name);
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
// Overridable template methods for cache initialization
/**
* Decorate the given Cache object if necessary.
* @param cache the Cache object to be added to this CacheManager
* @return the decorated Cache object to be used instead,
* or simply the passed-in Cache object by default
*/
protected Cache decorateCache(Cache cache) {
return cache;
}
/**
* Return a missing cache with the specified {@code name}, or {@code null} if
* such a cache does not exist or could not be created on demand.
* <p>Caches may be lazily created at runtime if the native provider supports it.
* If a lookup by name does not yield any result, an {@code AbstractCacheManager}
* subclass gets a chance to register such a cache at runtime. The returned cache
* will be automatically added to this cache manager.
* @param name the name of the cache to retrieve
* @return the missing cache, or {@code null} if no such cache exists or could be
* created on demand
* @since 4.1
* @see #getCache(String)
*/
@Nullable
protected Cache getMissingCache(String name) {
return null;
}
}
各个厂商都会提供一个对应的CacheManager
实现这个抽象类,例如redis的RedisCacheManager
.
首先我们对AbstractCacheManager
进行解读,看看里面都有些什么操作。
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
这两个成员变量存的是缓存map和缓存名称。
@Override
public void afterPropertiesSet() {
initializeCaches();
}
实现了InitializingBean
在装在这个bean的时候调用initializeCaches();
方法进行初始化。下面看一下这个方法。
public void initializeCaches() {
// 调用抽象方法loadCaches();获取缓存
Collection<? extends Cache> caches = loadCaches();
// 线程安全的初始化成员变量cacheMap和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();
// 装饰之后放入map中
this.cacheMap.put(name, decorateCache(cache));
// 将缓存名称放入cacheNames
cacheNames.add(name);
}
// 将cacheNames设置成不可修改的set集合
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
}
看一下初始化的缓存从哪里来的。看看这个方法的定义。
protected abstract Collection<? extends Cache> loadCaches();
看一下RedisCacheManager
中的实现。
@Override
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList<>();
// 从配置中获取配置的缓存
for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
// 封装成RedisCache放在集合里
caches.add(createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
这里的initialCacheConfiguration
的定义如下:
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
左边的String是缓存名称。
我们在配置文件中配置cache-names等等最终就会放在这个initialCacheConfiguration
里面。具体放进去的过程下次写整合springCache的redis实现的时候介绍他的自动装配过程以及redis的缓存实现。配置文件中比如这样写:
#配置spring-cache
spring:
cache:
# 缓存使用的实现是redis
type: redis
# 初始化缓存的两个名称
#如果这里配置了那系统里@Cacheable注解的缓存就只能是这些缓存,
#没有配置就是自动动态创建 下文会介绍
cache-names: 'demo,test'
#对redis的一些配置
redis:
#自动生成key的前缀
key-prefix: 'tabtan:'
#默认过期时间
time-to-live: 3600000
这其中cache-names
中的每一个缓存名称就会作为initialCacheConfiguration
Map的键。根据一些其他的配置封装成的缓存就会成为对应的值。
回过头来再看初始化方法initializeCaches()
开头的loadCaches()
方法。
Collection<? extends Cache> caches = loadCaches();
redis的实现把你配置的caches
封装成了List<RedisCache>
返回到了这里。
至此初始化就完成。(RedisCache
实现了Cache
)
在说其他方法之前,我想说这种具体实现延迟到子类的方式是十分常用的,可以借鉴学习。
下面开始介绍CacheManager
接口的第一个方法Cache getCache(String name);
看一下接口中如何定义
/**
* Get the cache associated with the given name.
* 获取与给定名称关联的缓存。
* <p>Note that the cache may be lazily created at runtime if the
* native provider supports it.
* @param name the cache identifier (must not be {@code null})
* @param name 缓存名称 不能为空
* @return the associated cache, or {@code null} if such a cache
* does not exist or could be not created
*/
@Nullable
Cache getCache(String name);
看一下抽象类中的实现
@Override
@Nullable
public Cache getCache(String name) {
// 获取初始化后的cacheMap看看有没有这个缓存 有直接返回 没有就getMissingCache(name)动态创建
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// The provider may support on-demand cache creation...
// 上边的英文注释意思大致是 实现可能支持按需创建缓存
//我们今天看的是redis实现 是支持动态按需创建的
// 同样这个getMissingCache提供给子类实现 但不是抽象方法。
Cache missingCache = getMissingCache(name);
// 判断一下getMissingCache(name)之后是不是空 空的话就返回空的
if (missingCache != null) {
// Fully synchronize now for missing cache registration
// 上锁,防止注册丢失
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
// double check
if (cache == null) {
// 装饰好按需创建的
cache = decorateCache(missingCache);
// 放到map里
this.cacheMap.put(name, cache);
// 更新缓存的名字 这个方法是借口的第二个方法
updateCacheNames(name);
}
}
}
return cache;
}
看一下getMissingCache(name)
的定义。
@Nullable
protected Cache getMissingCache(String name) {
return null;
}
默认返回null,意思就是不支持按需创建缓存,没有把这个方法定义成抽象方法的原因就在这里,如果具体实现不想支持按需创建缓存的话就不需要实现这个方法。redis是支持的我们看一下具体实现。
@Override
protected RedisCache getMissingCache(String name) {
// 默认支持动态创建就createRedisCache(name, defaultCacheConfig)创建 如果配置关闭了动态创建就直接
return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
}
关闭动态创建的配置方法为disableCreateOnMissingCache()
文章来源:https://www.toymoban.com/news/detail-626142.html
public RedisCacheManagerBuilder disableCreateOnMissingCache() {
this.allowInFlightCacheCreation = false;
return this;
}
值得关注的是,这个关闭动态创建缓存的方法只在RedisCacheManagerBuilder
能配置,配置文件里没有 你想配置的话可以自己写。调用这个方法组合cache-names
可以限制系统中的缓存名称只能是在cache-names
配置中的这些。文章来源地址https://www.toymoban.com/news/detail-626142.html
到了这里,关于springboot缓存之CacheManager详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!