一、簡述
從 3.1 開始魂爪,Spring 引入了對 Cache 的支持先舷。其使用方法和原理都類似于 Spring 對事務管理的支持。Spring Cache 是作用在方法上的滓侍,其核心思想:當調(diào)用一個緩存方法時蒋川,會把該方法參數(shù)和返回結果作為一個鍵值對存放在緩存中,等到下次利用同樣的參數(shù)來調(diào)用該方法時將不再執(zhí)行該方法撩笆,而是直接從緩存中獲取結果進行返回捺球。所以在使用 Spring Cache 的時候,要保證緩存的方法對于相同的方法參數(shù)要有相同的返回結果浇衬。使用 Spring Cache 需要做到兩點:
1??在 Springboot 的啟動類上使用 @EnableCaching 開啟緩存懒构。
2??聲明某些方法使用緩存。
和 Spring 對事務管理的支持一樣耘擂,Spring 對 Cache 的支持也有基于注解和基于 XML 配置兩種方式胆剧。
二、基于注解的支持
其核心主要是 @Cacheable 和 @CacheEvict醉冤。使用 @Cacheable 標記的方法在執(zhí)行后秩霍,Spring Cache 將緩存其返回結果,而使用 @CacheEvict 標記的方法會在方法執(zhí)行前或者執(zhí)行后移除 Spring Cache 中的某些元素蚁阳。
1??@Cacheable:可以標記在類上或者方法上铃绒。
當標記在方法上時表示該方法是支持緩存的,當標記在類上時則表示該類所有的方法都是支持緩存的螺捐。對于一個支持緩存的方法颠悬,Spring 會在其被調(diào)用后將其返回值緩存起來,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結果定血,而不需要再次執(zhí)行該方法赔癌。Spring 在緩存方法的返回值時是以鍵值對進行緩存的,值就是方法的返回結果澜沟,至于鍵的話灾票,Spring 又支持兩種策略,默認策略和自定義策略茫虽。需要注意的是當一個支持緩存的方法在對象內(nèi)部被調(diào)用時是不會觸發(fā)緩存功能的刊苍。@Cacheable 可以指定三個屬性既们,value、key 和 condition正什。
【value 屬性指定 Cache 名稱】
value 是必須指定的啥纸,其表示當前方法的返回值被緩存在哪個 Cache 上,對應 Cache 的名稱婴氮。其可以是一個 Cache 也可以是多個 Cache脾拆,當需要指定多個 Cache 時其是一個數(shù)組。
@Cacheable("cache1")//Cache是發(fā)生在cache1上的
public User find(Integer id) {
return null;
}
@Cacheable({"cache1", "cache2"})//Cache是發(fā)生在cache1和cache2上的
public User find(Integer id) {
return null;
}
【使用 key 屬性自定義 key】
key 屬性是用來指定 Spring 緩存方法的返回結果時對應的 key 的莹妒。該屬性支持 SpringEL 表達式。當沒有指定該屬性時绰上,Spring 將使用默認策略生成 key旨怠。
自定義策略是指可以通過 Spring 的 EL 表達式來指定 key。這里的 EL 表達式可以使用方法參數(shù)及它們對應的屬性蜈块。使用方法參數(shù)時可以直接使用#參數(shù)名
或者#p參數(shù)index
鉴腻。如下:
//key是指傳入時的參數(shù)
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
// 表示第一個參數(shù)
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
// 表示User中的id值
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
// 表示第一個參數(shù)里的id屬性值
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
除了上述使用方法參數(shù)作為 key 之外牢撼,Spring 還提供了一個 root 對象可以用來生成 key量淌。通過該 root 對象可以獲取到以下信息。當要使用 root 對象的屬性作為 key 時衷旅,也可以將“#root”省略器一,因為 Spring 默認使用的就是 root 對象的屬性课锌。如:
// key值為:user中的name屬性的值
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
【condition 屬性指定發(fā)生的條件】
有時候可能并不希望緩存一個方法的所有返回結果。condition 屬性默認為空祈秕,表示將緩存所有的調(diào)用情形渺贤。其值是通過 SpringEL 表達式來指定的,當為 true 時表示進行緩存處理请毛;當為 false 時表示不進行緩存處理志鞍,即每次調(diào)用該方法時該方法都會執(zhí)行一次。如下表示只有當 user 的 id 為偶數(shù)時才會緩存:
// 根據(jù)條件判斷是否緩存
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {
System.out.println("find user by user " + user);
return user;
}
2??@CachePut
在支持 Spring Cache 的環(huán)境下方仿,對于使用 @Cacheable 標注的方法固棚,Spring 在每次執(zhí)行前都會檢查 Cache 中是否存在相同 key 的緩存元素,如果存在就不再執(zhí)行該方法仙蚜,而是直接從緩存中獲取結果進行返回此洲,否則才會執(zhí)行并將返回結果存入指定的緩存中。@CachePut 也可以聲明一個方法支持緩存功能鳍征。與 @Cacheable 不同的是使用 @CachePut 標注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結果黍翎,而是每次都會執(zhí)行該方法,并將執(zhí)行結果以鍵值對的形式存入指定的緩存中艳丛。
//@CachePut也可以標注在類或方法上匣掸√宋桑可以指定的屬性跟@Cacheable是一樣的
@CachePut("users")//每次都會執(zhí)行方法,并將結果存入指定的緩存中
public User find(Integer id) {
return null;
}
3??@CacheEvict
@CacheEvict 是用來標注在需要清除緩存元素的方法或類上的碰酝。當標記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作霎匈。@CacheEvict 可以指定的屬性有 value、key送爸、condition铛嘱、allEntries 和 beforeInvocation。其中 value袭厂、key 和 condition 的語義與 @Cacheable 對應的屬性類似墨吓。即 value 表示清除操作是發(fā)生在哪些 Cache 上的(對應 Cache 的名稱);key 表示需要清除的是哪個 key纹磺,如未指定則會使用默認策略生成的 key帖烘;condition 表示清除操作發(fā)生的條件。
【allEntries 屬性】
allEntries 是 boolean 類型橄杨,表示是否需要清除緩存中的所有元素秘症。默認為 false,表示不需要式矫。當指定了 allEntries 為 true 時乡摹,Spring Cache 將忽略指定的 key。有的時候需要 Cache 一下清除所有的元素采转,這比一個一個清除元素更有效率聪廉。
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
【beforeInvocation 屬性】
清除操作默認是在對應方法成功執(zhí)行之后觸發(fā)的,如果方法拋出異常而未能成功返回時不會觸發(fā)清除操作故慈。使用b eforeInvocation 可以改變觸發(fā)清除操作的時間锄列,當指定該屬性值為 true 時,Spring 會在調(diào)用該方法之前清除緩存中的指定元素惯悠。
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
其實除了使用 @CacheEvict 清除緩存元素外邻邮,當使用 Ehcache 作為實現(xiàn)時,也可以配置 Ehcache 自身的清除策略克婶,其是通過 Ehcache 的配置文件來指定的筒严。
4??@Caching
@Caching 注解可以在一個方法或者類上同時指定多個 Spring Cache 相關的注解。其擁有三個屬性:cacheable情萤、put 和 evict鸭蛙,分別用于指定 @Cacheable、@CachePut 和 @CacheEvict筋岛。
@Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {
return null;
}
5??使用自定義注解
Spring 允許在配置可緩存的方法時使用自定義的注解娶视,前提是自定義的注解上必須使用對應的注解進行標注。如有如下這么一個使用 @Cacheable 進行標注的自定義注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value="users")
public @interface MyCacheable {
}
那么在需要緩存的方法上使用 @MyCacheable 進行標注也可以達到同樣的效果。
@MyCacheable
public User findById(Integer id) {
System.out.println("find user by id: " + id);
User user = new User();
user.setId(id);
user.setName("Name" + id);
return user;
}
三肪获、應用場景
1??@CachePut:更新數(shù)據(jù)庫的數(shù)據(jù)并更新緩存
- 先調(diào)用更新數(shù)據(jù)庫方法
- 再將更新的數(shù)據(jù)寫入名為(users)緩存中
@PostMapping(value = "/updateUserInfo")
@CachePut(value = "users", key = "#user.userId")
public void updateUserInfo(User user) {
userService.updateUserInfo(user);
}
2??@Cahceable:查詢出的數(shù)據(jù)是最新的而不是之前緩存中的
- 這里 value 屬性的值同 @CachePut 時的 value 值一樣寝凌,確保查詢與更新都是同一塊緩存。
- 此時查詢的 key 要和更新的 key 保持一致孝赫,確保拿到的是更新后的數(shù)據(jù)较木。
@GetMapping(value = "/queryUserInfo/{userId}")
@Cacheable(value = "users", key = "#userId")
public User queryUserInfo(@PathVariable("userId")Integer userId) {
userService.queryUserInfo(userId);
}