前言
Spring框架支援透明地嚮應用程式新增快取對快取進行管理,其管理快取的核心是將快取應用於運算元據的方法(包括增刪查改等),從而減少運算元據的執行次數(主要是查詢,直接從快取中讀取資料),同時不會對程式本身造成任何干擾。
SpringBoot繼承了Spring框架的快取管理功能,通過使用@EnableCaching註解開啟基於註解的快取支援,SpringBoot就可以啟動快取管理的自動化配置。
接下來針對SpringBoot支援的預設快取管理進行講解。
SpringBoot預設快取管理
1、基礎環境搭建
(1)準備資料
使用前面 SpringBoot資料訪問(一) SpringBoot整合Mybatis 一文中建立的資料庫springbootdata,該資料庫中包含兩張資料表:t_article和t_comment。
(2)建立專案,程式碼編寫
1、在專案依賴中新增SQL模組的JPA依賴、MySql依賴以及Web模組中的Web依賴,如下圖所示:
引入這三個依賴器建立專案,在專案pom.xml檔案會出現以下依賴:
2、編寫資料庫表對應的實體類,並使用JPA相關注解配置對映關係
package com.hardy.springbootdatacache.entity; import org.springframework.data.annotation.Id; import javax.persistence.*; /** * @Author: HardyYao * @Date: 2021/6/19 */ @Entity(name = "t_comment") // 設定ORM實體類,並指定對映的表名 public class Comment { @Id // 對映對應的主鍵id @GeneratedValue(strategy = GenerationType.IDENTITY) // 設定主鍵自增策略 private Integer id; private String content; private String author; @Column(name = "a_id") // 指定對映的表欄位名 private Integer aId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Integer getaId() { return aId; } public void setaId(Integer aId) { this.aId = aId; } @Override public String toString() { return "Comment{" + "id=" + id + ", content='" + content + '\'' + ", author='" + author + '\'' + ", aId=" + aId + '}'; } }
3、編寫資料庫操作的Repository介面檔案
package com.hardy.springbootdatacache.repository; import com.hardy.springbootdatacache.entity.Comment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.transaction.annotation.Transactional; /** * @Author: HardyYao * @Date: 2021/6/19 */ public interface CommentRepository extends JpaRepository<Comment, Integer> { /** * 根據評論id修改評論作者author * @param author * @param id * @return */ @Transactional @Modifying @Query("update t_comment c set c.author = ?1 where c.id=?2") int updateComment(String author,Integer id); }
4、編寫service層
package com.hardy.springbootdatacache.service; import com.hardy.springbootdatacache.entity.Comment; import com.hardy.springbootdatacache.repository.CommentRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Optional; /** * @Author: HardyYao * @Date: 2021/6/19 */ @Service public class CommentService { @Autowired private CommentRepository commentRepository; /** * 根據評論id查詢評論 * @Cacheable:將該方法的查詢結果comment存放在SpringBoot預設快取中 * cacheNames:起一個快取名稱空間,對應快取唯一標識 * @param id * @return */ @Cacheable(cacheNames = "comment") public Comment findCommentById(Integer id){ Optional<Comment> comment = commentRepository.findById(id); if(comment.isPresent()){ Comment comment1 = comment.get(); return comment1; } return null; } }
5、編寫controller層
package com.hardy.springbootdatacache.controller; import com.hardy.springbootdatacache.entity.Comment; import com.hardy.springbootdatacache.service.CommentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: HardyYao * @Date: 2021/6/19 */ @RestController public class CommentController { @Autowired private CommentService commentService; @RequestMapping(value = "/findCommentById") public Comment findCommentById(Integer id){ Comment comment = commentService.findCommentById(id); return comment; } }
6、編寫配置檔案
在全域性配置檔案application.properties中編寫對應的資料庫連線配置
# MySQL資料庫連線配置 spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root # 顯示使用JPA進行資料庫查詢的SQL語句 spring.jpa.show-sql=true # 開啟駝峰命名匹配對映 mybatis.configuration.map-underscore-to-camel-case=true # 解決中文亂碼問題 spring.http.encoding.force-response=true
7、測試
在瀏覽器中輸入:http://localhost:8080/findCommentById?id=1 進行訪問(連續訪問三次):
在上圖中,因為沒有在SpringBoot專案中開啟快取管理,故雖然資料表中的資料沒有任何變化,但是每執行一次查詢操作,即便執行的是相同的SQL語句,都還是會訪問一次資料庫。
2、預設快取使用
在前面搭建的Web應用的基礎上,開啟SpringBoot預設支援的快取,以使用SpringBoot預設快取。
1、在專案啟動類的類名上方使用@EnableCaching註解開啟基於註解的快取支援
package com.hardy.springbootdatacache; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching // 開啟SpringBoot基於註解的快取管理支援 @SpringBootApplication public class SpringbootdataCacheApplication { public static void main(String[] args) { SpringApplication.run(SpringbootdataCacheApplication.class, args); } }
2、使用@Cacheable註解對資料操作方法進行快取管理
將@Cacheable註解標註在Service類的查詢方法上,對查詢結果進行快取:
package com.hardy.springbootdatacache.service; import com.hardy.springbootdatacache.entity.Comment; import com.hardy.springbootdatacache.repository.CommentRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.Optional; /** * @Author: HardyYao * @Date: 2021/6/19 */ @Service public class CommentService { @Autowired private CommentRepository commentRepository; /** * 根據評論id查詢評論 * @param id * @return */ @Cacheable(cacheNames = "comment") public Comment findCommentById(Integer id){ Optional<Comment> comment = commentRepository.findById(id); if(comment.isPresent()){ Comment comment1 = comment.get(); return comment1; } return null; } }
3、測試訪問
在瀏覽器中輸入:http://localhost:8080/findCommentById?id=1 進行訪問(連續訪問三次):
可以看到,在使用SpringBoot預設快取註解後,重複進行同樣的查詢操作,資料庫只執行了一次SQL查詢語句,說明專案開啟的預設快取支援已生效。
SpringBoot預設快取底層結構:在諸多的快取自動配置類中,SpringBoot預設裝配的是SimpleCacheConfiguration,它使用的CacheManager是ConcurrentMapCacheManager,使用ConcurrentMap作為底層的資料結構,根據Cache的名字查詢出Cache,每一個Cache中存在多個key-value鍵值對、快取值。
4、快取註解介紹
前面我們通過使用@EnableCaching、@Cacheable註解實現了SpringBoot預設的基於註解的快取管理,除此之外,還有其它註解及註解屬性也可用於配置優化快取管理。下面,我們對@EnableCaching、@Cacheable及其他與快取管理相關的註解進行介紹。
4.1、@EnableCaching註解
@EnableCaching註解是由Spring框架提供的,SpringBoot框架對該註解進行了繼承,該註解需要配置在類的上方(一般配置在專案啟動類上),用於開啟基於註解的快取支援。
4.2、@Cacheable註解
@Cacheable註解也是由Spring框架提供的,可以作用於類或方法上(通常作用於資料查詢方法上),用於對方法的執行結果進行資料快取儲存。註解的執行順序是:先進行快取查詢,如果為空則進行方法查詢(查資料庫),並將結果進行快取;如果快取中有資料,則不進行方法查詢,而是直接使用快取資料。
@Cacheable註解提供了多個屬性,用於對快取儲存進行相關設定,如下所示:
執行流程&時機
方法執行之前,先去查詢Cache(快取元件),按照cacheNames指定的名字獲取,(CacheManager先獲取相應的快取),第一次獲取快取如果獲取不到,Cache元件會自動建立。
去Cache中查詢快取的內容,使用一個key進行查詢,預設在只有一個引數的情況下,key值預設就是方法的引數;如果有多個引數或者沒有引數,則SpringBoot會按照某種策略生成key,預設是使用KeyGenerator生成的,其實現類為SimpleKeyGenerator。
SimpleKeyGenerator生成key的預設策略:
常用的SPEL表示式:
4.3、@CachePut註解
目標方法執行完之後生效。@CachePut被使用於修改操作較多,若快取中已經存在目標值了,該註解作用的方法依然會執行,執行後將結果儲存在快取中(覆蓋掉原來的目標值)。
@CachePut註解也提供了多個屬性,這些屬性與@Cacheable註解的屬性完全相同。
4.4、@CacheEvict註解
@CacheEvict註解也是由Spring框架提供的,可以作用於類或方法上(通常作用於資料刪除方法上),該註解的作用是刪除快取資料。
@CacheEvict註解的預設執行順序是:先進行方法呼叫,然後將快取進行清除。