spring框架cacheAnnotation緩存註釋聲明解析
1.基於註釋聲明緩存
聲明緩存,Spring緩存抽象提供瞭一個java annotation集合.
@Cacheable:觸發緩存填充.
@CacheEvict: 觸發緩存刪除.
@CachePut: 不幹擾方法執行的情況下更新緩存.
@Caching: 把多種緩存操作應用重組到一個方法上
@CacheConfig: 在類上設置,將一些共用緩存相關設置共享
1.1@EnableCaching
將該註解放在配置類上開啟緩存,使用該註解後,允許你指定各種選項,通過AOP的方式把這些影響添加到你的應用程序中. 類似於 @Transactional
1.2@Cacheable
觸發數據存儲於緩存
顧名思義,@Cacheable用於標示可緩存的方法 – 即將結果存儲到緩存中的方法,以便在後續調用(具有相同的參數)時,返回緩存中的值而不必實際執行該方法.
下圖是所有的緩存的存儲結構
cacheNames:指定緩存的名稱,不同緩存的數據是彼此隔離的,可以指定多個緩存名稱.(就是生成多個緩存name).如果有一個緩存命中,關聯值就會返回.
更新一個name的緩存,其他所有沒有包含這個值的緩存也會被更新,即使這個緩存方法沒有實際調用.
@Cacheable({"coffees","coffees2"}) public List<Coffee> findAllCoffee() { return coffeeRepository.findAll(); }
1.2.1默認key生成規則
Key :
上面cacheNames指定瞭緩存名稱,但是每個方法由於傳參不同,其return數據也會不同,所以一個方法中可能會有多個緩存。要在同一個cacheNames中區別不同的緩存,就需要使用key。通過SPEL表達式,指定 name為key。Spring 會默認以參數為key 如果沒有參數則有一個org.springframework.cache.interceptor.SimpleKey.
@Cacheable(cacheNames = "coffees",key = "#name") public Optional<Coffee> findOneCoffee(String name) { ExampleMatcher matcher = ExampleMatcher.matching() .withMatcher("name", exact().ignoreCase()); Optional<Coffee> coffee = coffeeRepository.findOne( Example.of(Coffee.builder().name(name).build(), matcher)); log.info("Coffee Found: {}", coffee); return coffee; }
因為Caches是一個key-value的存儲形式,每次調用緩存方法需要翻譯成對應的key來訪問緩存。緩存抽象使用簡單的 keygenerator 基於以下算法:
- 如果沒有參數,返回SimpleKey.EMPTY.
- 如果隻有一個參數,就會作為實例返回.
- 如果超過一個參數,就會返回包含所有參數的 SimpleKey.
該方法對大多數case有效,隻要參數有nautral keys並且他們有效的實現瞭 hashcode()和equals()方法.如果不是這個case的需要去改變策略.
提供不同的key 生成器,需要去實現 org.springframework.cache.interceptor.KeyGenerator 這個接口.
默認的key生成策略在spring 4.0以後有所改變.之前的Spring版本使用用的key生成策略,對於多個key參數,僅用參數的hashCode()沒有使用equeal().這可能造成key沖突(從 SPR-10237 查看背景).新的 SimpleKeyGenerator對於這種場景使用瞭組合key.
如果你想保持之前的key生成策略,你可以配置不推薦使用的org.springframework.cache.interceptor.DefaultKeyGenerator或者創建一個自定義的 基於hash的 key產生的實現
1.2.2聲明自定義key 生成
因為緩存是通用的,所以目標方法很可能有多種寫法,這些不能夠被頂層緩存結構簡單的映射.當一個方法有多個參數,但隻有一些參數需要緩存時(其他key隻給方法使用),這一點變得很明顯.
@Cacheable("books") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
上面的例子: 用到瞭兩個佈爾參數,他們對緩存可能沒有用處,此外如果有一個有用另一個沒用應該怎麼辦?
上面說過我們可以通過@Cacheable 來指定key通過哪些參數生成.可以使用SPEL 來選擇感興趣的參數(以及嵌套屬性),執行操作,或者甚至調用任意方法都可以不需要寫任何代碼或者實現任何接口. 推薦的默認的key 產生方式在上面已經說過,但是默認的策略並不能適應所有的方法.
下面例子使用瞭多種SPEL語法:
@Cacheable(cacheNames="books", key="#isbn") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) @Cacheable(cacheNames="books", key="#isbn.rawNumber") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) @Cacheable(cacheNames="books", key="T(someType).hash(#isbn)") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
如果生成key的算法太過特殊或者需要共享,可以自定義keyGenerator.
@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
註: key 和 keyGenerator是互斥的,同時指定會產生異常
1.2.3默認的cache resolution
默認的cache resolution是一個簡單的 CacheResolver ,通過配置CacheManager來檢索在操作級別定義的緩存.
通過繼承實現org.springframework.cache.interceptor.CacheResolver 接口來提供不同的默認resolver.
默認的cache resolution是由單個CacheManager實現並且沒有太復雜的resolution的需求.
默認的cache resolution是由單個CacheManager實現並且沒有太復雜的resolution的需求.
當一個應用同時使用多個 cacheManager 可以通過cachemanager指定
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") public Book findBook(ISBN isbn) {...}
也可以通過cacheResolver替換resolver 類似於替換key generation.
@Cacheable(cacheResolver="runtimeCacheResolver") public Book findBook(ISBN isbn) {...}
這個resolution在每次緩存操作都會請求,允許我們基於實時的參數來實現解析緩存.
從spring 4.1之後, value的屬性不再強制要求傳,因為無論annotation的內容傳什麼,這個特定信息都會被CacheResolver提供.
類似於key和keygenerator,cacheManager和cahceResolver這兩個參數都是互斥的,同時指定會返回異常,因為定義的cacheManager會被定義的cacheResolver的實現給忽略.
1.2.4同步緩存
在多線程環境裡,某些操作可能使用相同參數並發調用(典型的例如啟動).默認情況下,緩存抽象不會加鎖,如果一個同樣的值多次被計算是違背緩存的初衷的.
在這種特殊的情況下,可以使用 sync 屬性來指定緩存提供方在計算緩存實體的時候加鎖.
@Cacheable(cacheNames="foos", sync=true) public Foo executeExpensiveOperation(String id) {...}
這個可選特性,也許提供緩存的庫並不支持.所有的 核心框架提供得CacheManager是支持的.
1.2.5 緩存的條件
有時,一個方法並不能在所有時候都適用於一個緩存(比如,它可能依賴於給定參數).緩存的註解支持這些情況,通過使用 condition 參數.
condition中使用 Spel 表達式可以得出 true 或者false.如果是true,方法緩存,反之,方法不會緩存(這個方法無論什麼值或者使用任何參數都會被調用).
@Cacheable(cacheNames="book", condition="#name.length() < 32") public Book findBook(String name)
1.上面設置的條件是name的長度 小於32
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") public Book findBook(String name)
2.使用unless, 否決掉對應的value到緩存中.不像condition,unless表達式是在方法調用完後被調用,上面的例子就是如果我們隻想緩存paperback book, 我們可以阻擋 hardback.
緩存抽象同樣支持 java.util.Optional,返回類型.如果Optional是present,則被關聯到對應緩存,如果not present,將會存儲一個null. #result 總是引用業務對象並且永遠不會支持包裝器,所以上面的例子可以改為
@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback") public Optional<Book> findBook(String name)
上面的例子 #result一直引用Book 而不是Optional<Book>.因為有可能為空,所以使用SpEL’s的Safe Navigation Operator
1.2.6可用的Spel 評估上下文
名稱 |
位置 |
描述 |
例子 |
methodName |
Root object |
被調用方法的名稱 |
#root.methodName |
method |
Root object |
被調用的方法 |
#root.method.name |
target |
Root object |
被調用的目標對象 |
#root.target |
targetClass |
Root object |
被調用對象的類 |
#root.targetClass |
args |
Root object |
調用的參數數組 |
#root.args[0] |
caches |
Root object |
針對當前運行方法的所有緩存 |
#root.caches[0].name |
Argument name |
Evaluation context |
任何方法參數的名稱.如果名稱找不到(也許是沒有debug信息),這個參數名稱可能在#a<#arg>之下找到, 這個#arg表示參數索引(從0開始) |
#iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias). |
result |
Evaluation context |
方法返回調用(值將被緩存).隻有在 unless表達式中, cache put表達式(去計算一個key)或者 cache evict 表達式(當 beforeInvocation=false)可用. 為瞭支持wrappers(比如 Optional),#result 引用的實際的對象而非 wrapper |
#result |
以上就是spring框架cacheAnnotation註釋聲明緩存解析的詳細內容,更多關於spring框架cache緩存Annotation註釋的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 使用@CacheEvict清除指定下所有緩存
- SpringBoot如何使用@Cacheable進行緩存與取值
- 使用Spring Cache設置緩存條件操作
- SpringBoot集成本地緩存性能之王Caffeine示例詳解
- SpringBoot淺析緩存機制之Ehcache 2.x應用