前言
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注解的默認執行順序是:先進行方法調用,然后將緩存進行清除。