缓存-Spring Cache 缓存抽象
Spring从版本3.1开始提供非侵入的将Cache集成到Spring应用的方式。Spring Cache提供Cache的统一抽象,支持集成各种不同的缓存解决方案。从4.1版本开始,提供了注解和更多的定制参数。
Spring Cache 抽象提供了对Java方法的缓存,使用Spring Cache缓存,当方法调用使用相同的参数,调用结果被缓存下来,当方法被再次调用时将直接返回参数结果。
关键类和接口
Cache
org.springframework.cache.Cache 提供Spring框架中使用的Cache的统一抽象功能, 具体的实现类使用实际的缓存解决方案。
- NoOpCache: 空缓存,没有实际的缓存实现
- EhCacheCache: 使用EhCache缓存方案。
- CaffeineCache: 使用Java CaffeineCache。Caffeine提出了一种更高效的近似LFU准入策略的缓存结构TinyLFU及其变种W-TinyLFU,借鉴Guava Cache的设计经验,是一款功能强大,性能更优本地缓存。
- JCacheCache: Javax Cache 标准接口,类似于Spring Cache的 JavaJ JSR缓存规范。
- ConcurrentMapCache: 使用ConccrentMap的缓存实现。
- TransactionAwareCacheDecorator: 识别Spring 事务的缓存实现装饰模式,只要当事务提交时才触发缓存的更新。
CacheManager
org.springframework.cache.CacheManager:Spring cache 管理API。不同的CacheManager实现提供对相应Cache实现缓存的管理,通过管理API Cache getCache(String name);方法对外提供。
CacheResolver
org.springframework.cache.interceptor.CacheResolver 方法调用拦截使用来获取Cache实现的Resolver,通常使用相应的CacheManager实现类来获取Cache。
KeyGenerator
org.springframework.cache.interceptor. KeyGenerator:缓存中Key的生成实现。默认的key生成器:org.springframework.cache.interceptor.SimpleKeyGenerator 类
-
方法没有参数则使用SimpleKey.EMPTY
-
方法只有一个参数:key=唯一参数
-
方法有多个参数:SimpleKey(params)
可以通过实现org.springframework.cache.interceptor.KeyGenerator接口定制key生成器。
使用Spring Cache
启用Spring Cache
@Configuration
@EnableCaching
注解使用
**@Cacheable:**作用于方法之上,配置方法缓存
@Cacheable("cacheName"): 指定用于缓存结果的缓存名称,当指定多个名称时,方法调用将在多个缓存中查找相同参数的方法调用,命中即返回。
Cache 的key
指定KeyGenerator:
@Cacheable(cacheNames="XXX", keyGenerator="myKeyGenerator")。
通过SpEL指定key
@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
@CachePut: @CachePut指定的方法不影响方法的执行,方法执行结果将用于更新缓存。@CachePut支持同@Cacheable一样的配置。
示例:
@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
**@CacheEvict:**触发缓存清理陈旧或无用的数据。可以配置指定数据的清理或Cache范围的清理。
示例:
@CacheEvict(cacheNames="books", allEntries=true)
public void loadBooks(InputStream batch)
@Caching: 在一个方法上指定触发缓存的多个操作。
示例:
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)
**@CacheConfig:**以上的注解都是方法级别的注解,@CacheConfig提供类级别的公共配置,该类下的方法级别配置如无指定则使用类级别上的@CacheConfig。
实现方式
Spring AOP
使用 Spring AOP切面实现Cache相关方法的拦截获取缓存和Cache请求的处理
- 配置切面: 所有所有类和所有方法都进行拦截
org.springframework.cache.interceptor.CacheProxyFactoryBean
相关代码:
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| private Pointcut pointcut = Pointcut.TRUE; protected Object createMainInterceptor() { this.cacheInterceptor.afterPropertiesSet(); return new DefaultPointcutAdvisor(this.pointcut, this.cacheInterceptor); } |
- org.springframework.cache.interceptor.CacheInterceptor 实现
org.springframework.cache.interceptor.MethodInterceptor接口,
CacheAspectSupport.execute方法是Spring Cache核心处理流程,下面是主要流程的部分代码解析:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 处理CacheEvict请求 processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT); // 从缓存中查询是否存在缓存 Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); // 如未命中,生成CachePut请求:缓存条件满足 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)) { // 缓存命中,并且没有Put需求,返回缓存值 cacheValue = cacheHit.get(); returnValue = wrapCacheValue(method, cacheValue); } else { // 未命中,调用对象,获取可缓存值 returnValue = invokeOperation(invoker); cacheValue = unwrapReturnValue(returnValue); } // 检查是否存在CachePut请求 collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests); // 缓存更新 for (CachePutRequest cachePutRequest : cachePutRequests) { cachePutRequest.apply(cacheValue); } // 处理缓存Evicts请求 rocessCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue); return returnValue; } |