打开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
@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;}
项目中基本上都需要使用到cache的功能, 但是spring提供的cacheable并不能很好的满足我们的需求, 所以这里自己借助spring思想完成自己的业务逻辑.
@target({elementtype.method, elementtype.type})@retention(retentionpolicy.runtime)@inherited@documentedpublic @interface cacheable { rediskey value(); string key();}
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; }}
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();}
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() { }}
public interface cachemanager { /** * 获取缓存 * @return */ cache getcache(string name); /** * 获取所有的缓存名称 */ collection<string> getcachenames(); }
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(); }}
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; }}
/** * 缓存数据过滤器, 缓存到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); }}
<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 条评论) |