第七篇:Spring Boot整合Spring Cache
為了提高效能,減少資料庫的壓力,使用快取是非常好的手段之一。因此本文講解 Spring Boot 如何整合快取管理。
宣告式快取
Spring 定義 CacheManager 和 Cache 介面用來統一不同的快取技術。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用 Spring 整合 Cache 的時候,我們需要註冊實現CacheManager 的 Bean。
幾個重要概念&快取註解
@Cacheable/@CachePut/@CacheEvict 主要的引數
SpEL上下文資料
注意:
- 當我們要使用root物件的屬性作為key時我們也可以將“#root”省略,因為Spring預設使用的就是root物件的屬性。 如
@Cacheable(key = "targetClass + methodName +#p0")
- 使用方法引數時我們可以直接使用“#引數名”或者“#p引數index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多種運算子
Spring Boot預設整合CacheManager
Spring Boot 為我們自動配置了多個 CacheManager 的實現。
Spring Boot 為我們自動配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。
預設使用 ConcurrenMapCacheManager
在我們不使用其他第三方快取依賴的時候,springboot自動採用ConcurrenMapCacheManager作為快取管理器。
環境依賴
Ehcache是一種廣泛使用的開源Java分散式快取。主要面向通用快取,Java EE和輕量級容器。它具有記憶體和磁碟儲存,快取載入器,快取擴充套件,快取異常處理程式,一個gzip快取servlet過濾器,支援REST和SOAP api等特點。本文使用Ehcache,在pom檔案引入spring-boot-starter-cache和ehcache環境依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
yml配置
需要說明的是config: classpath:/ehcache.xml可以不用寫,因為預設就是這個路徑。但ehcache.xml必須有。
spring:
cache:
type: ehcache
ehcache:
config: classpath:/ehcache.xml
ehcache.xml
<ehcache>
<!--
磁碟儲存:將快取中暫時不使用的物件,轉移到硬碟,類似於Windows系統的虛擬記憶體
path:指定在硬碟上儲存物件的路徑
path可以配置的目錄有:
user.home(使用者的家目錄)
user.dir(使用者當前的工作目錄)
java.io.tmpdir(預設的臨時目錄)
ehcache.disk.store.dir(ehcache的配置目錄)
絕對路徑(如:d:\\ehcache)
檢視路徑方法:String tmpDir = System.getProperty("java.io.tmpdir");
-->
<diskStore path="java.io.tmpdir" />
<!--
defaultCache:預設的快取配置資訊,如果不加特殊說明,則所有物件按照此配置項處理
maxElementsInMemory:設定了快取的上限,最多儲存多少個記錄物件
eternal:代表物件是否永不過期 (指定true則下面兩項配置需為0無限期)
timeToIdleSeconds:最大的發呆時間 /秒
timeToLiveSeconds:最大的存活時間 /秒
overflowToDisk:是否允許物件被寫入到磁碟
說明:下列配置自快取建立起600秒(10分鐘)有效 。
在有效的600秒(10分鐘)內,如果連續120秒(2分鐘)未訪問快取,則快取失效。
就算有訪問,也只會存活600秒。
-->
<defaultCache maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="600" timeToLiveSeconds="600" overflowToDisk="true" />
<cache name="userCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="600" overflowToDisk="true" />
</ehcache>
開啟快取支援
在 Spring Boot 中使用 @EnableCaching 開啟快取支援。
@EnableCaching
@SpringBootApplication
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
}
}
Service層
建立一個服務類,新增@CacheConfig(cacheNames = {“userCache”})註解設定ehcache的名稱,這個名稱必須在ehcache.xml已配置 。
@Service
@CacheConfig(cacheNames = {"userCache"})
public class UserService {
}
首先,我們先來講解下 @Cacheable 註解。@Cacheable 在方法執行前 Spring 先檢視快取中是否有資料,如果有資料,則直接返回快取資料;若沒有資料,呼叫方法並將方法返回值放進快取。此處的value不是必需的,你可以使用@CacheConfig(cacheNames = {“userCache”})註解來統一指定value的值,這時可省略value,如果你在方法依舊寫上了value,那麼依然以方法的value值為準。注意:此處的User實體類一定要實現序列化,否則會報java.io.NotSerializableException異常。
@Cacheable(key = "#id")
public User findUser(int id) {
return userDao.findUser(id);
}
@CachePut註解的作用主要針對方法配置,能夠根據方法的請求引數對其結果進行快取,和@Cacheable不同的是,它每次都會觸發真實方法的呼叫 。簡單來說就是使用者更新快取資料。但需要注意的是該註解的value和key必須與要更新的快取相同,也就是與@Cacheable 相同。
@CachePut(key = "#id")
public User add(int id,String name,int age) {
User user = new User(id, name, age);
userDao.add(user);
return user;
}
@CachePut(key = "#id")
public User update(String name,int age, int id) {
User user = new User(id, name, age);
userDao.update(user);
return user;
}
@CacheEvict 將一條或多條資料從快取中刪除, 主要用於刪除方法,用來從快取中移除相應資料。
@CacheEvict(key = "#id")
public int delete(int id) {
return userDao.delete(id);
}
Controller層
為了展現效果,我們先定義一組簡單的 RESTful API 介面進行測試。
@RestController
@RequestMapping("/user/cache")
public class UserController {
@Autowired
private UserService userService;
@GetMapping(value = "/list")
public List<User> findUserList() {
return userService.findUserList();
}
@GetMapping(value = "/{id}")
public User findUser(@PathVariable("id") int id) {
return userService.findUser(id);
}
@PutMapping(value = "/{id}")
public String updateAccount(@PathVariable("id") int id, @RequestParam(value = "name", required = true) String name,
@RequestParam(value = "age", required = true) int age) {
if (0 < userService.update(name, age, id)) {
return "success";
} else {
return "fail";
}
}
@DeleteMapping(value = "/{id}")
public String delete(@PathVariable(value = "id") int id) {
if (0 < userService.delete(id)) {
return "success";
} else {
return "fail";
}
}
@PostMapping(value = "/add")
public String postAccount(@RequestParam(value = "id") int id, @RequestParam(value = "name") String name,
@RequestParam(value = "age") int age) {
if (0 < userService.add(id, name, age)) {
return "success";
} else {
return "fail";
}
}
}
測試
- 多次呼叫查詢介面
檢視日誌資訊
由上圖可知,只打印了一條日誌資訊,這說明快取起了作用。- 呼叫儲存介面,再呼叫查詢介面
檢視日誌資訊
由上圖可知,只打印了一條日誌資訊,這說明快取起了作用。
擴充套件閱讀
如果想更深入理解 Spring 的 Cache 機制,小編推薦一篇不錯的文章。
Spring Cache 抽象詳解