詳解Spring緩存注解@Cacheable,@CachePut , @CacheEvict使用


轉載自https://www.jb51.net/article/112849.htm

 

注釋介紹

@Cacheable

@Cacheable 的作用 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存

@Cacheable 作用和配置方法

 

參數 解釋 example
value 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 例如:
@Cacheable(value=”mycache”)
@Cacheable(value={”cache1”,”cache2”}
key 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合 @Cacheable(value=”testcache”,key=”#userName”)
condition 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存 @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

 

實例

@Cacheable(value=”accountCache”),這個注釋的意思是,當調用這個方法的時候,會從一個名叫 accountCache 的緩存中查詢,如果沒有,則執行實際的方法(即查詢數據庫),並將執行的結果存入緩存中,否則返回緩存中的對象。這里的緩存中的 key 就是參數 userName,value 就是 Account 對象。“accountCache”緩存是在 spring*.xml 中定義的名稱。

?
1
2
3
4
5
6
@Cacheable (value= "accountCache" ) // 使用了一個緩存名叫 accountCache
public Account getAccountByName(String userName) {
    // 方法內部實現不考慮緩存邏輯,直接實現業務
    System.out.println( "real query account." +userName);
    return getFromDB(userName);
}

@CachePut

@CachePut 的作用 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存,和 @Cacheable 不同的是,它每次都會觸發真實方法的調用

@CachePut 作用和配置方法

 

參數 解釋 example
value 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 @CachePut(value=”my cache”)
key 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合 @CachePut(value=”testcache”,key=”#userName”)
condition 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存 @CachePut(value=”testcache”,condition=”#userName.length()>2”)

 

實例

@CachePut 注釋,這個注釋可以確保方法被執行,同時方法的返回值也被記錄到緩存中,實現緩存與數據庫的同步更新。

?
1
2
3
4
@CachePut (value= "accountCache" ,key= "#account.getName()" ) // 更新accountCache 緩存
public Account updateAccount(Account account) {
   return updateDB(account);
}

@CacheEvict

@CachEvict 的作用 主要針對方法配置,能夠根據一定的條件對緩存進行清空

@CacheEvict 作用和配置方法

 

參數 解釋 example
value 緩存的名稱,在 spring 配置文件中定義,必須指定至少一個 @CacheEvict(value=”my cache”)
key 緩存的 key,可以為空,如果指定要按照 SpEL 表達式編寫,如果不指定,則缺省按照方法的所有參數進行組合 @CacheEvict(value=”testcache”,key=”#userName”)
condition 緩存的條件,可以為空,使用 SpEL 編寫,返回 true 或者 false,只有為 true 才進行緩存 @CacheEvict(value=”testcache”,condition=”#userName.length()>2”)
allEntries 是否清空所有緩存內容,缺省為 false,如果指定為 true,則方法調用后將立即清空所有緩存 @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法執行前就清空,缺省為 false,如果指定為 true,則在方法還沒有執行的時候就清空緩存,缺省情況下,如果方法執行拋出異常,則不會清空緩存 @CachEvict(value=”testcache”,beforeInvocation=true)

 

實例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@CacheEvict (value= "accountCache" ,key= "#account.getName()" ) // 清空accountCache 緩存
public void updateAccount(Account account) {
    updateDB(account);
}
 
@CacheEvict (value= "accountCache" ,allEntries= true ) // 清空accountCache 緩存
public void reload() {
    reloadAll()
}
 
@Cacheable (value= "accountCache" ,condition= "#userName.length() <=4" ) // 緩存名叫 accountCache
public Account getAccountByName(String userName) {
  // 方法內部實現不考慮緩存邏輯,直接實現業務
  return getFromDB(userName);
}

@CacheConfig

所有的@Cacheable()里面都有一個value=“xxx”的屬性,這顯然如果方法多了,寫起來也是挺累的,如果可以一次性聲明完 那就省事了, 所以,有了@CacheConfig這個配置,@CacheConfig is a class-level annotation that allows to share the cache names,如果你在你的方法寫別的名字,那么依然以方法的名字為准。

?
1
2
3
4
5
6
@CacheConfig ( "books" )
public class BookRepositoryImpl  implements BookRepository {
 
   @Cacheable
   public Book findBook(ISBN isbn) {...}
}

條件緩存

下面提供一些常用的條件緩存

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//@Cacheable將在執行方法之前( #result還拿不到返回值)判斷condition,如果返回true,則查緩存;
@Cacheable (value =  "user" , key =  "#id" , condition =  "#id lt 10" )
public User conditionFindById( final Long id)
 
//@CachePut將在執行完方法后(#result就能拿到返回值了)判斷condition,如果返回true,則放入緩存;
@CachePut (value =  "user" , key =  "#id" , condition =  "#result.username ne 'zhang'" )
public User conditionSave( final User user) 
 
//@CachePut將在執行完方法后(#result就能拿到返回值了)判斷unless,如果返回false,則放入緩存;(即跟condition相反)
@CachePut (value =  "user" , key =  "#user.id" , unless =  "#result.username eq 'zhang'" )
public User conditionSave2( final User user) 
 
//@CacheEvict, beforeInvocation=false表示在方法執行之后調用(#result能拿到返回值了);且判斷condition,如果返回true,則移除緩存;
@CacheEvict (value =  "user" , key =  "#user.id" , beforeInvocation =  false , condition =  "#result.username ne 'zhang'" )
public User conditionDelete( final User user) 

@Caching

有時候我們可能組合多個Cache注解使用;比如用戶新增成功后,我們要添加id–>user;username—>user;email—>user的緩存;此時就需要@Caching組合多個注解標簽了。

?
1
2
3
4
5
6
@Caching (put = {
@CachePut (value =  "user" , key =  "#user.id" ),
@CachePut (value =  "user" , key =  "#user.username" ),
@CachePut (value =  "user" , key =  "#user.email" )
})
public User save(User user) {

自定義緩存注解

比如之前的那個@Caching組合,會讓方法上的注解顯得整個代碼比較亂,此時可以使用自定義注解把這些注解組合到一個注解中,如:

?
1
2
3
4
5
6
7
8
9
10
@Caching (put = {
@CachePut (value =  "user" , key =  "#user.id" ),
@CachePut (value =  "user" , key =  "#user.username" ),
@CachePut (value =  "user" , key =  "#user.email" )
})
@Target ({ElementType.METHOD, ElementType.TYPE})
@Retention (RetentionPolicy.RUNTIME)
@Inherited
public @interface UserSaveCache {
}

這樣我們在方法上使用如下代碼即可,整個代碼顯得比較干凈。

?
1
2
@UserSaveCache
public User save(User user)

擴展

比如findByUsername時,不應該只放username–>user,應該連同id—>user和email—>user一起放入;這樣下次如果按照id查找直接從緩存中就命中了

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Caching (
   cacheable = {
     @Cacheable (value =  "user" , key =  "#username" )
   },
   put = {
     @CachePut (value =  "user" , key =  "#result.id" , condition =  "#result != null" ),
     @CachePut (value =  "user" , key =  "#result.email" , condition =  "#result != null" )
   }
)
public User findByUsername( final String username) {
   System.out.println( "cache miss, invoke find by username, username:" + username);
   for (User user : users) {
     if (user.getUsername().equals(username)) {
       return user;
     }
   }
   return null ;
}

其實對於:id—>user;username—->user;email—>user;更好的方式可能是:id—>user;username—>id;email—>id;保證user只存一份;如:

?
1
2
3
4
5
6
@CachePut (value= "cacheName" , key= "#user.username" , cacheValue= "#user.username" )
public void save(User user) 
 
 
@Cacheable (value= "cacheName" , key= "#user.username" , cacheValue= "#caches[0].get(#caches[0].get(#username).get())" )
public User findByUsername(String username)

SpEL上下文數據

Spring Cache提供了一些供我們使用的SpEL上下文數據,下表直接摘自Spring官方文檔:

 

名稱 位置 描述 示例
methodName root對象 當前被調用的方法名 root.methodName
method root對象 當前被調用的方法 root.method.name
target root對象 當前被調用的目標對象 root.target
targetClass root對象 當前被調用的目標對象類 root.targetClass
args root對象 當前被調用的方法的參數列表 root.args[0]
caches root對象 當前方法調用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”})),則有兩個cache root.caches[0].name
argument name 執行上下文 當前被調用的方法的參數,如findById(Long id),我們可以通過#id拿到參數 user.id
result 執行上下文 方法執行后的返回值(僅當方法執行之后的判斷有效,如‘unless','cache evict'的beforeInvocation=false) result

 

?
1
2
@CacheEvict (value =  "user" , key =  "#user.id" , condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username" , beforeInvocation =  true )
public void conditionUpdate(User user)

 

 


免責聲明!

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



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