国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring Boot緩存源碼分析

wangbjun / 2250人閱讀

摘要:依賴配置文件設定緩存的默認數據過期策略應用上加上注解然后就可以在代碼里面使用注解了,像這樣。使用該集合過濾執行緩存處理。從中獲取實例,然后執行放入緩存的操作是一個標準接口,其中就是的實現類。

前言

項目里面要增加一個應用緩存,原本想著要怎么怎么來整合ehcache和springboot,做好準備配置這個配置那個,結果只需要做三件事:

pom依賴

寫好一個ehcache的配置文件

在boot的application上加上注解@EnableCaching.
這就完事了,是不是很魔幻。

pom依賴


            net.sf.ehcache
            ehcache
            2.10.5

配置文件



    
    

應用上加上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 parseCacheAnnotations(SpringCacheAnnotationParser.DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
        Collection ops = null;
        //找@cacheable注解方法
        Collection 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 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 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 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 cachingOps = this.parseCachingAnnotation(ae, cachingConfig, caching);
                if (cachingOps != null) {
                    ops.addAll(cachingOps);
                }
            }
        }
        return ops;
}

解析Cachable,Caching,CachePut,CachEevict 這四個注解對應的方法都保存到了Collection 集合中。

執行方法時做了什么

執行的時候,主要使用了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() {
                        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 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 getCaches(CacheOperationInvocationContext context, CacheResolver cacheResolver) {
        Collection 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文件

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72159.html

相關文章

  • spring boot - 收藏集 - 掘金

    摘要:引入了新的環境和概要信息,是一種更揭秘與實戰六消息隊列篇掘金本文,講解如何集成,實現消息隊列。博客地址揭秘與實戰二數據緩存篇掘金本文,講解如何集成,實現緩存。 Spring Boot 揭秘與實戰(九) 應用監控篇 - HTTP 健康監控 - 掘金Health 信息是從 ApplicationContext 中所有的 HealthIndicator 的 Bean 中收集的, Spring...

    rollback 評論0 收藏0
  • Java學習路線總結,搬磚工逆襲Java架構師(全網最強)

    摘要:哪吒社區技能樹打卡打卡貼函數式接口簡介領域優質創作者哪吒公眾號作者架構師奮斗者掃描主頁左側二維碼,加入群聊,一起學習一起進步歡迎點贊收藏留言前情提要無意間聽到領導們的談話,現在公司的現狀是碼農太多,但能獨立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0
  • 【推薦】最新200篇:技術文章整理

    摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...

    BicycleWarrior 評論0 收藏0

發表評論

0條評論

wangbjun

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<