SpringBoot配置Ehcache緩存


一、Ehcache介紹

EhCache 是一個純Java的進程內緩存框架,具有快速、精干等特點,是Hibernate中默認CacheProvider。Ehcache是一種廣泛使用的開源Java分布式緩存。主要面向通用緩存,Java EE和輕量級容器。它具有內存和磁盤存儲,緩存加載器,緩存擴展,緩存異常處理程序,一個gzip緩存servlet過濾器,支持REST和SOAP API等特點。

主要的特性有:

  1. 快速

  2. 簡單

  3. 多種緩存策略

  4. 緩存數據有兩級:內存和磁盤,因此無需擔心容量問題

  5. 緩存數據會在虛擬機重啟的過程中寫入磁盤

  6. 可以通過RMI、可插入API等方式進行分布式緩存

  7. 具有緩存和緩存管理器的偵聽接口

  8. 支持多緩存管理器實例,以及一個實例的多個緩存區域

  9. 提供Hibernate的緩存實現

二、Spring緩存抽象

Spring從3.1開始定義了org.springframework.cache.Cache 和 org.springframework.cache.CacheManager 接口來統一不同的緩存技術;

  • Cache接口為緩存的組件規范定義,包含緩存的各種操作集合;

  • Cache接口下spring提供了各種xxxCache的實現,比如EhCacheCache、RedisCache等等

  • 每次調用需要緩存功能的方法時,Spring會檢查指定參數的指定目標方法是否已經被調用過;如果有緩存就直接從緩存中獲取結果,沒有就調用方法並緩存結果后返回給用戶。下次調用則直接從緩存中獲取。

1、緩存注解概念

Cache 緩存接口,定義緩存操作。實現有:EhCacheCache、RedisCache等等
CacheManager 緩存管理器,管理各種緩存組件
@Cacheable 主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存
@CacheEvict 清空緩存
@CachePut 保證方法被調用,又希望結果被緩存
@EnableCaching 開啟基於注解的緩存

 

三、SpringBoot 添加 EhCache緩存

1、pom.xml 添加依賴

<!--ehcache 緩存-->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.2</version>
</dependency><dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2、resources目錄下創建ehcache.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
    <!-- 磁盤緩存位置 -->
    <diskStore path="E:\data"/><!-- 默認緩存 -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    
    <!-- cache 可以設置多個,例如localCache、UserCache和HelloWorldCache -->
    <cache name="localCache"
           eternal="true"
           maxElementsInMemory="100"
           maxElementsOnDisk="1000"
           overflowToDisk="true"
           diskPersistent="true"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU"/><cache name="UserCache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="10"
           timeToLiveSeconds="10"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU"/><!-- hello world緩存 -->
    <cache name="HelloWorldCache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="5"
           timeToLiveSeconds="5"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU"/>
    <!-- memoryStoreEvictionPolicy Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)-->
    <!-- 緩存配置
     name:緩存名稱。
     maxElementsInMemory:緩存最大個數。
     eternal:對象是否永久有效,一但設置了,timeout將不起作用。
     timeToIdleSeconds:設置對象在失效前的允許閑置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閑置時間無窮大。
     timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介於創建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。
     overflowToDisk:當內存中對象數量達到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。 diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩沖區。
     maxElementsOnDisk:硬盤最大緩存個數。
     diskPersistent:是否緩存虛擬機重啟期數據 Whether the disk
     store persists between restarts of the Virtual Machine. The default value is false.
     diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。
     memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置為FIFO(先進先出)或是LFU(較少使用)。
     clearOnFlush:內存數量最大時是否清除。
     --></ehcache>

3、創建測試方法測試CacheManager

