SpringBoot 集成 Ehcache 实现本地缓存

目录

1、Ehcache 简介

EhCache 是一种广泛使用的开源 Java 分布式缓存。主要面向通用缓存、Java EE 和轻量级容器,可以和大部分 Java 项目无缝整合。

Ehcache 虽然也支持分布式模式,但是分布式方案不是很好,建议只将其作为单机的进程内缓存使用

特点:

  • 直接在 JVM 虚拟机中缓存,速度快,效率高
  • 支持多种缓存策略:LRU、LFU、FIFO 淘汰算法
  • 支持内存和磁盘存储,默认存储在内存中,如内存不够时把缓存数据同步到磁盘中;
  • 支持多缓存管理器实例,以及一个实例的多个缓存区域
  • 支持基于 Filter 的 Cache 实现,也支持 Gzip 压缩算法
  • EhCache 可以单独使用,一般在第三方库中被用到的比较多【mybatis、shiro】;EhCache 对分布式支持不够好,多个节点通过组播方式同步,效率不高,通常和 Redis 一块使用【通过 RMI 或者 Jgroup 多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适】

2、Ehcache 集群方式

Ehcache 目前支持五种集群方式:

  • RMI:使用组播方式通知所有节点同步数据。如果网络有问题,或某台服务宕机,则存在数据无法同步的可能,导致数据不一致。
  • JMS。JMS 类似 MQ,所有节点订阅消息,当某节点缓存发生变化,就向 JMS 发消息,其他节点感知变化后,同步数据
  • JGroup
  • Terracotta
  • Ehcache Server

3、工作原理

3.1、缓存写入

当应用程序向 Ehcache 中写入数据时,Ehcache 会首先检查该数据是否已经存在于缓存中。如果数据已经存在于缓存中,则更新该数据;否则,将该数据写入缓存。如下:

  1. 当应用程序请求写入一个数据项到 Ehcache 中时,这个数据项被传递给Ehcache API
  2. Ehcache 首先根据该数据项的键值对定位其对应的 Cache 对象
  3. Ehcache 根据配置中的缓存策略,比如是否应该在缓存中创建一个新的元素,以及新元素是否会淘汰老的元素
  4. 如果需要创建一个新缓存元素,则 Ehcache 创建一个新的元素并将其添加到 Cache 对象中
  5. 如果缓存中已经存在该元素,Ehcache 会根据缓存策略对该元素进行更新或替换
  6. Ehcache 将更新后的 Cache 对象返回给应用程序

3.2、缓存查找

当应用程序需要查询缓存中的数据时,Ehcache 首先会检查该数据是否存在于缓存中。如果数据存在于缓存中,则直接返回缓存数据;否则,从数据库或其他资源获取数据,并将其存入缓存中。如下:Ehcache 缓存查找的详细流程。

  1. 当应用程序请求从 Ehcache 中读取一个数据项时,这个请求被传递给 Ehcache API。
  2. Ehcache 首先根据该数据项的键值对定位其对应的 Cache 对象。
  3. Ehcache 检查该数据项是否已经存在于缓存中。
  4. 如果数据项存在于缓存中,Ehcache 可以直接将其返回给应用程序。
  5. 如果数据项不存在于缓存中,Ehcache 就需要从数据库或其他数据源(如文件、网络等)中获取数据。
  6. 获取到数据后,Ehcache 会将其添加到缓存中并返回给应用程序

3.3、缓存过期和驱逐

Ehcache 提供了多种缓存失效策略,例如基于时间的缓存失效、基于访问的缓存失效、基于大小的缓存失效等。当缓存数据过期或缓存空间不足时,Ehcache 会选择一部分缓存元素进行驱逐以腾出更多的内存空间。如下:

  1. Ehcache 会周期性地扫描缓存中的元素来标记那些已经过期的元素。
  2. Ehcache根据缓存策略(如基于时间、基于访问、基于大小等)判断哪些缓存元素应该被驱逐。
  3. 驱逐过程通常是异步执行的,Ehcache 会在后台线程上执行这个操作

3.4、缓存持久化

