您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

Spring Boot缓存源码的理解

2024/3/24 11:08:41发布16次查看
本篇文章给大家带来的内容是关于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@enablecachingpublic 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缓存源码的理解的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product