替换Spring Boot 的EnableCaching注解

SpringBoot 中可以使用@Cacheable注解来更方便的使用redis,这个注解是经过拦截器工做的,使用了@Cacheable的方法执行时,执行到CglibAopProxy.java中的 DynamicAdvisedInterceptor.intercept方法中以下图位置时,会发现CacheInterceptor:java

        CacheInterceptor是由EnableCaching注解引入的:git

        CachingConfigurationSelector:github

        注意上图中的ProxyCachingConfiguration:redis

        方法的返回值若是缓存中存在直接返回缓存中结果,缓存中没有才会实际执行方法这个功能的实现就是在CacheInterceptor拦截器中了,它的invoke方法:typescript

public Object invoke(final MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() { @Override public 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(); } }

        具体读取数据在protected的execute方法中的private的execute中:数据库

protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically) if (this.initialized) { Class<?> targetClass = getTargetClass(target); Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass); if (!CollectionUtils.isEmpty(operations)) { return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass)); } } return invoker.invoke(); }

        首先下面代码第一行findCachedItem方法判断缓存中是否有数据,而后在后面的那个判断中判断,有就取缓存并直接返回结果,再也不执行被拦截方法;不然else执行被拦截方法,被拦截方法中通常就是读数据库了,不过这就和这部分没啥关系了。缓存

Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class)); // Collect puts from any @Cacheable miss, if no cached item is found List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>(); if (cacheHit == null) { collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests); } Object cacheValue; Object returnValue; if (cacheHit != null && cachePutRequests.isEmpty() && !hasCachePut(contexts)) { // If there are no put requests, just use the cache hit cacheValue = cacheHit.get(); returnValue = wrapCacheValue(method, cacheValue); } else { // Invoke the method if we don't have a cache hit returnValue = invokeOperation(invoker); cacheValue = unwrapReturnValue(returnValue); }

        我这是用redis作缓存的,经过上面代码能够发现,一旦redis挂了,findCachedItem方法报异常,被拦截的方法就不能正常使用了,那么我须要redis出异常了,正常走数据库该怎么办呢。我首先考虑的是重写protected的那个execute方法,然而发现有一个private的内部类绕不过去,全都本身实现一遍,彻底不必,由于懒得细调了因而我就新建了一个拦截器,继承CacheInterceptor:微信

@Override protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { try { return super.execute(invoker, target, method, args); }catch (Exception e){ return invokeOperation(invoker); } }

        而后就是让新拦截器起做用,我讨厌SpringBoot的注解就讨厌在这部分。若是还用EnableCaching注解这问题就复杂了,因而简单粗暴新建注解代替EnableCaching,而后CachingConfigurationSelector也须要替换掉。接着是ProxyCachingConfiguration以及它的基类AbstractCachingConfiguration:app

        重写这个方法,用新建的注解替换掉方法中的EnableCaching就能够了,因为用的是线上代码测试的就很差往这贴了,不过亲测成功,虽然方法比较粗糙,可是SpringBoot里的代码留的扩展余地就那样,也不想太费工夫了。ide

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

相关文章
相关标签/搜索