天天看點

Spring之路(49)–Spring緩存性能提升如升天?但你真的用對了嗎?

1. 背景

Spring緩存,用了确實爽,性能的提升就像俺升天了那麼爽快,但是如果了解不夠深,不夠準确的話,會帶來災難性的問題。

比如該使用緩存的時候,實際上并沒有使用緩存,這種情況,相當于緩存無效。

比如不該使用緩存的時候,緩存卻跳出來了,這種情況就可怕了,意味着你拿到了不該拿的資料。

是以本文就以實際的例子,示範下Spring緩存中那些需要注意的點。

2. 同一緩存下,隻看參數不看方法名

如下面的例子

@Cacheable("blogs")

public List<BlogDo> getListAsc() {

 System.out.println("升序擷取blog清單");

 return null;

}

public List<BlogDo> getListDesc() {

 System.out.println("降序擷取blog清單");

本意是想有兩個緩存,分别緩存升序的blog清單和降序的blog清單,但是由于這兩個方法都是使用的名為blogs的緩存,且都沒有參數,導緻第二個方法會将第一個方法執行的緩存取出來:

public static void main(String[] args) throws SQLException {

 // 擷取容器

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

 // 擷取blogService元件

 BlogService blogService = context.getBean("blogService", BlogService.class);

 //輸出:升序擷取blog清單

 blogService.getListAsc();

 //沒有輸出,因為直接取緩存了

 blogService.getListDesc();

那麼這種情況該如何處理呢,有以下幾種處理辦法:

改為不同的緩存名稱,比如一個用@Cacheable("blogsAsc"),另一個用@Cacheable("blogsDesc")。這種方法十分不推薦,因為都是面向的blog這個表,用了兩個緩存,那麼清除緩存的時候咋辦?很麻煩!

使用參數區分,加一個枚舉類型表示升序和降序,方法改為getListAsc(SortEnum.ASC)和getListAsc(SortEnum.ASC)。這種方法也不推薦,因為太麻煩了。

推薦方法是:既然都是取的部落格清單,直接定義一個getList方法即可,然後對該方法添加緩存。至于排序的事情,自己取出結果後排序就是了。

3. 緩存方法調用判斷的是對象相等,而不是數值相等

如下面的例子:

public Long getLong(Long a) {

 System.out.println("getLong");

 return a+1;

public Integer getInteger(Integer a) {

 System.out.println("getInteger");

 return a+2;

感覺上,如果都是對數字1進行查詢,應該能觸發緩存,實際上并沒有,就是因為這兩個對象并不相等

public class Main {

 //測試

 blogService.getLong(1L);// 輸出getLong

 blogService.getLong(1L);// 沒有輸出,因為已經有緩存

 blogService.getInteger(1);// 輸出getInteger因為參數并不相等

 Integer a = 1;

 Long b = 1L;

 System.out.println(a.equals(b));// 輸出false,證明這兩個參數實際上不相等

}

4. 如果參數是對象,一定要實作.equals和hashcode

在上面我們已經說明了,緩存參數的觸發,是按對象是否相等來實作的,如果沒有實作.equals和hashcode,就會出現:

BlogDo blog1=new BlogDo();

   blog1.setId(1L);

   blogService.getByObject(blog1);

   blogService.getByObject(blog1);//觸發緩存

   BlogDo blog2=new BlogDo();

   blog2.setId(1L);

   blogService.getByObject(blog2);//沒觸發緩存,因為blog1與blog2不同

注意,因為是map結構,務必要同時實作.equals和hashcode,否則判斷也不準确!

5. 總結

如果感覺還不清楚的話,還可以在調試模式下去檢視CacheManager的具體内容,如下:

 //檢視緩存

 CacheManager cacheManager=context.getBean("cacheManager", CacheManager.class);

此時我們檢視緩存中的内容就好了解了,此處感興趣的可以自己去試下,我不再詳細分析了。

Spring之路(49)–Spring緩存性能提升如升天?但你真的用對了嗎?