首页 > 作文

关于Spring Cache 缓存拦截器( CacheInterceptor)

更新时间:2023-04-04 07:53:31 阅读: 评论:0

目录
spring cache 缓存拦截器( cacheinterceptor)spring cache常用的三种缓存操作具体整个流程是这样的cacheinterceptor.java定义cacheable注解定义rediskey.javacache.javarediscache.javacachemanager.javaabstractcachemanager.javarediscachemanager.java实现cacheinterceptor.java配置spring.xml测试使用

spring cache 缓存拦截器( cacheinterceptor)

打开spring cache的核心缓存拦截器cacheinterceptor,可以看到具体实现:

public class cacheinterceptor extends cacheaspectsupport implements methodinterceptor, rializable {@overridepublic object invoke(final methodinvocation invocation) throws throwable {method method = invocation.getmethod();cacheoperationinvoker aopallianceinvoker = new cacheoperationinvoker() {@overridepublic object invoke() {try {return invocation.proceed();}catch (throwable ex) {throw new throwablewrapper(ex);}}};try {return execute(aopallianceinvoker, invocation.getthis(), method, invocation.getarguments());}catch (cacheoperationinvoker.throwablewrapper th) {throw th.getoriginal();}}}

cacheinterceptor默认实现了spring aop的methodinterceptor接口,methodinterceptor的功能是做方法拦截。拦截的方法都会调用invoke方法,在invoke方法里面主要缓存逻辑是在execute方法里面,该方法是继承了父类cacheaspectsupport。