Ehcache 还提供了缓存持久化功能,它可以将缓存中的数据持久化到磁盘或者其他数据源。在应用程序重启或者缓存失效后,Ehcache 可以从持久化存储中恢复数据,从而保证数据的完整性和可靠性。如下:

  1. Ehcache 使用磁盘存储或数据库等持久化技术来存储缓存数据。
  2. 当缓存中的数据更新时,Ehcache 会自动将此数据持久化到持久化存储中。
  3. 在应用程序重启或者缓存失效后,Ehcache 会从持久化存储中读取缓存数据并重新加载到内存中

4、入门案例 ------ Ehcache 2.x 版本

EhCache缓存框架

4.1、单独使用 Ehcache

4.1.1、引入依赖

xml 复制代码
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.6</version>
</dependency>

4.1.2、配置 Ehcache

配置 Ehcache:用于定义缓存的行为和属性。

以下是一些主要用途:

  • 缓存定义: 可以定义一个或多个缓存,包括它们的名称、存储策略(如内存、磁盘)、最大条目数等
  • 过期策略: 你可以设置缓存条目的过期时间,如"时间到期"或"空闲时间"。这有助于控制缓存数据的生命周期
  • 溢出到磁盘: 如果缓存超出了内存限制,可以配置将数据溢出到磁盘,以避免数据丢失
  • 持久化选项: 配置是否需要将缓存数据持久化到文件系统,以便在应用重启后恢复缓存状态
  • 性能优化: 可以为缓存设置特定的性能参数,以优化应用的缓存行为

配置 Ehcache 有两种方式,分别为 XML、API

4.1.2.1、XML 配置方式
4.1.2.1.1、新建 ehcache.xml

ehcache.xml:是 EhCache 的配置文件。通过这个文件,可以指定多个缓存的设置以及全局的默认配置。

classpath 路径下新建 ehCache/ehcache.xml 文件,内容如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <!--
        磁盘的缓存位置
            磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
            path:指定在硬盘上存储对象的路径
                可以配置的目录有:
                    user.home(用户的家目录)
                    user.dir(用户当前的工作目录)
                    java.io.tmpdir(默认的临时目录)
                    ehcache.disk.store.dir(ehcache 的配置目录)
                    绝对路径(如:d:\\ehcache)
                查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir");
     -->
    <diskStore path="D:/ehCache/data"/>

    <!--默认缓存-->
    <!--
        defaultCache:默认缓存策略,当 ehcache 找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <!--userCache 缓存-->
    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大数目
        maxElementsOnDisk:硬盘最大缓存个数。
        eternal:对象是否永久有效,一但设置了,timeout 将不起作用。
        overflowToDisk:是否保存到磁盘,当系统宕机时
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当 eternal=false 对象不是永久有效时使用,
                          默认是 0.,也就是对象存活时间无穷大。
        diskPersistent:是否缓存虚拟机重启期数据,默认为 false
        diskSpoolBufferSizeMB:这个参数设置 DiskStore(磁盘缓存)的缓存区大小。默认是 30MB。每个 Cache 都应该有自己的一个缓冲区。
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120 秒。
        memoryStoreEvictionPolicy:当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为 FIFO(先进先出)或是 LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
        memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
        FIFO,first in first out,这个是大家最熟的,先进先出。
        LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个 hit 属性,hit 值最小的将
                                   会被清出缓存。
        LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳
                                  离当前时间最远的元素将被清出缓存。
    -->
    <cache name="userCache"
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="5"
           timeToLiveSeconds="5"
           overflowToDisk="false"
           memoryStoreEvictionPolicy="LRU"/>
</ehcache>

配置说明:

  • diskStore :用于配置缓存的磁盘存储路径。所有被溢出到磁盘的缓存数据将会存储在这个目录中
    • 数据持久化:当内存中的缓存条目超过限制时,EhCache 会将一些条目溢出到指定的磁盘路径,以减少内存使用并保持应用性能
    • 恢复能力:如果配置了持久化策略,可以在应用重启后从磁盘恢复缓存数据,确保数据不会丢失
  • defaultCache :默认缓存策略。当 Ecache 找不到定义的缓存时,则使用这个缓存策略。只能定义一个
    • maxEntriesLocalHeap="10000":指定在本地内存(Heap)中可以存储的最大条目数。这里设置为 10000,表示最多可以存放 10,000 个缓存条目
    • eternal="false":缓存条目是否永远有效。设置为 false,意味着条目会在指定的时间后失效
    • timeToIdleSeconds="120":指定条目在不被访问的情况下可以保持有效的时间,单位为秒。如果条目在 120 秒内未被访问,将会被移除
    • timeToLiveSeconds="120":指定条目从被创建开始到失效的总时间,单位为秒。如果条目在 120 秒后不论是否被访问都会被移除
    • maxEntriesLocalDisk="10000000":指定在本地磁盘中可以存储的最大条目数。设置为 10000000,表示最多可以在磁盘上存放 10,000,000 个条目
    • diskExpiryThreadIntervalSeconds="120":指定处理磁盘过期条目的线程运行的间隔,单位为秒。设置为 120,表示每 120 秒检查一次磁盘中的过期条目
    • memoryStoreEvictionPolicy="LRU">:指定内存存储的驱逐策略。在这里设置为 LRU(Least Recently Used),表示当内存达到最大容量时,会优先移除最近最少使用的条目
    • persistence strategy="localTempSwap":用于配置缓存的持久化策略。strategy="localTempSwap" 表示将缓存数据临时存储到本地磁盘,以防数据丢失,但不一定是永久存储
  • userCache
4.1.2.1.2、使用 Ehcache
java 复制代码
public class Test {

    public static void main(String[] args) {
        // 获取 EhCache 的缓存管理对象
        CacheManager cacheManager = new CacheManager(Test.class.getClassLoader().getResourceAsStream("ehcache/ehcache.xml"));
        Cache cache = cacheManager.getCache("userCache");
        // 创建缓存数据
        Element element = new Element("name","zzc");
        // ① 存入缓存
        cache.put(element);
        // ② 从缓存中取出
        Element element1 = cache.get("name");
        System.out.println(element1.getObjectValue());
        // ③ 删除缓存
        boolean flag = cache.remove("name");
        if (flag) {
            System.out.println("删除成功");
        } else {
            // ④ 再次获取缓存数据
            element1 = cache.get("name");
            System.out.println(element1.getObjectValue());
        }
        cacheManager.shutdown();
    }
}

可以使用 putgetremove 等方法进行缓存操作

4.1.2.2、API 配置方式
4.1.2.2.1、添加一个配置类
java 复制代码
public class EhCacheConfig {

    private CacheManager cacheManager;

    public EhCacheConfig() {
        // 创建全局配置
        Configuration configuration = new Configuration();
        // 配置缓存
        CacheConfiguration cacheConfig = new CacheConfiguration("userCache", 1000) // 缓存名称和最大条目数
                .memoryStoreEvictionPolicy("LRU") // 驱逐策略
                .timeToLiveSeconds(300) // 存活时间
                .timeToIdleSeconds(300); // 空闲时间

        configuration.addCache(cacheConfig);
        // 初始化 CacheManager
        cacheManager = CacheManager.newInstance(configuration);
    }

    public Cache getCache(String cacheName) {
        return cacheManager.getCache(cacheName);
    }

    public void shutdown() {
        if (cacheManager != null) {
            cacheManager.shutdown();
        }
    }
}
4.1.2.2.2、使用 Ehcache
java 复制代码
public class Test {

    public static void main(String[] args) {
        // 获取 EhCache 的缓存管理对象
        EhCacheConfig ehCacheConfig = new EhCacheConfig();
        Cache cache = ehCacheConfig.getCache("userCache");
        // 创建缓存数据
        Element element = new Element("name","zzc");
        // ① 存入缓存
        cache.put(element);
        // ② 从缓存中取出
        Element element1 = cache.get("name");
        System.out.println(element1.getObjectValue());
        // ③ 删除缓存
        boolean flag = cache.remove("name");
        if (flag) {
            System.out.println("删除成功");
        } else {
            // ④ 再次获取缓存数据
            element1 = cache.get("name");
            System.out.println(element1.getObjectValue());
        }
        ehCacheConfig.shutdown();
    }
}

5、入门案例 ------ Ehcache 3.x 版本

5.1、单独使用 Ehcache

5.1.1、引入依赖

xml 复制代码
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.10.0</version>
</dependency>

