替換Spring Boot 的EnableCaching注解


SpringBoot 中可使用@Cacheable注解來更方便的使用redis,這個注解是通過攔截器工作的,使用了@Cacheable的方法執行時,執行到CglibAopProxy.java中的 DynamicAdvisedInterceptor.intercept方法中如下圖位置時,會發現CacheInterceptor:

        CacheInterceptor是由EnableCaching注解引入的:

        CachingConfigurationSelector:

        注意上圖中的ProxyCachingConfiguration:

        方法的返回值如果緩存中存在直接返回緩存中結果,緩存中沒有才會實際執行方法這個功能的實現就是在CacheInterceptor攔截器中了,它的invoke方法:

	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:

        重寫這個方法,用新建的注解替換掉方法中的EnableCaching就可以了,由於用的是線上代碼測試的就不好往這貼了,不過親測成功,雖然方法比較粗糙,但是SpringBoot里的代碼留的擴展余地就那樣,也不想太費工夫了。

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

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

微信公眾號:

                      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM