Spring Cache和EhCache實現緩存管理方式
1、認識 Spring Cache
Spring Cache是Spring提供的一整套緩存解決方案。它本身並不提供緩存實現,而是提供統一的接口和代碼規范、配置、註解等,以便整合各種Cache方案,使用戶不用關心Cache的細節。
Spring支持“透明”地向應用程序添加緩存,將緩存應用於方法,在方法執行前檢查緩存中是否有可用的數據。這樣可以減少方法執行的次數,同時提高響應的速度。緩存的應用方式“透明”,不會對調用者造成任何幹擾。隻要通過註解@EnableCaching啟用瞭緩存支持,Spring Boot就會自動處理好緩存的基礎配置。
Spring Cache作用在方法上。當調用一個緩存方法時,會把該方法參數和返回結果作為一個“鍵值對”(key / value)存放在緩存中,下次用同樣的參數來調用該方法時將不再執行該方法,而是直接從緩存中獲取結果進行返回。所以在使用Spring Cache時,要保證在緩存的方法和方法參數相同時返回相同的結果。
Spring Boot提供的聲明式緩存(cache)註解,如下表:
註解 | 說明 |
---|---|
@EnableCaching | 開啟緩存。 |
@Cacheable | 可以作用在類和方法上,以鍵值對的方式緩存類或方法的返回值。 |
@CachePut | 方法被調用,然後結果被緩存。 |
@CacheEvict | 清空緩存。 |
@Caching | 用來組合多個註解標簽。 |
2、認識 EhCache
Spring Boot支持多種不同的緩存產品。在默認情況下使用的是簡單緩存,不建議在正式環境中使用。我們可以配置一些更加強大的緩存,比如Ehcache。
Ehcache是一種廣泛使用的開源Java分佈式緩存,它具有內存和磁盤存儲、緩存加載器、緩存擴展、緩存異常處理、GZIP緩存、Servlet 過濾器,以及支持 REST 和 SOAP API 等特點。
3、創建SpringBoot與MyBatis的整合項目
【實例】創建SpringBoot與MyBatis的整合項目,實現用戶信息的查詢、新增、修改、刪除功能。並使用 Spring Cache 和 EhCache 實現緩存管理,執行結果如下圖:
3.1 創建數據表
在MySQL數據庫中創建用戶信息表(tb_user),並添加數據。
-- 判斷數據表是否存在,存在則刪除 DROP TABLE IF EXISTS tb_user; -- 創建“用戶信息”數據表 CREATE TABLE IF NOT EXISTS tb_user ( user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用戶編號', user_name VARCHAR(50) NOT NULL COMMENT '用戶姓名', age INT DEFAULT(0) NOT NULL COMMENT '年齡', blog_url VARCHAR(50) NOT NULL COMMENT '博客地址', blog_remark VARCHAR(50) COMMENT '博客信息' ) COMMENT = '用戶信息表'; -- 添加數據 INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES('pan_junbiao的博客',32,'https://blog.csdn.net/pan_junbiao','您好,歡迎訪問 pan_junbiao的博客');
3.2 創建項目
(1)創建SpringBoot項目,項目結構如下圖:
(2)添加pom.xml配置信息
在pom.xml配置文件中添加MyBatis、 MySQL的JDBC數據庫驅動、Spring Boot 緩存支持啟動器、Ehcache 緩存等。
<!-- MyBatis與SpringBoot整合依賴 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <!-- MySQL的JDBC數據庫驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- 引入Thymeleaf模板引擎 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- Spring Boot緩存支持啟動器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> <version>2.3.2.RELEASE</version> </dependency> <!-- Ehcache緩存管理器 --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>
(3)配置相關信息
將默認的application.properties文件的後綴修改為“.yml”,即配置文件名稱為:application.yml,並配置以下信息:
#Spring配置 spring: #緩存管理器 cache: type: ehcache ehcache: config: classpath:ehcache.xml #緩存加載配置文件 #使用Thymeleaf模板引擎 thymeleaf: mode: HTML5 encoding: UTF-8 cache: false #使用Thymeleaf模板引擎,關閉緩存 servlet: content-type: text/html #DataSource數據源 datasource: url: jdbc:mysql://localhost:3306/db_admin?useSSL=false& username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver #MyBatis配置 mybatis: type-aliases-package: com.pjb.entity #別名定義 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #指定 MyBatis 所用日志的具體實現,未指定時將自動查找 map-underscore-to-camel-case: true #開啟自動駝峰命名規則(camel case)映射 lazy-loading-enabled: true #開啟延時加載開關 aggressive-lazy-loading: false #將積極加載改為消極加載(即按需加載),默認值就是false #lazy-load-trigger-methods: "" #阻擋不相幹的操作觸發,實現懶加載 cache-enabled: true #打開全局緩存開關(二級環境),默認值就是true
4、配置EhCache緩存管理器
4.1 創建 ehcache.xml 配置文件
在 resources (資源目錄)下,創建 ehcache.xml 配置文件,配置信息如下:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"> <!-- 這個是磁盤存儲路徑,當內存緩存滿瞭的時候,就會往這裡面放, java.io.tmdir是操作系統緩存的臨時目錄,不同操作系統緩存目錄不一樣 --> <diskStore path="java.io.tmpdir"/> <!--defaultCache:echcache的默認緩存策略 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <cache name="userCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </cache> </ehcache>
配置屬性說明:
屬性 | 說明 |
---|---|
<diskStore path=”java.io.tmpdir”/> | 這個是磁盤存儲路徑,當內存緩存滿瞭的時候,就會往這裡面放,java.io.tmdir是操作系統緩存的臨時目錄,不同操作系統緩存目錄不一樣。 |
maxElementsInMemory | 內存緩存中最多可以存放的元素數量,若放入Cache中的元素超過這個數值,則有以下兩種情況: (1)若 overflowToDisk=true,則會將Cache中多出的元素放入磁盤文件中。 (2)若 overflowToDisk=false,則根據memoryStoreEvictionPolicy策略替換Cache中原有的元素。 |
overflowToDisk | 內存不足時,是否啟用磁盤緩存。 |
eternal | 緩存中對象是否永久有效。 |
timeToIdleSeconds | 緩存數據在失效前的允許閑置時間(單位:秒),僅當 eternal=false 時使用,默認值是0表示可閑置時間無窮大,若超過這個時間沒有訪問此Cache中的某個元素,那麼此元素將被從Cache中清除。 |
timeToLiveSeconds | 緩存數據的總的存活時間(單位:秒),僅當 eternal=false 時使用,從創建開始計時,失效結束。 |
maxElementsOnDisk | 磁盤緩存中最多可以存放的元素數量,0表示無窮大。 |
diskExpiryThreadIntervalSeconds | 磁盤緩存的清理線程運行間隔,默認是120秒。 |
memoryStoreEvictionPolicy | 內存存儲與釋放策略,即達到 maxElementsInMemory 限制時,Ehcache會根據指定策略清理內存,共有三種策略,分別為LRU(最近最少使用)、LFU(最常用的)、FIFO(先進先出)。 |
defaultCache | 默認緩存方式。 |
cache | 自定義的緩存方式,自行設置 name。 |
4.2 配置緩存管理器
在 application.yml 配置文件中配置目標緩存管理器,支持 Ehcache、Generic、Redis、Jcache等。這裡配置使用Ehcache。
#Spring配置 spring: #緩存管理器 cache: type: ehcache ehcache: config: classpath:ehcache.xml #緩存加載配置文件
4.3 開啟緩存功能
在SpringBoot項目啟動入口類中添加註解@EnableCaching,開啟緩存功能。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching public class SpringcacheDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringcacheDemoApplication.class, args); } }
5、使用EhCache實現緩存管理
5.1 創建實體類(Entity層)
在com.pjb.entity包中,創建UserInfo類(用戶信息實體類)。
package com.pjb.entity; import java.io.Serializable; /** * 用戶信息實體類 * @author pan_junbiao **/ public class UserInfo implements Serializable { private int userId; //用戶編號 private String userName; //用戶姓名 private int age; //年齡 private String blogUrl; //博客地址 private String blogRemark; //博客信息 //省略getter與setter方法... }
註意:實體類必須實現 Serializable 接口,否則無法實現緩存功能。
5.2 數據庫映射層(Mapper層)
在com.pjb.mapper包中,創建UserMapper接口(用戶信息Mapper動態代理接口)。
package com.pjb.mapper; import com.pjb.entity.UserInfo; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; /** * 用戶信息Mapper動態代理接口 * @author pan_junbiao **/ @Mapper @Repository public interface UserMapper { /** * 根據用戶ID,獲取用戶信息 */ @Select("SELECT * FROM tb_user WHERE user_id = #{userId}") public UserInfo getUserById(int userId); /** * 新增用戶,並獲取自增主鍵 */ @Insert("INSERT INTO tb_user(user_name,age,blog_url,blog_remark) VALUES(#{userName},#{age},#{blogUrl},#{blogRemark});") @Options(useGeneratedKeys = true, keyColumn = "user_id", keyProperty = "userId") public int insertUser(UserInfo userInfo); /** * 修改用戶 */ @Update("UPDATE tb_user SET user_name = #{userName} ,age = #{age} ,blog_url = #{blogUrl} ,blog_remark = #{blogRemark} WHERE user_id = #{userId}") public int updateUser(UserInfo userInfo); /** * 刪除用戶 */ @Delete("DELETE FROM tb_user WHERE user_id = #{userId}") public int deleteUser(int userId); }
5.3 業務邏輯層(Service層)
在com.pjb.service包下,創建UserService接口(用戶信息業務邏輯接口)。
package com.pjb.service; import com.pjb.entity.UserInfo; /** * 用戶信息業務邏輯接口 * @author pan_junbiao **/ public interface UserService { /** * 根據用戶ID,獲取用戶信息 */ public UserInfo getUserById(int userId); /** * 新增用戶,並獲取自增主鍵 */ public UserInfo insertUser(UserInfo userInfo); /** * 修改用戶 */ public UserInfo updateUser(UserInfo userInfo); /** * 刪除用戶 */ public int deleteUser(int userId); }
在com.pjb.service.impl包下,創建UserServiceImpl類(用戶信息業務邏輯類)。
package com.pjb.service.impl; import com.pjb.entity.UserInfo; import com.pjb.mapper.UserMapper; import com.pjb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; 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; /** * 用戶信息業務邏輯類 * @author pan_junbiao **/ //註意:必須對應配置文件ehcache.xml中cache節點的name屬性值 //@CacheConfig(cacheNames = "userCache") @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; //註意:必須對應配置文件ehcache.xml中cache節點的name屬性值 private static final String CACHE_NAME = "userCache"; /** * 根據用戶ID,獲取用戶信息 */ @Override @Cacheable(value = CACHE_NAME, key = "#userId") public UserInfo getUserById(int userId) { return userMapper.getUserById(userId); } /** * 新增用戶,並獲取自增主鍵 */ @Override @CachePut(value = CACHE_NAME, key = "#userInfo.userId") public UserInfo insertUser(UserInfo userInfo) { userMapper.insertUser(userInfo); return userInfo; } /** * 修改用戶 */ @Override @CachePut(value = CACHE_NAME, key = "#userInfo.userId") public UserInfo updateUser(UserInfo userInfo) { userMapper.updateUser(userInfo); return userInfo; } /** * 刪除用戶 */ @Override @CacheEvict(value = CACHE_NAME, key = "#userId") public int deleteUser(int userId) { return userMapper.deleteUser(userId); } }
從上述代碼可以看出,查詢用戶的方法使用瞭 @Cacheable 註解來開啟緩存。添加和修改方法使用瞭 @CachePut 註解,它是先處理方法,然後把結果進行緩存的。要想刪除數據,則需要使用 @CacheEvict 註解來清空緩存。
@CacheConfig註解:如果所有的 @Cacheable() 裡面都有一個 value=“xxx” 的屬性,這顯然如果方法多瞭,寫起來也是挺累的,如果可以一次性聲明完 那就省事瞭,所以有瞭 @CacheConfig 這個配置,@CacheConfig is a class-level annotation that allows to share the cache names,如果你在方法寫別的名字,那麼依然以方法的名字為準。
5.4 控制器方法(Controller層)
在com.pjb.controller包中,創建UserController類(用戶控制器),實現用戶數據的查詢、新增、修改、刪除,並實現數據的返回。
package com.pjb.controller; import com.pjb.entity.UserInfo; import com.pjb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; /** * 用戶信息控制器 * @author pan_junbiao **/ @Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 獲取用戶信息 */ @RequestMapping("getUserById") public ModelAndView getUserById(int userId) { //根據用戶ID,獲取用戶信息 UserInfo userInfo = userService.getUserById(userId); if(userInfo==null) { userInfo = new UserInfo(); } //返回結果 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("userInfo",userInfo); modelAndView.setViewName("/user-info.html"); return modelAndView; } /** * 新增用戶 */ @ResponseBody @RequestMapping("insertUser") public boolean insertUser() { //創建新用戶 UserInfo userInfo = new UserInfo(); userInfo.setUserName("pan_junbiao的博客"); userInfo.setAge(32); userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao"); userInfo.setBlogRemark("您好,歡迎訪問 pan_junbiao的博客"); //執行新增方法 userService.insertUser(userInfo); //返回結果 return userInfo.getUserId() > 0 ? true : false; } /** * 修改用戶 */ @ResponseBody @RequestMapping("updateUser") public boolean updateUser(int userId) { UserInfo userInfo = new UserInfo(); userInfo.setUserId(userId); userInfo.setUserName("pan_junbiao的博客_02"); userInfo.setAge(35); userInfo.setBlogUrl("https://blog.csdn.net/pan_junbiao"); userInfo.setBlogRemark("您好,歡迎訪問 pan_junbiao的博客"); //執行修改方法 userService.updateUser(userInfo); //返回結果 return true; } /** * 刪除用戶 */ @ResponseBody @RequestMapping("deleteUser") public boolean deleteUser(int userId) { //執行新增方法 int result = userService.deleteUser(userId); //返回結果 return result > 0 ? true : false; } }
5.5 顯示頁面(View層)
在 resources/templates 目錄下,創建 user-info.html 用戶信息顯示頁面。
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>用戶信息</title> <meta name="author" content="pan_junbiao的博客"> <style> table { border-collapse: collapse; margin-bottom: 10px} table,table tr th, table tr td { border:1px solid #000000; padding: 5px 10px;} </style> </head> <body> <div align="center"> <table> <caption>用戶信息</caption> <tr> <th>用戶ID:</th> <td th:text="${userInfo.userId}"></td> </tr> <tr> <th>用戶名稱:</th> <td th:text="${userInfo.userName}"></td> </tr> <tr> <th>年齡:</th> <td th:text="${userInfo.age}"></td> </tr> <tr> <th>博客地址:</th> <td th:text="${userInfo.blogUrl}"></td> </tr> <tr> <th>備註信息:</th> <td th:text="${userInfo.blogRemark}"></td> </tr> </table> </div> </body> </html>
至此,項目已經編寫完成,執行結果如下圖:
接著運行項目的其他方法,然後多次訪問查詢方法的URL,體驗緩存效果。主要觀察數據庫是否進行瞭操作,如果數據庫沒有操作數據而正常返回數據,則代表緩存成功。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- SpringBoot整合Druid實現數據庫連接池和監控
- SpringBoot與SpringCache概念用法大全
- Spring使用@Value註解與@PropertySource註解加載配置文件操作
- Spring Boot 整合 TKMybatis 二次簡化持久層代碼的實現
- 一篇超詳細的Spring Boot整合Mybatis文章