5.1.2、配置 Ehcache

5.1.2.1、XML 文件配置
5.1.2.1.1、新建 ehcache.xml 文件
xml 复制代码
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

    <cache alias="userCache">
        <key-type>java.lang.Integer</key-type>
        <value-type>java.lang.String</value-type>
        <expiry>
        	<!-- 设置过期时间为 5 分钟 -->
            <ttl unit="minutes">5</ttl> 
        </expiry>
        <resources>
            <heap unit="entries">100</heap> <!-- 最大条目数 -->
        </resources>
    </cache>
</config>

定义了一个名为 userCache 的缓存,配置了键值类型、过期时间、最大存储数量等参数

5.1.2.1.2、使用 Ehcache
java 复制代码
public class Test {

    public static void main(String[] args) {
        // 获取 EhCache 的缓存管理对象
        XmlConfiguration xmlConfig = new XmlConfiguration(Test.class.getResource("/ehcache/ehcache.xml"));
        CacheManager cacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
        cacheManager.init();
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        // ① 存入缓存
        userCache.put(1, "zzc");
        // ② 从缓存中取出
        String name = userCache.get(1);
        System.out.println(name);
        // ③ 删除缓存
        userCache.remove(1);
        System.out.println("删除成功");
        // ④ 再次获取缓存数据
        name = userCache.get(1);
        System.out.println(name);
        cacheManager.close();
    }
}
5.1.2.2、API 配置
5.1.2.2.1、新建一个配置类
java 复制代码
public class EhCacheConfig {

    private CacheManager cacheManager;

    public EhCacheConfig() {
        this.cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("userCache",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(
                                Integer.class,
                                String.class,
                                ResourcePoolsBuilder.newResourcePoolsBuilder()
                                // 最大条目数
                                .heap(100).build()).build())
                // 初始化 CacheManager
                .build(true);
    }

    public Cache<Integer, String> getCache(String cacheName) {
        return cacheManager.getCache(cacheName, Integer.class, String.class);
    }

    public void close() {
        if (cacheManager != null) {
            cacheManager.close();
        }
    }
}
5.1.2.2.2、使用 EhCache
java 复制代码
public class Test {

    public static void main(String[] args) {
        EhCacheConfig cacheConfig = new EhCacheConfig();
        Cache<Integer, String> userCache = cacheConfig.getCache("userCache");
        // ① 存入缓存
        userCache.put(1, "zzc");
        // ② 从缓存中取出
        String name = userCache.get(1);
        System.out.println(name);
        // ③ 删除缓存
        userCache.remove(1);
        System.out.println("删除成功");
        // ④ 再次获取缓存数据
        name = userCache.get(1);
        System.out.println(name);
        cacheConfig.close();
    }
}
5.1.2.2.3、EhCache 数据存储位置

EhCache3.x 版本中不但提供了堆内缓存 heap,还提供了堆外缓存 off-heap,并且还提供了数据的持久化操作,可以将数据落到磁盘中 disk

  • heap :使用堆内内存
    • heap(10):当前 Cache 最多只能存储 10 个数据,当你 put 第 11 个数据时,第一个数据就会被移除
    • heap(10,大小单位MB):当前 Cache 最多只能存储 10MB 数据
  • off-heap :堆外内存。将存储的数据放到操作系统的一块内存区域存储,不是JVM内部,这块空间属于RAM。这种对象是不能直接拿到JVM中使用的,在存储时,需要对数据进行序列化操作,同时获取出来的时候也要做反序列化操作
  • disk:磁盘。将数据落到本地磁盘,这样的话,当服务重启后,依然会从磁盘反序列化数据到内存中

EhCache 提供了三种组合方式:

  • heap + off-heap
  • heap + disk
  • heap + off-heap + disk


在组合情况下存储,存储数据时,数据先落到堆内内存,同时同步到对外内存以及本地磁盘。本地底盘因为空间充裕,所以本地磁盘数据是最全的。而且 EhCache 要求空间大小必须 disk > off-heap > heap

在组合情况下读取,因为性能原型,肯定是先找heap查询数据,没有数据去off-heap查询数据,off-heap没有数据再去disk中读取数据,同时读取数据之后,可以将数据一次同步到off-heap、heap