protected object execute(cacheoperationinvoker invoker, object target, method method, object[] args) {// check whether aspect is enabled (to cope with cas where the aj is pulled in automatically)if (this.initialized) {class<?> targetclass = gettargetclass(target);//获取执行方法上所有的缓存操作集合。如果有缓存操作则执行到execute(...),如果没有就执行invoker.invoke()直接调用执行方法了collection<cacheoperation> operations = getcacheoperationsource().getcacheoperations(method, targetclass);if (!collectionutils.impty(operations)) {return execute(invoker, method, new cacheoperationcontexts(operations, method, args, target, targetclass));}}return invoker.invoke();}

集合collection operations中存放了所有的缓存操作cacheputoperation、cacheableoperation、cacheevictoperation

spring cache常用的三种缓存操作

@cacheput:执行方法后,将方法返回结果存放到缓存中。不管有没有缓存过,执行方法都会执行,并缓存返回结果(unless可以否决进行缓存)。(当然,这里说的缓存都要满足condition条件)@cacheable:如果没有缓存过,获取执行方法的返回结果;如果缓存过,则直接从缓存中获取,不再执行方法。@cacheevict:如果设置了beforeintercepte则在方法执行前进行缓存删除操作,如果没有,则在执行方法调用完后进行缓存删除操作。
private object execute(final cacheoperationinvoker invoker, method method, cacheoperationcontexts contexts) {// special handling of synchronized invocationif (contexts.issynchronized()) {cacheoperationcontext context = contexts.get(cacheableoperation.class).iterator().next();if (isconditionpassing(context, cacheoperationexpressionevaluator.no_result)) {object key = generatekey(context, cacheoperationexpressionevaluator.no_result);cache cache = context.getcaches().iterator().next();try {return wrapcachevalue(method, cache.get(key, new callable<object>() {@overridepublic object call() throws exception {return unwrapreturnvalue(invokeoperation(invoker));}}));}catch (cache.valueretrievalexception ex) {// the invoker wraps any throwable in a throwablewrapper instance so we// can just make sure that one bubbles up the stack.throw (cacheoperationinvoker.throwablewrapper) ex.getcau();}}el {// no caching required, only call the underlying methodreturn invokeoperation(invoker);}}// 处理beforeintercepte=true的缓存删除操作processcacheevicts(contexts.get(cacheevictoperation.class), true,cacheoperationexpressionevaluator.no_result);// 从缓存中查找,是否有匹配@cacheable的缓存数据cache.valuewrapper cachehit = findcacheditem(contexts.get(cacheableoperation.class));// 如果@cacheable没有被缓存,那么就需要将数据缓存起来,这里将@cacheable操作收集成cacheputrequest集合,以便后续做@cacheput缓存数据存放。list<cacheputrequest> cacheputrequests = new linkedlist<cacheputrequest>();if (cachehit == null) {collectputrequests(contexts.get(cacheableoperation.class),cacheoperationexpressionevaluator.no_result, cacheputrequests);}object cachevalue;object returnvalue;//如果没有@cacheput操作,就使用@cacheable获取的结果(可能也没有@cableable,所以result可能为空)。if (cachehit != null && cacheputrequests.impty() && !hascacheput(contexts)) {//如果没有@cacheput操作,并且cachehit不为空,说明命中缓存了,直接返回缓存结果cachevalue = cachehit.get();returnvalue = wrapcachevalue(method, cachevalue);}el {// 否则执行具体方法内容,返回缓存的结果returnvalue = invokeoperation(invoker);cachevalue = unwrapreturnvalue(returnvalue);}// collect any explicit @cacheputscollectputrequests(contexts.get(cacheputoperation.class), cachevalue, cacheputrequests);// process any collected put requests, either from @cacheput or a @cacheable missfor (cacheputrequest cacheputrequest : cacheputrequests) {cacheputrequest.apply(cachevalue);}// process any late evictionsprocesscacheevicts(contexts.get(cacheevictoperation.class), fal, cachevalue);return returnvalue;}//根据key从缓存中查找,返回的结果是valuewrapper,它是返回结果的包装器private cache.valuewrapper findcacheditem(collection<cacheoperationcontext> contexts) {object result = cacheoperationexpressionevaluator.no_result;for (cacheoperationcontext context : contexts) {if (isconditionpassing(context, result)) {object key = generatekey(context, result);cache.valuewrapper cached = findincaches(context, key);if (cached != null) {return cached;}el {if (logger.istraceenabled()) {logger.trace("no cache entry for key '" + key + "' in cache(s) " + context.getcachenames());}}}}return null;}private cache.valuewrapper findincaches(cacheoperationcontext context, object key) {for (cache cache : context.getcaches()) {cache.valuewrapper wrapper = doget(cache, key);if (wrapper != null) {if (logger.istraceenabled()) {logger.trace("cache entry for key '" + key + "' found in cache '" + cache.getname() + "'");}return wrapper;}}return null;}

具体整个流程是这样的

cacheinterceptor.java

项目中基本上都需要使用到cache的功能, 但是spring提供的cacheable并不能很好的满足我们的需求, 所以这里自己借助spring思想完成自己的业务逻辑.

定义cacheable注解

@target({elementtype.method, elementtype.type})@retention(retentionpolicy.runtime)@inherited@documentedpublic @interface cacheable {     rediskey value();     string key();}

定义rediskey.java

public enum rediskeyenum {     test_cache("test:", 24, timeunit.hours, "test");     /**     * 缓存key的前缀     */    舞蹈艺考学校private string keyprefix;     /**     * 过期时间     */    private long timeout;     /**     * 过期时间单位     */    private timeunit timeunit;     /**     * 描述     */    private string desc;     private static final string redis_key_defualt_parator = ":";     rediskey(string keyprefix, long timeout, timeunit timeunit, string desc){        this.keyprefix = keyprefix;        this.timeout = timeout;        this.timeunit = timeunit;        this.desc = desc;    }     public long gettimeout() {        return timeout;    }     public timeunit gettimeunit() {        return timeunit;    }     publi中药学专业主要学什么c string getdesc() {        return desc;    }     /**     * 获取完整的缓存key     * @param keys     * @return     */    public string getkey(string... keys) {        if(keys == null || keys.length <= 0){            return this.keyprefix;        }        string rediskey = keyprefix;        for (int i = 0, length = keys.length; i < length; i++) {            string key = keys[i];            rediskey += key;            if (i < length - 1) {                rediskey += redis_key_defualt_parator;            }        }        return rediskey;    }}

cache.java

public interface cache<k, v> {     /**     * 返回缓存名称     * @return     */    string getname();     /**     * 添加一个缓存实例     *     * @param key     * @param value     */    v put(k key, v value);     /**     * 添加一个可过期的缓存实例     * @param key     * @param value     * @param expire     * @param timeunit     * @return     */    v put(k key, v value, long expire, timeunit timeunit);     /**     * 返回缓存数据     *     * @param key     * @return     */    v get(k key);     /**     * 删除一个缓存实例, 并返回缓存数据     *     * @param key     * @return     */    void remove(k key);     /**     * 获取所有的缓存key     * @return     */    t<k> keys();     /**     * 获取所有的缓存key     * @return     */    t<k> keys(k pattern);     /**     * 获取所有的缓存数据     * @return     */    collection<v> values();     /**     * 清空所有缓存     */    void clear();}

rediscache.java

public class rediscache<k, v> implements cache<k, v> {     public static final string default_cache_name =  rediscache.class.getname() + "_cache_name";     private redistemplate<k, v> redistemplate;     private valueoperations<k, v> valueoperations;     public rediscache(redistemplate redistemplate) {        this.redistemplate = redistemplate;        this.valueoperations = redistemplate.opsforvalue();        datatype datatype = redistemplate珍爱生命预防溺水手抄报图画.type("a");    }     @override    public string getname() {        return default_cache_name;    }     @override    public v put(k key, v value) {        valueoperations.t(key, value);        return value;    }     @override    public v put(k key, v value, long expire, timeunit timeunit) {        valueoperations.t(key, value, expire, timeunit);        return value;    }     @override    public v get(k key) {        return valueoperations.get(key);    }     @override    public void remove(k key) {//        v value = valueoperations.get(key);        redistemplate.delete(key);    }     @override    public t<k> keys() {        return null;    }     @override    public t<k> keys(k pattern) {        return redistemplate.keys(pattern);    }     @override    public collection<v> values() {        return null;    }     @override    public void clear() {     }}

cachemanager.java

public interface cachemanager {     /**     * 获取缓存     * @return     */    cache getcache(string name);     /**     * 获取所有的缓存名称     */    collection<string> getcachenames(); }

abstractcachemanager.java

public abstract class abstractcachemanager implements cachemanager, initializingbean, disposablebean {     private static final logger logger = loggerfactory.getlogger(abstractcachemanager.class);     private final map<string, cache> cachemap = new concurrenthashmap<>(16);     private volatile t<string> cachenames = collections.emptyt();     private static final string default_cache_name_suffix = "_cache_name";     @override    public void afterpropertiest() throws exception {        initlalizingcache();    }     private void initlalizingcache(){        collection<? extends cache> caches = loadcaches();        synchronized (this.cachemap) {            this.cachenames = collections.emptyt();            this.cachemap.clear();            t<string> cachenames = new自然选择 linkedhasht<string>(caches.size());            for (cache cache : caches) {                string name = cache.getname();                if(stringutils.impty(name)){                    name = cache.getclass().getname() + default_cache_name_suffix;                }                this.cachemap.put(name, cache);                cachenames.add(name);            }            this.cachenames = collections.unmodifiablet(cachenames);        }    }     @override    public cache getcache(string name) {        cache cache = cachemap.get(name);        if(cache != null){            return cache;        }        return null;    }     protected abstract collection<? extends cache> loadcaches();     @override    public collection<string> getcachenames() {        return this.cachenames;    }     @override    public void destroy() throws exception {        cachemap.clear();    }}

rediscachemanager.java

public class rediscachemanager extends abstractcachemanager {     private redistemplate redistemplate;     public rediscachemanager(redistemplate redistemplate) {        this.redistemplate = redistemplate;    }      @override    protected collection<? extends cache> loadcaches() {        collection<cache<string, object>> caches = new arraylist<>();        rediscache<string, object> rediscache = new rediscache<>(redistemplate);        caches.add(rediscache);        return caches;    }}

实现cacheinterceptor.java

/** * 缓存数据过滤器, 缓存到redis数据中的数据是rviceresult.getdatemap()数据 * 使用: 在rvice方法上添加com.chinaredstar.urms.annotations.cacheable注解, 并指定rediskeyeunm和cache key, cache key支持spel表达式 * 以下情况不缓存数据: *  1: 返回状态为fasle时, 不缓存数据 *  2: 返回datamap为空时, 不缓存数据 *  3: 返回数据结构不是rvicereslut实例时, 不缓存数据 * * 当缓存问题时, 不影响正常业务, 但所有的请求都会打到db上, 对db有很大的冲击 */public class cacheinterceptor implements methodinterceptor {     private static final logger logger = loggerfactory.getlogger(cacheinterceptor.class);     private static final parameternamediscoverer parameternamediscoverer = new defaultparameternamediscoverer();     private cachemanager cachemanager;     public void tcachemanager(cachemanager cachemanager) {        this.cachemanager = cachemanager;    }     @override    public object invoke(methodinvocation methodinvocation) throws throwable {        method method = methodinvocation.getmethod();        object[] args = methodinvocation.getarguments();        cacheable cacheable = method.getannotation(cacheable.class);        if (cacheable == null) {            return methodinvocation.proceed();        }        string key = parcachekey(method, args, cacheable.key());        logger.info(">>>>>>>> -- 获取缓存key : {}", key);        if(stringutils.impty(key)){            return methodinvocation.proceed();        }        rediskey rediskey = cacheable.value();        cache cache = cachemanager.getcache(rediscache.default_cache_name);        object value = null;        try{            value = cache.get(rediskey.getkey(key));        } catch (exception e){            logger.info(">>>>>>>> -- 从缓存中获取数据异常 : {}", exceptionutil.exceptionstacktrace(e));        }        if (value != null) {            logger.info(">>>>>>>> -- 从缓存中获取数据 : {}", jsonutil.tojson(value));            return rviceresult.newinstance(true, value);        }        value = methodinv大学生可以贷款吗ocation.proceed();        logger.info(">>>>>>>> -- 从接口中获取数据 : {}", jsonutil.tojson(value));        if ( value != null && value instanceof rviceresult ) {            rviceresult result = (rviceresult) value;            if(!result.issuccess() || result.getdatamap() == null){                return value;            }            try{                cache.put(rediskey.getkey(key), result.getdatamap(), rediskey.gettimeout(), rediskey.gettimeunit());            } catch (exception e){                logger.info(">>>>>>>> -- 将数据放入缓存异常 : {}", exceptionutil.exceptionstacktrace(e));            }        }        return value;    }     /**     * 使用spel解析缓存key     * @param method     * @param args     * @param expressionstring     * @return     */    private string parcachekey(method method, object[] args, string expressionstring) {        string[] parameternames = parameternamediscoverer.getparameternames(method);        evaluationcontext context = new standardevaluationcontext();        if (parameternames != null && parameternames.length > 0                && args != null && args.length > 0                && args.length == parameternames.length ) {            for (int i = 0, length = parameternames.length; i < length; i++) {                context.tvariable(parameternames[i], args[i]);            }        }        expressionparr parr = new spelexpressionparr();        expression expression = parr.parexpression(expressionstring);        return (string) expression.getvalue(context);    }}

配置spring.xml

