SpringBoot缓存原理

三个注解

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

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

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

配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache.xsd">

    <cache:annotation-driven/>

    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="userCache"/>
                </bean>
            </set>
        </property>
    </bean>
</beans>

缓存底层

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

public class ConcurrentMapCache extends AbstractValueAdaptingCache {

	private final String name;

	private final ConcurrentMap<Object, Object> 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<CachePutRequest> 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来控制是代码执行前删除缓存,还是代码执行后删除。默认是代码执行后删除

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值