通过 API 实现组合存储方式:

java 复制代码
public EhCacheConfig() {
    String path = "D:/temp";
    this.cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            // 设置disk存储的位置
            .with(CacheManagerBuilder.persistence(path))
            .withCache("userCache",
                    CacheConfigurationBuilder.newCacheConfigurationBuilder(
                            Integer.class,
                            String.class,
                            ResourcePoolsBuilder.newResourcePoolsBuilder()
                            // 堆内存 最大条目数
                            .heap(100)
                            // 堆外内存
                            .offheap(10, MemoryUnit.MB)
                            // 磁盘  记得添加true,才能正常的持久化,并且序列化以及反序列化
                            .disk(100, MemoryUnit.MB, true)
                            .build()).build())
            // 初始化 CacheManager
            .build(true);
}

本地磁盘存储的方式,一共有三个文件

  • mata:元数据存储,记录这当前 cache 的 key 类型和 value 类型
  • data:存储具体数据的位置,将数据序列化成字节存储
  • index:类似索引,帮助查看数据的
5.1.2.2.4、数据生存时间

因为数据如果一致存放在内存当中,可能会出现内存泄漏等问题,数据在内存,一致不用,还占着空间

EhCache 对数据设置生存时间的机制提供了三种机制【三选一】:

  • noExpiration:不设置生存时间
  • timeToLiveExpiration:从数据落到缓存计算生存时间
  • timeToIdleExpiration:从最后一个 get 计算生存时间
java 复制代码
public EhCacheConfig() {
    String path = "D:/temp";
    this.cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            // 设置disk存储的位置
            .with(CacheManagerBuilder.persistence(path))
            .withCache("userCache",
                    CacheConfigurationBuilder.newCacheConfigurationBuilder(
                            Integer.class,
                            String.class,
                            ResourcePoolsBuilder.newResourcePoolsBuilder()
                            // 堆内存 最大条目数
                            .heap(100)
                            // 堆外内存
                            .offheap(10, MemoryUnit.MB)
                            // 磁盘  记得添加true,才能正常的持久化,并且序列化以及反序列化
                            .disk(100, MemoryUnit.MB, true)
                            .build())
                            // 【三选一】不设置生存时间
                            //.withExpiry(ExpiryPolicy.NO_EXPIRY)
                            // 设置生存时间,从存储开始计算
                            //.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMillis(1000)))
                            // 设置生存时间,每次获取数据后,重置生存时间
                            .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(1000)))
                            .build())
            // 初始化 CacheManager
            .build(true);
}

5.2、在 SpringBoot 中使用 Ehcache

5.2.1、引入依赖

xml 复制代码
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.3</version>
</dependency>

5.2.2、配置 Ehcache

5.2.2.1、XML 配置文件
5.2.2.1.1、新建一个配置文件
xml 复制代码
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

    <cache alias="userCache">
        <key-type>java.lang.Integer</key-type>
        <value-type>java.lang.String</value-type>
        <expiry>
            <!-- 设置过期时间为 5 分钟 -->
            <ttl unit="minutes">5</ttl>
        </expiry>
        <resources>
            <!-- 最大条目数 -->
            <heap unit="entries">100</heap>
        </resources>
    </cache>
</config>
5.2.2.1.2、启动类开启缓存注解

启动类上添加注解 @EnableCaching

5.2.2.1.3、添加配置类
java 复制代码
@Configuration
public class EhCacheConfig {

    @Bean(name = "ehCacheManager")
    public CacheManager cacheManager() {
        XmlConfiguration xmlConfig = new XmlConfiguration(EhCacheConfig.class.getResource("/ehcache/ehcache.xml"));
        CacheManager cacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
        // 确保初始化
        cacheManager.init();
        return cacheManager;
    }
}
5.2.2.1.4、使用 Ehcache
java 复制代码
@RestController
public class TeController {

    @Autowired
    private CacheManager cacheManager;

    @GetMapping("/test")
    public String test(Integer key) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        userCache.put(key, "Value for " + key);
        String value = userCache.get(key);
        System.out.println(value);
        userCache.remove(key);
        System.out.println("删除成功");
        value = userCache.get(key);
        System.out.println(value);
        return "Value for " + key;
    }
}
5.2.2.2、API 配置
5.2.2.2.1、新建一个配置类
java 复制代码
@Configuration
public class EhCacheConfig {

