小編給大家分享一下Spring Boot緩存源碼是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
項目里面要增加一個應用緩存,原本想著要怎么怎么來整合ehcache和springboot,做好準備配置這個配置那個,結果只需要做三件事:
pom依賴
寫好一個ehcache的配置文件
在boot的application上加上注解@EnableCaching.
這就完事了,是不是很魔幻。
pom依賴
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.5</version>
</dependency>配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!-- 設定緩存的默認數據過期策略 -->
<defaultCache
maxElementsInMemory="500"
maxElementsOnDisk="2000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="90"
timeToLiveSeconds="300"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="300"/>
</ehcache>應用上加上EnableCaching注解
@SpringBootApplication
@EnableCaching
public class EhCacheApplication {
public static void main(String[] args) {
SpringApplication.run(EhCacheApplication.class, args);
}
}然后就可以在代碼里面使用cache注解了,像這樣。
@CachePut(value = "fish-ehcache", key = "#person.id")
public Person save(Person person) {
System.out.println("為id、key為:" + person.getId() + "數據做了緩存");
return person;
}
@CacheEvict(value = "fish-ehcache")
public void remove(Long id) {
System.out.println("刪除了id、key為" + id + "的數據緩存");
}
@Cacheable(value = "fish-ehcache", key = "#person.id")
public Person findOne(Person person) {
findCount.incrementAndGet();
System.out.println("為id、key為:" + person.getId() + "數據做了緩存");
return person;
}很方便對不對。下面,我們就來挖一挖,看看spring是怎么來做到的。主要分成兩部分,一是啟動的時候做了什么,二是運行的時候做了什么,三是和第三方緩存組件的適配
啟動的時候做了什么、
這個得從@EnableCaching標簽開始,在使用緩存功能時,在springboot的Application啟動類上需要添加注解@EnableCaching,這個標簽引入了
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default 2147483647;
}引入了CachingConfigurationSelector類,這個類便開啟了緩存功能的配置。這個類添加了AutoProxyRegistrar.java,ProxyCachingConfiguration.java兩個類。
AutoProxyRegistrar : 實現了ImportBeanDefinitionRegistrar接口。這里看不懂,還需要繼續學習。
ProxyCachingConfiguration : 是一個配置類,生成了BeanFactoryCacheOperationSourceAdvisor,CacheOperationSource,和CacheInterceptor這三個bean。
CacheOperationSource封裝了cache方法簽名注解的解析工作,形成CacheOperation的集合。CacheInterceptor使用該集合過濾執行緩存處理。解析緩存注解的類是SpringCacheAnnotationParser,其主要方法如下
/**
由CacheOperationSourcePointcut作為注解切面,會解析
SpringCacheAnnotationParser.java
掃描方法簽名,解析被緩存注解修飾的方法,將生成一個CacheOperation的子類并將其保存到一個數組中去
**/
protected Collection<CacheOperation> parseCacheAnnotations(SpringCacheAnnotationParser.DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = null;
//找@cacheable注解方法
Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
if (!cacheables.isEmpty()) {
ops = this.lazyInit(ops);
Iterator var5 = cacheables.iterator();
while(var5.hasNext()) {
Cacheable cacheable = (Cacheable)var5.next();
ops.add(this.parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
}
//找@cacheEvict注解的方法
Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
if (!evicts.isEmpty()) {
ops = this.lazyInit(ops);
Iterator var12 = evicts.iterator();
while(var12.hasNext()) {
CacheEvict evict = (CacheEvict)var12.next();
ops.add(this.parseEvictAnnotation(ae, cachingConfig, evict));
}
}
//找@cachePut注解的方法
Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
if (!puts.isEmpty()) {
ops = this.lazyInit(ops);
Iterator var14 = puts.iterator();
while(var14.hasNext()) {
CachePut put = (CachePut)var14.next();
ops.add(this.parsePutAnnotation(ae, cachingConfig, put));
}
}
Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
if (!cachings.isEmpty()) {
ops = this.lazyInit(ops);
Iterator var16 = cachings.iterator();
while(var16.hasNext()) {
Caching caching = (Caching)var16.next();
Collection<CacheOperation> cachingOps = this.parseCachingAnnotation(ae, cachingConfig, caching);
if (cachingOps != null) {
ops.addAll(cachingOps);
}
}
}
return ops;
}解析Cachable,Caching,CachePut,CachEevict 這四個注解對應的方法都保存到了Collection<CacheOperation> 集合中。
執行方法時做了什么
執行的時候,主要使用了CacheInterceptor類。
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
public CacheInterceptor() {
}
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
public Object invoke() {
try {
return invocation.proceed();
} catch (Throwable var2) {
throw new ThrowableWrapper(var2);
}
}
};
try {
return this.execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
} catch (ThrowableWrapper var5) {
throw var5.getOriginal();
}
}
}這個攔截器繼承了CacheAspectSupport類和MethodInterceptor接口。其中CacheAspectSupport封裝了主要的邏輯。比如下面這段。
/**
CacheAspectSupport.java
執行@CachaEvict @CachePut @Cacheable的主要邏輯代碼
**/
private Object execute(final CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) {
if (contexts.isSynchronized()) {
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
if (this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
Cache cache = (Cache)context.getCaches().iterator().next();
try {
return this.wrapCacheValue(method, cache.get(key, new Callable<Object>() {
public Object call() throws Exception {
return CacheAspectSupport.this.unwrapReturnValue(CacheAspectSupport.this.invokeOperation(invoker));
}
}));
} catch (ValueRetrievalException var10) {
throw (ThrowableWrapper)var10.getCause();
}
} else {
return this.invokeOperation(invoker);
}
} else {
/**
執行@CacheEvict的邏輯,這里是當beforeInvocation為true時清緩存
**/
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
//獲取命中的緩存對象
ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
List<CacheAspectSupport.CachePutRequest> cachePutRequests = new LinkedList();
if (cacheHit == null) {
//如果沒有命中,則生成一個put的請求
this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
/**
如果沒有獲得緩存對象,則調用業務方法獲得返回對象,hasCachePut會檢查exclude的情況
**/
if (cacheHit != null && cachePutRequests.isEmpty() && !this.hasCachePut(contexts)) {
cacheValue = cacheHit.get();
returnValue = this.wrapCacheValue(method, cacheValue);
} else {
returnValue = this.invokeOperation(invoker);
cacheValue = this.unwrapReturnValue(returnValue);
}
this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
Iterator var8 = cachePutRequests.iterator();
while(var8.hasNext()) {
CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next();
/**
執行cachePut請求,將返回對象放到緩存中
**/
cachePutRequest.apply(cacheValue);
}
/**
執行@CacheEvict的邏輯,這里是當beforeInvocation為false時清緩存
**/
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
}上面的代碼片段比較核心,均是cache的內容,對于aop的源碼,這里不詳細展開,應該單起一篇文章進行研究。主要的類和接口都在spring的context中,org.springframework.cache包中。
和第三方緩存組件的適配
通過以上的分析,知道了spring cache功能的來龍去脈,下面需要分析的是,為什么只需要maven聲明一下依賴,spring boot 就可以自動就適配了.
在上面的執行方法中,我們看到了cachePutRequest.apply(cacheValue) ,這里會操作緩存,CachePutRequest是CacheAspectSupport的內部類。
private class CachePutRequest {
private final CacheAspectSupport.CacheOperationContext context;
private final Object key;
public CachePutRequest(CacheAspectSupport.CacheOperationContext context, Object key) {
this.context = context;
this.key = key;
}
public void apply(Object result) {
if (this.context.canPutToCache(result)) {
//從context中獲取cache實例,然后執行放入緩存的操作
Iterator var2 = this.context.getCaches().iterator();
while(var2.hasNext()) {
Cache cache = (Cache)var2.next();
CacheAspectSupport.this.doPut(cache, this.key, result);
}
}
}
}Cache是一個標準接口,其中EhCacheCache就是EhCache的實現類。這里就是SpringBoot和Ehcache之間關聯的部分,那么context中的cache列表是什么時候生成的呢。答案是CacheAspectSupport的getCaches方法
protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) {
Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);
if (caches.isEmpty()) {
throw new IllegalStateException("No cache could be resolved for '" + context.getOperation() + "' using resolver '" + cacheResolver + "'. At least one cache should be provided per cache operation.");
} else {
return caches;
}
}而獲取cache是在每一次進行進行緩存操作的時候執行??梢钥匆幌抡{用棧

貌似有點跑題,拉回來... 在spring-boot-autoconfigure包里,有所有自動裝配相關的類。這里有個EhcacheCacheConfiguration類 ,如下
@Configuration
@ConditionalOnClass({Cache.class, EhCacheCacheManager.class})
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class, EhCacheCacheConfiguration.ConfigAvailableCondition.class})
class EhCacheCacheConfiguration {
......
static class ConfigAvailableCondition extends ResourceCondition {
ConfigAvailableCondition() {
super("EhCache", "spring.cache.ehcache", "config", new String[]{"classpath:/ehcache.xml"});
}
}
}這里會直接判斷類路徑下是否有ehcache.xml文件
以上是Spring Boot緩存源碼是什么的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。