@Test
void cacheManagerTest() {
    // 1. 創建緩存管理器
    CacheManager cacheManager = CacheManager.create(this.getClass().getResourceAsStream("/ehcache.xml"));
​
    // 2. 獲取ehcache.xml 中定義的 HelloWorldCache 緩存對象
    Cache cache = cacheManager.getCache("HelloWorldCache");
​
    // 3. 創建元素
    Element element = new Element("key1", "value1");
​
    // 4. 將元素添加到緩存
    cache.put(element);
​
    // 5. 獲取緩存
    Element value = cache.get("key1");
    System.out.println(value);
    System.out.println(value.getObjectKey());
    System.out.println(value.getObjectValue());
​
    // 6. 刪除元素
    cache.remove("key1");
    System.out.println(cache.getSize());
​
    // 7. 刷新緩存
    cache.flush();
​
    // 8. 關閉緩存管理器
    cacheManager.shutdown();
}

運行結果如下:

[ key = key1, value=value1, version=1, hitCount=1, CreationTime = 1630287141405, LastAccessTime = 1630287141406 ]
key1
value1
0

四、EhCache封裝

1、封裝CacheManagerHelper

package com.example.ehcachedemo.helper;
​
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
​
/*
 * Ehcache 緩存管理對象單例
 * 磁盤 + 內存
 * */
public class CacheManagerHelper {
    private final String EHCAHE_PATH = "/ehcache.xml";
    private final String CACHE_NAME = "localCache";
    private CacheManager manager;
    private Cache cache;
​
    private CacheManagerHelper() {
        init();
    }
​
    private static class SingletonInstance {
        private static final CacheManagerHelper singleton = new CacheManagerHelper();
    }
​
    public static CacheManagerHelper getInstance() {
        return SingletonInstance.singleton;
    }
​
    /**
     * 每次開始使用緩存對象需要初始化
     */
    public void init() {
        manager = CacheManager.create(this.getClass().getResourceAsStream(EHCAHE_PATH));
        cache = manager.getCache(CACHE_NAME);
    }
​
    /**
     * 把key放入緩存中
     */
    public void put(String key, Object value) {
        cache.put(new Element(key, value));
        flush();
    }
​
    /**
     * 根據key獲取緩存元素
     */
    public Object get(String key) {
        Element element = cache.get(key);
        return element != null ? element.getObjectValue() : null;
    }
​
    /**
     * 根據key移除緩存
     */
    public void remove(String key) {
        cache.remove(key);
        flush();
    }
​
    /**
     * 構建內存與磁盤的關系
     */
    public void flush() {
        cache.flush();
    }
​
    /**
     * 關閉緩存管理器
     */
    public void shutdown() {
        manager.shutdown();
    }
}

2、新建User類和UserController測試CacheManagerHelper

User:

package com.example.ehcachedemo.bean;
​
public class User {
​
    public User() {
​
    }
​
    public User(String userId, String name, int age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
​
    private String userId;
    private String name;
    private int age;
​
    //region getter and setter
    public String getUserId() {
        return userId;
    }
​
    public void setUserId(String userId) {
        this.userId = userId;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
    //endregion
​
    @Override
    public String toString() {
​
        return "User{" +
                "userId='" + userId + '\'' +
                ", name='" + name + '\'' +
                ", age=" + this.age +
                '}';
    }
}

UserController

package com.example.ehcachedemo.controller;
​
import com.example.ehcachedemo.bean.User;
import com.example.ehcachedemo.helper.CacheManagerHelper;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping(value = "/user")
public class UserController {
​
    /**
     * 通過CacheManagerHelper獲取緩存信息
     * @param id 訪問id
     * */
    @GetMapping("byId/{id}")
    public String getUserById(@PathVariable String id) {
        String result = (String) CacheManagerHelper.getInstance().get(id);
        if (result == null) {
            User user = new User(id, "張三", (int) (Math.random() * 100));
            result = user.toString();
            CacheManagerHelper.getInstance().put(id, result);
        }
        return result;
    }
}

3、封裝EhCacheConfiguration類,用於注解方式使用Ehcache

package com.example.ehcachedemo.configuration;
​
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
​
@Configuration
@EnableCaching
public class EhCacheConfiguration {
    @Bean
    public EhCacheManagerFactoryBean cacheManagerFactoryBean() {
        EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
        bean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        bean.setShared(true);
        return bean;
    }
​
    @Bean
    public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean) {
        return new EhCacheCacheManager(bean.getObject());
    }
}

Application啟動類需要加入  @EnableCaching 

4、UserController 注解方式使用EhCache