    @Bean(name = "ehCacheManager")
    public CacheManager cacheManager() {
        String path = "D:/temp";
        return CacheManagerBuilder.newCacheManagerBuilder()
                // 设置disk存储的位置
                .with(CacheManagerBuilder.persistence(path))
                .withCache("userCache",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(
                                        Integer.class,
                                        String.class,
                                        ResourcePoolsBuilder.newResourcePoolsBuilder()
                                                // 堆内存 最大条目数
                                                .heap(100)
                                                // 堆外内存
                                                .offheap(10, MemoryUnit.MB)
                                                // 磁盘  记得添加true,才能正常的持久化,并且序列化以及反序列化
                                                .disk(100, MemoryUnit.MB, true)
                                                .build())
                                // 设置生存时间,每次获取数据后,重置生存时间
                                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(1000)))
                                .build())
                // 初始化 CacheManager
                .build(true);
    }

}
5.2.2.2.2、使用 Ehcache
java 复制代码
@RestController
public class TeController {

    @Autowired
    private CacheManager cacheManager;

    @GetMapping("/test")
    public String test(Integer key) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        userCache.put(key, "Value for " + key);
        String value = userCache.get(key);
        System.out.println(value);
        userCache.remove(key);
        System.out.println("删除成功");
        value = userCache.get(key);
        System.out.println(value);
        return "Value for " + key;
    }
}

5.3、集成 Spring Cache

5.3.1、引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.3</version>
</dependency>

5.3.2、启动类开启缓存注解

启动类上添加注解 @EnableCaching

5.3.3、配置 Ehcache

这里以 API 的方式进行配置,并将配置信息放入 yml 配置文件中

5.3.3.1、添加配置信息

yml 配置文件中添加如下信息:

yml 复制代码
# 准备EhCache基础配置项
ehcache:
  heap: 1000           # 堆内内存缓存个数
  off-heap: 10         # 对外内存存储大小 MB
  disk: 20             # 磁盘存储数据大小  MB
  diskDir: D:/data/    # 磁盘存储路径
  cacheNames:          # 基于CacheManager构建多少个缓存
    - userCache
    - itemCache
5.3.3.2、配置属性类
java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "ehcache")
public class EhCacheProperty {
    private int heap;
    private int offheap;
    private int disk;
    private String diskDir;
    private Set<String> cacheNames;
}
5.3.3.3、配置 CachaManager
java 复制代码
@Configuration
public class EhCacheConfig {

    @Autowired
    private EhCacheProperty ehCacheProperty;

    @Bean(name = "ehCacheManager")
    public CacheManager cacheManager() {
        // ①:设置内存存储位置和数量大小
        ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder()
                // 堆内存
                .heap(ehCacheProperty.getHeap())
                // 堆外内存
                .offheap(ehCacheProperty.getOffheap(), MemoryUnit.MB)
                // 磁盘
                .disk(ehCacheProperty.getDisk(),MemoryUnit.MB, true)
                .build();
        // ②:设置生存时间
        ExpiryPolicy userExpiry = ExpiryPolicyBuilder.noExpiration();
        ExpiryPolicy itemExpiry = ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(1000));
        // ③:设置 CacheConfiguration
        CacheConfiguration userCache = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Integer.class, String.class, resourcePools)
                .withExpiry(userExpiry)
                .build();
        CacheConfiguration itemCache = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Integer.class, String.class, resourcePools)
                .withExpiry(itemExpiry)
                .build();
        // ④:设置磁盘存储的位置
        CacheManagerBuilder<PersistentCacheManager> cacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(ehCacheProperty.getDiskDir()));
        // ⑤:设置缓存名称
        Set<String> cacheNames = ehCacheProperty.getCacheNames();
        Map<String, CacheConfiguration> cacheMap = new HashMap<>(2);
        cacheMap.put("userCache", userCache);
        cacheMap.put("itemCache", itemCache);
        for (String cacheName : cacheNames) {
            cacheManagerBuilder = cacheManagerBuilder.withCache(cacheName, cacheMap.get(cacheName));
        }
        // 初始化 CacheManager
        return cacheManagerBuilder.build(true);
    }

}

