替換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

微信公衆號:

                      

相關文章
相關標籤/搜索