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);
此時我們檢視緩存中的内容就好了解了,此處感興趣的可以自己去試下,我不再詳細分析了。