5.3.4、使用 Ehcache

Spring Boot 提供了 @Cacheable@CachePut@CacheEvict 等注解来简化缓存操作,同时也支持基于缓存管理器的编程方式

5.3.4.1、基于注解的缓存
5.3.4.1.1、新建一个 UserServiceImpl
java 复制代码
@Slf4j
@Service
@CacheConfig(cacheNames = "userCache")
public class UserServiceImpl {

    // 模拟数据库数据
    private Map<Integer, User> userMap = new HashMap<>();

    @CachePut(key = "#user.id")
    public User add(User user) {
        log.info("add");
        userMap.put(user.getId(), user);
        return user;
    }

    @Cacheable(key = "#id", unless = "#result == null")
    public User get(Integer id) {
        log.info("get");
        return userMap.get(id);
    }

    @CachePut(key = "#user.id")
    public User update(User user) {
        log.info("update");
        userMap.put(user.getId(), user);
        return user;
    }

    @CacheEvict(key = "#id")
    public void delete(Integer id) {
        log.info("delete");
        userMap.remove(id);
    }

}
5.3.4.1.2、新建一个 TestController
java 复制代码
@RestController
public class TeController {
    @Autowired
    private UserServiceImpl userServiceImpl;

    @PostMapping
    public String add(@RequestBody User user) {
        userServiceImpl.add(user);
        return "add";
    }

    @GetMapping("/{id}")
    public User get(@PathVariable Integer id) {
        User user = userServiceImpl.get(id);
        return user;
    }

    @PutMapping
    public String update(@RequestBody User user) {
        userServiceImpl.update(user);
        return "update";
    }

    @DeleteMapping("/{id}")
    public String delete(@PathVariable Integer id) {
        userServiceImpl.delete(id);
        return "delete";
    }

}
5.3.4.12、基于缓存管理器的编程方式

Spring Boot 中,可以使用 CacheManagerCache 接口来实现缓存的管理和操作

java 复制代码
@RestController
public class TeController {

    @Autowired
    private CacheManager cacheManager;

    @PostMapping
    public String add(@RequestBody User user) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        if (Objects.nonNull(userCache)) {
            userCache.put(user.getId(), JSON.toJSONString(user));
        }
        return "add";
    }

    @GetMapping("/{id}")
    public User get(@PathVariable Integer id) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        if (Objects.nonNull(userCache)) {
            String s = userCache.get(id);
            return JSON.parseObject(s, User.class);
        }
        return null;
    }

    @PutMapping
    public String update(@RequestBody User user) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        if (Objects.nonNull(userCache)) {
            userCache.put(user.getId(), JSON.toJSONString(user));
        }
        return "update";
    }

    @DeleteMapping("/{id}")
    public String delete(@PathVariable Integer id) {
        Cache<Integer, String> userCache = cacheManager.getCache("userCache", Integer.class, String.class);
        if (Objects.nonNull(userCache)) {
            userCache.remove(id);
        }
        return "delete";
    }
}
相关推荐
聪明的笨猪猪12 小时前
Java Redis “Sentinel(哨兵)与集群”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
222you12 小时前
Mybatis(1)
java·tomcat·mybatis
渣哥12 小时前
三级缓存揭秘:Spring 如何优雅地处理循环依赖问题
javascript·后端·面试
靠近彗星12 小时前
1.5操作系统引导
java·linux·服务器·操作系统
xuejianxinokok12 小时前
Postgres 18 的新功能
后端·postgresql
渣哥12 小时前
为什么几乎所有 Java 项目都离不开 IoC?Spring 控制反转的优势惊人!
javascript·后端·面试
用户38568033499612 小时前
appium从入门到精通php,移动端自动化测试Appium 从入门到项目实战Python版
后端
瑶山13 小时前
社区版Idea怎么创建Spring Boot项目?Selected Java version 17 is not supported. 问题解决
java·spring boot·intellij-idea·创建项目
学习编程的Kitty13 小时前
JavaEE初阶——多线程(1)初识线程与创建线程
java·开发语言·java-ee
陈小桔13 小时前
SpringBoot之配置文件
spring boot