• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

springboot缓存:CacheManager

武飞扬头像
好饿啊早知道不学java了
帮助3

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中的每一个缓存名称就会作为initialCacheConfigurationMap的键。根据一些其他的配置封装成的缓存就会成为对应的值。
回过头来再看初始化方法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()

public RedisCacheManagerBuilder disableCreateOnMissingCache() {

	this.allowInFlightCacheCreation = false;
	return this;
}

值得关注的是,这个关闭动态创建缓存的方法只在RedisCacheManagerBuilder能配置,配置文件里没有 你想配置的话可以自己写。调用这个方法组合cache-names可以限制系统中的缓存名称只能是在cache-names配置中的这些。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhhgai
系列文章
更多 icon
同类精品
更多 icon
继续加载