springboot缓存之CacheManager详解

这篇具有很好参考价值的文章主要介绍了springboot缓存之CacheManager详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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配置中的这些。文章来源地址https://www.toymoban.com/news/detail-626142.html

到了这里,关于springboot缓存之CacheManager详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SpringBoot篇】使用Spring Cache高效处理缓存数据

    Spring Cache是一个框架,只要简单加一个注解,就能实现缓存功能。Spring Cache是Spring Framework提供的一个模块,它为应用程序添加了缓存支持。通过使用Spring Cache,你可以在方法级别上定义缓存规则,将方法的返回结果缓存起来,以提高方法调用的性能和响应速度。 是一个框架,只要简

    2024年02月05日
    浏览(59)
  • 【分布式缓存】springboot整合jetcache使用详解

    目录 一、前言 二、多级缓存问题 2.1 缓存分类 2.1.1 本地缓存 2.1.2 分布式缓存

    2024年02月17日
    浏览(45)
  • Spring Boot 3.0系列【25】数据篇之Spring Cache缓存技术使用详解

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.5 源码地址:https://gitee.com/pearl-organization/study-spring-boot3

    2023年04月14日
    浏览(36)
  • 【springboot】Spring Cache缓存:

    一、导入Maven依赖: 二、实现思路: 三、代码开发:

    2024年02月11日
    浏览(46)
  • springboot2入门到实战-spring缓存

    启动缓存@EnableCaching @Cacheable @Cacheable 注解就可以将运行结果缓存,以后查询相同的数据,直接从缓存中取,不需要调用方法。 序列 参数 解释 1 cacheNames 指定缓存组件的名字 2 key 缓存数据时使用的key,默认使用方法参数 3 keyGenerator key 的生成器。 key 和 keyGenerator 二选一使用

    2024年02月22日
    浏览(41)
  • Spring三级缓存详解

    Spring三级缓存 是为了解决 对象间的循环依赖 问题。 A依赖B,B依赖A,这就是一个简单的循环依赖。 我们来先看看三级缓存的源码。 (1)查看“获取Bean”的源码,注意getSingleton()方法。 (2)“添加到第1级缓存”的源码: (3)“添加到第3级缓存”的源码: (4)“创建Be

    2024年02月07日
    浏览(63)
  • Spring——三级缓存解决循环依赖详解

    就是在Bean生成流程中保存Bean对象三种形态的三个Map集合,如下: 用来解决什么问题? 这个大家应该熟知了,就是循环依赖 什么是循环依赖? 就像下面这样,AService 中注入了BService ,而BService 中又注入了AService ,这就是循环依赖 这几个问题我们结合源码来一起看一下 : 三级

    2024年02月03日
    浏览(43)
  • [Spring] 三级缓存解决循环依赖详解

    注册一个bean对象的过程: Spring扫描class得到BeanDefinition – 根据得到的BeanDefinition去生成bean – 现根据class推断构造方法 – 根据推断出来的构造方法,反射,得到一个对象 – 填充初始对象中的属性(依赖注入) – 如果原始对象种的某个方法被AOP了,那么要根据原始对象生成一

    2024年02月15日
    浏览(42)
  • 【SpringBoot3】Spring Boot 3.0 集成 Redis 缓存

    Redis缓存是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它主要用于作为数据库、缓存和消息中间件,以快速读写和丰富的数据结构支持而著称。 在应用程序和数据库之间,Redis缓存作为一个中间层起着关键

    2024年02月21日
    浏览(56)
  • 【Spring】Springboot过滤器Filter和拦截器Inteceptor详解及使用场景

    Springboot过滤器Filter和拦截器Inteceptor详解及使用场景

    2024年02月13日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包