    <bean id="rediscachemanager" class="com.package.cache.rediscachemanager">        <constructor-arg ref="cacheredistemplate" />    </bean>      <bean id="cacheinterceptor" class="com.package.interceptor.cacheinterceptor" p:cachemanager-ref="rediscachemanager"/>     <!-- 方法拦截器 methodinterceptor -->    <aop:config proxy-target-class="true">        <aop:pointcut id="cacheinterceptorpointcut" expression="execution(* com.package..*(..))                                                                and @annotation(com.package.annotations.cacheable)"/>        <aop:advisor advice-ref="cacheinterceptor" pointcut-ref="cacheinterceptorpointcut" order="2" />    </aop:config>

测试使用

@cacheable(value = rediskey.test_cache, key = "#code + ':' + #ur.id")public rviceresult<string> test(string code, ur ur){     return new rviceresult("success");}

说明

cacheable其中的参数key拼接的规则支持spring spel表达式。其规则和spring cacheable使用方法一致。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持www.887551.com。

本文发布于:2023-04-04 07:53:29,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/3b611496f15f9c6216496143ef3b8d84.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:关于Spring Cache 缓存拦截器( CacheInterceptor).doc

本文 PDF 下载地址:关于Spring Cache 缓存拦截器( CacheInterceptor).pdf

下一篇:返回列表
标签:缓存   方法   数据   操作
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图