栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot缓存原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot缓存原理

三个注解

@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。

@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

配置



    

    
        
            
                
                    
                
            
        
    

缓存底层

可以看出Cacheable用的是SimpleCacheManager(缓存管理),里面放了一个缓存容器caches,spring默认提供了一个缓存工厂ConcurrentMapCacheFactoryBean
里面实际使用的缓存容器是:ConcurrentMapCache

public class ConcurrentMapCache extends AbstractValueAdaptingCache {

	private final String name;

	private final ConcurrentMap store;

ConcurrentMapCache就是简单封装了一层ConcurrentMap,并没有提供额外优秀的功能(定时删除、容量限制…)

切面原理

路线:mvc -> spring-aop -> cglib -> spring-cache
spring-cache之前都是通用知识,不在本文描述范围内,从spring-cache开始
核心代码:org.springframework.cache.interceptor.CacheAspectSupport#execute

private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
	...
	// Process any early evictions
	processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
			CacheOperationexpressionevaluator.NO_RESULT);

	// Check if we have a cached item matching the conditions
	Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

	// Collect puts from any @Cacheable miss, if no cached item is found
	List cachePutRequests = new ArrayList<>();
	if (cacheHit == null) {
		collectPutRequests(contexts.get(CacheableOperation.class),
				CacheOperationexpressionevaluator.NO_RESULT, cachePutRequests);
	}

	Object cachevalue;
	Object returnValue;

	if (cacheHit != null && !hasCachePut(contexts)) {
		// If there are no put requests, just use the cache hit
		cachevalue = cacheHit.get();
		returnValue = wrapCachevalue(method, cachevalue);
	}
	else {
		// Invoke the method if we don't have a cache hit
		returnValue = invokeOperation(invoker);
		cachevalue = unwrapReturnValue(returnValue);
	}

	// Collect any explicit @CachePuts
	collectPutRequests(contexts.get(CachePutOperation.class), cachevalue, cachePutRequests);

	// Process any collected put requests, either from @CachePut or a @Cacheable miss
	for (CachePutRequest cachePutRequest : cachePutRequests) {
		cachePutRequest.apply(cachevalue);
	}

	// Process any late evictions
	processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cachevalue);

	return returnValue;
}
  1. 调用processCacheEvicts先看方法是否有@CacheEvict,进行缓存清除。
  2. 调用findCachedItem查询缓存是否有数据
  3. 如果没有缓存数据
    a)收集该方法的@Cacheable(可以有多个)
    b)调用invokeOperation执行用户方法
  4. 如果有缓存数据
    a)如果没有@CachePut,直接调用get方法把结果拿到,因为spring为了做懒处理,返回的是一个Supplier
    b)如果有@CachePut,还是要执行用户的方法invokeOperation
    注意:这里wrapCachevalue、unwrapReturnValue都是为了适配Optional返回值,不用关心
  5. collectPutRequests是收集所有的@CachePut(可以有多个)
  6. 上面收集了@Cacheable + @CachePut,然后调用cachePutRequest.apply把结果再放入缓存
  7. 最终检查是否有@CacheEvict,进行缓存清除

注意:缓存清除开始执行了一次,结束又执行了一次,是因为@CacheEvict用beforeInvocation来控制是代码执行前删除缓存,还是代码执行后删除。默认是代码执行后删除

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/658061.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号