    /**
     * 通過注解方式,如果緩存存在,則從緩存獲取數據,否則從方法體獲取,並更新到緩存中
     * @param id 訪問id
     * */
    @GetMapping("ByIdWithInject/{id}")
    @Cacheable(value = "localCache", key = "#id")
    public String getUserByIdWithInject(@PathVariable String id) {
        User user = new User(id, "張三", (int) (Math.random() * 100));
        return user.toString();
    }

5、@Cacheable、@CachePut、@CacheEvict的使用

在Service使用@Cacheable、@CachePut、@CacheEvict

package com.example.ehcachedemo.service;
​
import com.example.ehcachedemo.bean.User;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
​
import java.util.Date;
​
@Service
@Component
public class UserServiceImpl implements UserService {
​
    /**
     * @param userId
     * @return
     * @Cacheable: 1.方法運行之前,先去查詢Cache(緩存組件),按照cacheNames指定的名字獲取;
     * 2.去Cache中查找緩存的內容,使用一個key,默認就是方法的參數;
     * 3.沒有查到緩存就調用目標方法
     * 4.將目標方法返回的結果,放進緩存中
     * 5.condition:指定符合條件的情況下才緩存;
     */
    @Cacheable(value = "UserCache", key = "#userId", condition = "#userId!=''")
    @Override
    public User findById(String userId) {
        System.out.println(new Date().getTime() + "進入UserService.findById,當前userId為:" + userId);
        return new User(userId, "張三", (int) (Math.random() * 100));
    }
​
    /**
     * @param user
     * @return
     * @CachePut:既調用方法,又更新緩存數據;同步更新緩存 運行時機:
     * 1、先調用目標方法
     * 2、將目標方法的結果緩存起來
     */
    @CachePut(value = "UserCache", key = "#user.userId")
    @Override
    public User updateUser(User user) {
        System.out.println(new Date().getTime() + "進入UserService.updateUser,當前userId為:" + userId);
        return new User(userId, "張三", (int) (Math.random() * 100));
    }
​
    /**
     * @param userId
     * @CacheEvict:緩存清除 key:指定要清除的數據
     * beforeInvocation = true:代表清除緩存操作是在方法運行之前執行,無論方法是否出現異常,緩存都清除
     */
    @CacheEvict(value = "UserCache", key = "#userId", beforeInvocation = true)
    @Override
    public void deleteUser(String userId) {
        System.out.println(new Date().getTime() + "進入UserService.deleteUser,當前userId為:" + userId);
    }
}

測試代碼

    @Autowired
    UserService userService;
​
    @Test
    void userTest() throws InterruptedException {
        // 1.新增
        User user1 = new User("123", "張三", (int) (Math.random() * 100));
        userService.updateUser(user1);
        System.out.println("初始:" + user1);
​
        // 2.查詢
        user1 = userService.findById("123");
        System.out.println("查詢:" + user1);
​
        // 3.清除
        userService.deleteUser("123");
        System.out.println("已清除緩存");
​
        // 4.查詢
        user1 = userService.findById("123");
        System.out.println("查詢:" + user1);
​
        System.out.println("休眠10秒后重新查詢");
        Thread.sleep(10 * 1000);  // 休眠10秒,測試是否過期重新獲取
        user1 = userService.findById("123");
        System.out.println(user1);
    }

測試結果

1630291225178進入UserService.updateUser,當前userId為:123
初始:User{userId='123', name='張三', age=82}
查詢:User{userId='123', name='張三', age=82}
1630291225232進入UserService.deleteUser,當前userId為:123
已清除緩存
1630291225232進入UserService.findById,當前userId為:123
查詢:User{userId='123', name='張三', age=21}
休眠10秒后重新查詢
1630291235234進入UserService.findById,當前userId為:123
User{userId='123', name='張三', age=13}

五、參考文檔

https://blog.csdn.net/huang_wei_cai/article/details/105293166

https://www.jianshu.com/p/154c82073b07


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM