杰瑞科技汇

Java如何使用EhCache?

目录

  1. Ehcache 简介
  2. 准备工作:添加依赖
  3. 核心概念
  4. 快速上手:基础用法
  5. 高级特性
    • 缓存配置文件 (ehcache.xml)
    • 缓存过期策略
    • 缓存事件监听
  6. Ehcache 3 vs Ehcache 2.x
  7. 完整示例
  8. 最佳实践

Ehcache 简介

Ehcache 的主要特点:

Java如何使用EhCache?-图1
(图片来源网络,侵删)
  • 高性能:采用多种优化策略,读写性能优异。
  • 标准兼容:完全遵循 JSR-107 (JCache) 标准,具有良好的可移植性。
  • 功能丰富:支持堆内缓存、堆外缓存、磁盘持久化、集群同步、分布式缓存等。
  • 灵活配置:可以通过 XML、代码或注解进行配置。
  • 多版本:Ehcache 3 是基于 JSR-107 重新设计的现代化版本,而 Ehcache 2.x 是一个更传统的、功能强大的版本,两者 API 和配置方式不兼容。

准备工作:添加依赖

根据你使用的构建工具(Maven 或 Gradle),在 pom.xmlbuild.gradle 文件中添加 Ehcache 3 的核心依赖。

Maven (pom.xml)

<dependencies>
    <!-- Ehcache 3 核心库 -->
    <dependency>
        <groupId>org.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>3.10.8</version> <!-- 建议使用最新稳定版 -->
    </dependency>
    <!-- 为了支持 JSR-107 API (CachingProvider, CacheManager, Cache 等) -->
    <dependency>
        <groupId>javax.cache</groupId>
        <artifactId>cache-api</artifactId>
        <version>1.1.1</version>
    </dependency>
</dependencies>

Gradle (build.gradle)

dependencies {
    // Ehcache 3 核心库
    implementation 'org.ehcache:ehcache:3.10.8' // 建议使用最新稳定版
    // 为了支持 JSR-107 API
    implementation 'javax.cache:cache-api:1.1.1'
}

核心概念

在使用 Ehcache 之前,了解以下几个核心 JSR-107 接口非常重要:

  1. CachingProvider:缓存提供者,负责创建和管理 CacheManager,一个 JVM 中可以有多个 CachingProvider
  2. CacheManager:缓存管理器,负责创建、配置、管理和销毁 Cache 实例,一个 CacheManager 可以管理多个 Cache
  3. Cache<K, V>:缓存,一个命名的、强类型的、线程安全的对象存储区域,它是你与缓存进行交互的主要接口。
  4. Entry<K, V>:缓存条目,存储在缓存中的键值对。

标准的获取缓存对象的流程是: CachingProvider -> CacheManager -> Cache


快速上手:基础用法

最简单的方式是使用 CacheManagerBuilder 以编程方式创建缓存。

Java如何使用EhCache?-图2
(图片来源网络,侵删)

示例代码

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.expiry.ExpiryPolicy;
public class EhcacheQuickStart {
    public static void main(String[] args) throws InterruptedException {
        // 1. 创建 CacheManager
        // 使用 CacheManagerBuilder 创建一个临时的、基于堆的 CacheManager
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
        cacheManager.init(); // 初始化 CacheManager
        // 2. 定义并创建 Cache
        // 缓存名为 "userCache"
        // 键类型为 Long,值类型为 String
        // 设置缓存池为堆内存,最大存放 200 个元素
        Cache<Long, String> userCache = cacheManager.createCache("userCache",
                CacheConfigurationBuilder.newCacheConfigurationBuilder(
                        Long.class, // 键类型
                        String.class, // 值类型
                        ResourcePoolsBuilder.heap(200) // 缓存池配置:堆内存,200个元素
                ));
        // 3. 使用 Cache
        // 放入数据
        userCache.put(1L, "Alice");
        userCache.put(2L, "Bob");
        // 获取数据
        String user1 = userCache.get(1L);
        System.out.println("User with id 1: " + user1); // 输出: User with id 1: Alice
        // 检查是否存在
        boolean exists = userCache.containsKey(2L);
        System.out.println("User with id 2 exists: " + exists); // 输出: User with id 2 exists: true
        // 删除数据
        userCache.remove(1L);
        System.out.println("User with id 1 after removal: " + userCache.get(1L)); // 输出: User with id 1 after removal: null
        // 4. 关闭 CacheManager
        // 关闭后会释放所有资源
        cacheManager.close();
    }
}

高级特性

1 缓存配置文件 (ehcache.xml)

对于复杂的缓存配置,推荐使用 XML 文件,而不是硬编码在 Java 代码中。

  1. src/main/resources 目录下创建 ehcache.xml 文件。

  2. ehcache.xml 示例

    <?xml version="1.0" encoding="UTF-8"?>
    <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/v3/ehcache.xsd">
        <!-- 定义服务,用于指定堆外和磁盘存储的位置 -->
        <service>
            <persistence directory="java.io.tmpdir/ehcache-persistence"/>
        </service>
        <!-- 定义一个缓存 -->
        <cache alias="userCache">
            <!-- 键、值序列化方式 -->
            <key-type>java.lang.Long</key-type>
            <value-type>java.lang.String</value-type>
            <!-- 缓存池配置 -->
            <!-- heap: 堆内存 -->
            <!-- offheap: 堆外内存 -->
            <!-- disk: 磁盘持久化 -->
            <resources>
                <heap unit="entries">200</heap>
                <!-- 当堆内存满时,可以将 100MB 的数据存入堆外内存 -->
                <offheap unit="MB">100</offheap>
                <!-- 当堆外内存也满时,可以将 500MB 的数据持久化到磁盘 -->
                <disk unit="MB">500</disk>
            </resources>
            <!-- 过期策略 -->
            <expiry>
                <!-- 创建后 10 分钟过期 -->
                <ttl unit="minutes">10</ttl>
            </expiry>
        </cache>
        <!-- 再定义一个简单的缓存 -->
        <cache alias="simpleCache">
            <key-type>java.lang.String</key-type>
            <value-type>java.lang.Integer</value-type>
            <resources>
                <heap unit="entries">500</heap>
            </resources>
        </cache>
    </config>
  3. 在 Java 代码中使用配置文件

    Java如何使用EhCache?-图3
    (图片来源网络,侵删)
    // 使用配置文件创建 CacheManager
    CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            .with(CacheManagerBuilder.persistence("path/to/your/cache/store")) // 指定持久化目录
            .build(true); // build(true) 会自动初始化
    // 获取已配置的缓存
    Cache<Long, String> userCache = cacheManager.getCache("userCache", Long.class, String.class);
    Cache<String, Integer> simpleCache = cacheManager.getCache("simpleCache", String.class, Integer.class);
    // ... 使用缓存 ...
    cacheManager.close();

2 缓存过期策略

Ehcache 支持多种过期策略,可以在配置文件或代码中指定:

  • timeToIdle (TTI):一个条目在被访问后,多长时间内没有被再次访问,则过期。
  • timeToLive (TTL):一个条目从创建开始,多长时间后过期,无论是否被访问。
  • no expiry:永不过期。

可以在 ehcache.xml<expiry> 标签中配置:

<expiry>
    <!-- TTL: 创建后60秒过期 -->
    <ttl unit="seconds">60</ttl>
    <!-- 或者 TTI: 被访问后60秒未再次访问则过期 -->
    <!-- <tti unit="seconds">60</tti> -->
</expiry>

3 缓存事件监听

你可以为缓存添加监听器,在条目被创建、更新、删除或过期时执行自定义逻辑。

  1. 创建监听器类

    import javax.cache.event.CacheEntryEvent;
    import javax.cache.event.CacheEntryListenerException;
    import javax.cache.event.CacheEntryUpdatedListener;
    import java.util.EventListener;
    public class MyCacheListener implements CacheEntryUpdatedListener<Long, String> {
        @Override
        public void onUpdated(Iterable<CacheEntryEvent<? extends Long, ? extends String>> events) throws CacheEntryListenerException {
            for (CacheEntryEvent<? extends Long, ? extends String> event : events) {
                System.out.println("Cache Event: Key " + event.getKey() + " was updated from '" + event.getOldValue() + "' to '" + event.getValue() + "'");
            }
        }
    }
  2. 注册监听器

    你可以在创建缓存配置时添加监听器。

    CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
    cacheManager.init();
    Cache<Long, String> userCache = cacheManager.createCache("userCache",
            CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.heap(200))
                    .withEventFiltering() // 启用事件过滤
                    .withEventListeners(new MyCacheListener()) // 添加监听器
    );
    userCache.put(1L, "Alice");
    userCache.put(1L, "Alice Smith"); // 这会触发更新事件
    cacheManager.close();

Ehcache 3 vs Ehcache 2.x

特性 Ehcache 3 Ehcache 2.x
标准 完全遵循 JSR-107 (JCache) 标准 不完全遵循 JSR-107,有自己的 API
API 使用 JSR-107 接口 (Cache, CacheManager) 和 CacheManagerBuilder 使用 CacheManager, Cache 等自己定义的类
配置 优先使用 ehcache.xml,支持 JSR-107 的 @CacheResult 等注解 优先使用 ehcache.xml,有自己的 @Cacheable 等注解 (Spring Cache 集成时)
存储层 更灵活,支持堆、堆外、磁盘的多层存储 支持堆、磁盘、集群
集群 通过 Terracotta 客户端实现 通过 Terracotta 服务器实现,非常成熟
推荐 新项目推荐使用,现代化,标准,易于集成 旧项目或需要 Terracotta 集群特性的项目

简单来说:如果你现在开始一个新项目,请直接选择 Ehcache 3,它更符合行业标准,API 更现代化,如果你维护的是一个老项目,并且正在使用 Ehcache 2.x,那么可以继续使用,但升级到 3.x 需要做较多工作。


完整示例 (结合配置文件和事件监听)

这个例子演示了如何加载 ehcache.xml,创建一个带过期策略和监听器的缓存,并进行操作。

  1. src/main/resources/ehcache.xml (同上)
  2. src/main/java/com/example/MyCacheListener.java (同上)
  3. MainApp.java
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.xml.XmlConfiguration;
import javax.cache.configuration.Factory;
import java.net.URL;
public class MainApp {
    public static void main(String[] args) throws InterruptedException {
        // 1. 从类路径加载 ehcache.xml 配置文件
        URL myUrl = getClass().getResource("/ehcache.xml");
        XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
        // 2. 使用配置文件创建 CacheManager
        CacheManager cacheManager = CacheManagerBuilder.newCacheManager(xmlConfig).build(true);
        // 3. 获取已配置的缓存
        Cache<Long, String> userCache = cacheManager.getCache("userCache", Long.class, String.class);
        // 4. 使用缓存
        System.out.println("Putting user 101...");
        userCache.put(101L, "Charlie");
        System.out.println("Got user 101: " + userCache.get(101L)); // 应该输出 Charlie
        System.out.println("Updating user 101...");
        userCache.put(101L, "Charles"); // 这会触发 MyCacheListener 的 onUpdated 方法
        // 5. 测试过期策略 (TTL为10分钟,这里我们等待一小会儿来观察)
        // 注意:TTL 的检查不是实时的,通常在访问或写入时才会触发
        System.out.println("Waiting for 1 second to test TTL (though TTL is 10 mins, this just shows the concept)...");
        Thread.sleep(1000);
        System.out.println("Got user 101 after 1 sec: " + userCache.get(101L)); // 仍然存在
        // 6. 关闭 CacheManager
        cacheManager.close();
    }
}

最佳实践

  1. 选择合适的版本:新项目使用 Ehcache 3。
  2. 使用配置文件:将缓存配置(如大小、过期策略、存储层)与业务代码分离,使用 ehcache.xml
  3. 明确键和值类型:在创建缓存时,始终明确指定键和值的类型,以保证类型安全。
  4. 设置合理的缓存大小:根据应用内存和业务数据量,为 heap 缓存设置一个合理的上限,避免 OutOfMemoryError
  5. 配置合适的过期策略:为缓存数据设置 TTL 或 TTI,防止缓存中堆积过期的“僵尸数据”。
  6. 始终关闭 CacheManager:在应用生命周期结束时(如 Web 应用的 ServletContextListenercontextDestroyed 方法中),调用 cacheManager.close(),以释放所有资源,包括线程和文件句柄。
  7. 考虑集成 Spring Cache:如果你在使用 Spring Boot,可以非常方便地集成 Ehcache 作为 CacheManager 实现,只需添加依赖和配置即可,无需手动操作 CacheManagerCache,这是目前最主流的使用方式。
分享:
扫描分享到社交APP
上一篇
下一篇