1. 緩存簡介
緩存:緩存就是數(shù)據(jù)交換的緩沖區(qū)(稱作Cache)纲酗,當某一硬件要讀取數(shù)據(jù)時笋婿,會首先從緩存中查找需要的數(shù)據(jù),如果找到了則直接執(zhí)行勾给,找不到的話則從內存中找滩报。為什么使用緩存?究其原因就是緩存的讀寫速度遠快與磁盤播急,從減輕I/O開銷和加快運行速度方便都有很好的效果脓钾。那么我們緩存什么?哪些經(jīng)常讀取而又不經(jīng)常修改的數(shù)據(jù)桩警,那些數(shù)據(jù)量較大又很少修改的數(shù)據(jù)惭笑。緩存策略三要素:緩存命中率、緩存更新策略生真、最大緩存容量。
cache 可以說是后端提高響應速度捺宗、承載能力的標準套路了
spring boot中提供spring boot starter cache 組件 配合spring boot starter redis 或者其他緩存組件 可以很簡單的使用緩存柱蟀。
2. spring cache 支持的緩存類型
- Generic
- JCache (JSR-107)
- EhCache 2.x
- Hazelcast
- Infinispan
- Redis
- Guava
- Simple
如果不滿足上述的緩存方案 可以自實現(xiàn) cacheManager。
注解介紹
- @Cacheable
獲取緩存 如果有緩存 直接返回蚜厉。
@Cacheable
可以標記在一個方法上长已,也可以標記在一個類上。當標記在一個方法上時表示該方法是支持緩存的昼牛,當標記在一個類上時則表示該類所有的方法都是支持緩存的术瓮。對于一個支持緩存的方法,Spring會在其被調用后將其返回值緩存起來贰健,以保證下次利用同樣的參數(shù)來執(zhí)行該方法時可以直接從緩存中獲取結果胞四,而不需要再次執(zhí)行該方法。Spring在緩存方法的返回值時是以鍵值對進行緩存的伶椿,值就是方法的返回結果辜伟,至于鍵的話,Spring又支持兩種策略脊另,默認策略和自定義策略导狡。
value:緩存的名稱,在 spring 配置文件中定義偎痛,必須指定至少一個旱捧。如@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}
key:緩存的 key,可以為空踩麦,如果指定要按照 SpEL 表達式編寫枚赡,如果不指定,則缺省按照方法的所有參數(shù)進行組合靖榕。如@Cacheable(value=”testcache”,key=”#userName”)
condition:緩存的條件标锄,可以為空,使用 SpEL 編寫茁计,返回 true 或者 false料皇,只有為 true 才進行緩存谓松。如@Cacheable(value=”testcache”,condition=”#userName.length()>2”)
- @CachePut
執(zhí)行并且更新緩存相關 不管如何 肯定會執(zhí)行方法 然后返回 這樣可以更新緩存的內容,@CachePut也可以標注在類上和方法上践剂。使用@CachePut時我們可以指定的屬性跟@Cacheable是一樣的鬼譬。
在支持Spring Cache的環(huán)境下,對于使用@Cacheable標注的方法逊脯,Spring在每次執(zhí)行前都會檢查Cache中是否存在相同key的緩存元素优质,如果存在就不再執(zhí)行該方法,而是直接從緩存中獲取結果進行返回军洼,否則才會執(zhí)行并將返回結果存入指定的緩存中巩螃。@CachePut也可以聲明一個方法支持緩存功能。與@Cacheable不同的是使用@CachePut標注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結果匕争,而是每次都會執(zhí)行該方法避乏,并將執(zhí)行結果以鍵值對的形式存入指定的緩存中。
- @CacheEvict
刪除緩存相關甘桑,
@CacheEvict是用來標注在需要清除緩存元素的方法或類上的拍皮。當標記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作。@CacheEvict可以指定的屬性有value跑杭、key铆帽、condition、allEntries和beforeInvocation德谅。其中value爹橱、key和condition的語義與@Cacheable對應的屬性類似。即value表示清除操作是發(fā)生在哪些Cache上的(對應Cache的名稱)女阀;key表示需要清除的是哪個key宅荤,如未指定則會使用默認策略生成的key;condition表示清除操作發(fā)生的條件浸策。allEntries和beforeInvocation冯键。
allEntries:是否清空所有緩存內容,缺省為 false庸汗,如果指定為 true惫确,則方法調用后將立即清空所有緩存。如:@CachEvict(value=”testcache”,allEntries=true) 蚯舱。
beforeInvocation:是否在方法執(zhí)行前就清空改化,缺省為 false,如果指定為 true枉昏,則在方法還沒有執(zhí)行的時候就清空緩存陈肛,缺省情況下,如果方法執(zhí)行拋出異常兄裂,則不會清空緩存句旱。如:@CachEvict(value=”testcache”阳藻,beforeInvocation=true)
allEntries指定為true時,則會清楚所有緩存谈撒。
- @Caching
@Caching注解可以讓我們在一個方法或者類上同時指定多個Spring Cache相關的注解腥泥。其擁有三個屬性:cacheable、put和evict啃匿,分別用于指定@Cacheable蛔外、@CachePut和@CacheEvict。使用如下
@Caching(cacheable = {@Cacheable(value = "user", key = "#id", condition = "#id != '123'"),
@Cacheable(value = "user", key = "#id", condition = "#id != '321'")}
)
public User findById(String id) {
System.out.println("執(zhí)行數(shù)據(jù)庫查詢方法");
return userDao.findById(id);
}
實踐
- 引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- 配置
spring:
redis:
host: <ip>
port: <port>
password: <password>
cache:
# spring cache 緩存類型為redis 也可以是其他的實現(xiàn)
type: redis
- 使用cache
模擬帶緩存的service
package com.ming;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
//公共配置 可以在類上注釋 注釋本類的 緩存相關公共配置
//@CacheConfig(cacheNames = TestCacheService.CACHE_KEY)
public class TestCacheService {
public static final String CACHE_KEY = "test-cache";
/**
* 獲取信息 第二次訪問會取緩存
*
* @author ming
* @date 2018-07-11 17:41:47
*/
@Cacheable(cacheNames = CACHE_KEY ,key = "#id")
public String testCache(String id) {
return getString(id);
}
/**
* 更新信息 更新緩存
*
* @author ming
* @date 2018-07-12 09:50:53
*/
@CachePut(cacheNames = CACHE_KEY ,key = "#id")
public String testCachePut(String id) {
return getString(id + "update");
}
/**
* 清除緩存
*
* @author ming
* @date 2018-07-12 09:51:22
*/
@CacheEvict(cacheNames = CACHE_KEY ,key = "#id")
public void removeCache(String id) {
System.out.println("刪除緩存 ");
}
/**
* 獲取string 模擬調用方法
*
* @author ming
* @date 2018-07-11 17:41:58
*/
private String getString(String id) {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return id + "load";
}
}
- 測試用例
package com.ming;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Start.class)
public class TestCache {
@Autowired
private TestCacheService testCacheService;
@Test
public void test() {
String id = "ming";
System.out.println("第一次訪問沒有緩存--------");
long oneNow = System.currentTimeMillis();
System.out.println(testCacheService.testCache(id));
System.out.println("耗時:" + (System.currentTimeMillis() - oneNow) + "ms");
System.out.println("第二次訪問有緩存--------");
long twoNow = System.currentTimeMillis();
System.out.println(testCacheService.testCache(id));
System.out.println("耗時:" + (System.currentTimeMillis() - twoNow) + "ms");
System.out.println("更新緩存信息--------");
long threeNow = System.currentTimeMillis();
System.out.println(testCacheService.testCachePut(id));
System.out.println("耗時:" + (System.currentTimeMillis() - threeNow) + "ms");
System.out.println("獲取更新后的緩存信息-------");
long fourNow = System.currentTimeMillis();
System.out.println(testCacheService.testCache(id));
System.out.println("耗時:" + (System.currentTimeMillis() - fourNow) + "ms");
System.out.println("移除緩存------并且調用testCache方法");
testCacheService.removeCache(id);
long fiveNow = System.currentTimeMillis();
System.out.println(testCacheService.testCache(id));
System.out.println("耗時:" + (System.currentTimeMillis() - fiveNow) + "ms");
}
}
- 注意事項
@Cacheable 溯乒、@CachePut夹厌、@CacheEvict 必須要有 cacheNames或 value
注解必須放在public修飾的方法上。
如果只是獲取緩存使用@Cacheable即可 如果要更新數(shù)據(jù)庫并且更新緩存一定要使用@CachePut 否則@Cacheable會出現(xiàn)臟讀裆悄。
總結
spring cache 為緩存提供了一套簡單快捷的方案 可以很快速添加上緩存
具體緩存的實現(xiàn) 也有更多的選擇 也可以自己實現(xiàn)spring cache的緩存管理器 來實現(xiàn)自定義的緩存尊流。