- Ehcache 简介:为什么选择它?
- 快速上手:一个最简单的内存缓存示例。
- 核心概念:
CacheManager,Cache,Element。 - 配置详解:通过
ehcache.xml进行高级配置。 - Spring Boot 集成:在现代应用中最常见的使用方式。
- 高级特性:磁盘溢出、缓存集群、事件监听等。
- 最佳实践与注意事项。
Ehcache 简介
Ehcache 是一个纯 Java 的进程内缓存框架,它具有以下特点:

- 成熟稳定:发展多年,被大量大型项目验证过,可靠性高。
- 功能丰富:
- 支持内存缓存(堆内存)。
- 支持将缓存数据溢出到磁盘,避免 OOM(内存溢出)。
- 支持分布式缓存,可通过 RMI, JMS, Terracotta 等方式实现集群缓存。
- 支持缓存事件监听(如创建、更新、移除)。
- 支持多级缓存(如堆外内存 + 磁盘)。
- 高性能:经过高度优化,读写性能优异。
- 易用性:API 设计简单直观,并且与 Spring、Hibernate 等主流框架集成良好。
- 版本演进:
- Ehcache 2.x:非常经典和广泛使用的版本,功能稳定,配置方式是
ehcache.xml。 - Ehcache 3.x:基于 JSR-107 (JCache) 标准,重新设计,API 和配置方式(如
config.yaml)都有较大变化,新项目推荐使用 3.x,但维护旧项目时仍会遇到 2.x。
- Ehcache 2.x:非常经典和广泛使用的版本,功能稳定,配置方式是
本教程将主要介绍 Ehcache 3.x 的用法,因为它代表了未来的方向,并会简要提及 2.x 的区别。
快速上手 (Ehcache 3.x)
1 添加依赖
在你的 pom.xml (Maven) 或 build.gradle (Gradle) 中添加 Ehcache 3 的核心依赖。
Maven:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version> <!-- 建议使用最新稳定版 -->
</dependency>
2 编写代码
Ehcache 3 的使用非常简单,核心是 CacheManager 和 Cache。

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.newCacheManagerBuilder().build() 来构建一个 CacheManager
// .build() 是懒加载的,只有在第一次使用时才会初始化
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build();
// 2. 初始化 CacheManager (使其可用)
cacheManager.init();
// 3. 创建一个名为 "userCache" 的 Cache
// CacheConfigurationBuilder 定义了缓存的基本配置
// ResourcePoolsBuilder 定义了缓存可以使用的资源(这里只使用堆内存,大小为 10)
// ExpiryPolicy 定义了过期策略
Cache<Long, String> userCache = cacheManager.createCache("userCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Long.class, // Key 类型
String.class, // Value 类型
ResourcePoolsBuilder.heap(10) // 堆内存,最多存 10 个元素
).withExpiry(ExpiryPolicy.timeToLiveExpiration(java.time.Duration.ofSeconds(10))) // 设置 TTL 为 10 秒
);
// 4. 使用缓存
System.out.println("向缓存中添加数据...");
userCache.put(1L, "Alice");
userCache.put(2L, "Bob");
System.out.println("从缓存中获取数据...");
String user1 = userCache.get(1L);
System.out.println("ID 为 1 的用户是: " + user1); // 输出: Alice
System.out.println("更新缓存中的数据...");
userCache.put(1L, "Alice Smith");
String user1Updated = userCache.get(1L);
System.out.println("更新后,ID 为 1 的用户是: " + user1Updated); // 输出: Alice Smith
// 5. 等待过期
System.out.println("等待 12 秒,让缓存过期...");
Thread.sleep(12000);
String user1AfterExpiry = userCache.get(1L);
System.out.println("12 秒后,ID 为 1 的用户是: " + user1AfterExpiry); // 输出: null,因为已经过期
// 6. 关闭 CacheManager
// 这会释放所有缓存资源
cacheManager.close();
}
}
运行这段代码,你就能直观地看到 Ehcache 的基本用法:创建缓存、存取数据、更新数据和数据过期。
核心概念
-
CacheManager(缓存管理器):- Ehcache 的核心入口,负责管理一个或多个
Cache实例。 - 它是单例的,一个应用中通常只需要一个
CacheManager。 - 负责缓存的创建、获取和生命周期管理(初始化、关闭)。
- Ehcache 的核心入口,负责管理一个或多个
-
Cache(缓存):- 一个命名的、类型安全的(
<K, V>)数据存储区域。 - 你可以有多个
Cache,每个都有唯一的名称和独立的配置。 - 它是真正存放
Element的地方。
- 一个命名的、类型安全的(
-
Element(缓存元素):
(图片来源网络,侵删)- Ehcache 2.x 中的核心概念,代表缓存中的一条记录,包含 key、value、创建时间、访问时间等元数据。
- 在 Ehcache 3.x 中,这个概念被弱化了,你直接操作的是
Cache<K, V>接口,put(key, value)和get(key)的操作直接在键值对上进行,底层会自动处理Element的封装,这使得 API 更符合 Java 集合的常规用法。
配置详解 (ehcache.xml vs. API)
虽然 Ehcache 3.x 推荐使用 Java API 或 YAML/JSON 进行配置,但为了兼容性和灵活性,我们仍然可以使用 XML 配置文件。
1 创建 ehcache.xml
在 src/main/resources 目录下创建 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">
<!-- 定义一个名为 "userCache" 的缓存 -->
<cache alias="userCache">
<!-- key 和 value 的序列化方式 -->
<key-type>java.lang.Long</key-type>
<value-type>java.lang.String</value-type>
<!-- 定义资源池 -->
<resources>
<!-- 堆内内存,最多 200 个元素 -->
<heap unit="entries">200</heap>
<!-- 磁盘持久化,当堆内存满了,会溢出到磁盘上,最多 100MB -->
<offheap unit="MB">100</offheap>
<!-- 磁盘存储,路径为系统临时目录下的 "myData" 目录 -->
<disk unit="GB">1</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>
2 在代码中加载 XML 配置
// 从 classpath 加载 ehcache.xml 文件
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.configurationFromFile("ehcache.xml"))
.build();
cacheManager.init();
// 现在可以直接获取在 XML 中定义的缓存了
Cache<Long, String> userCache = cacheManager.getCache("userCache", Long.class, String.class);
Cache<String, Integer> simpleCache = cacheManager.getCache("simpleCache", String.class, Integer.class);
API vs. XML 配置对比:
- API 配置:代码化,类型安全,易于动态调整,适合单元测试。
- XML/YAML 配置:将配置与代码分离,运维人员可以修改配置而无需重新编译代码,适合生产环境。
在实际开发中,推荐使用 API,因为它更灵活且易于维护,Spring Boot 集成就是最好的例子。
Spring Boot 集成 (强烈推荐)
在 Spring Boot 应用中使用 Ehcache 非常简单,官方提供了 spring-boot-starter-cache 和 spring-boot-starter-cache。
1 添加依赖
<!-- Spring Boot Cache 抽象层 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache 3 实现 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2 启用缓存
在你的主启动类上添加 @EnableCaching 注解。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // 启用 Spring 缓存功能
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
3 配置 Ehcache
在 src/main/resources/application.yml 或 application.properties 中配置 Ehcache。
使用 application.yml (推荐):
spring:
cache:
type: ehcache
ehcache:
config: classpath:ehcache-spring.xml # 指定 Ehcache 的配置文件路径
或者使用 application.properties:
spring.cache.type=ehcache spring.cache.ehcache.config=classpath:ehcache-spring.xml
4 创建 Ehcache 配置文件
在 src/main/resources 下创建 ehcache-spring.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">
<!-- 默认缓存配置,所有未显式声明的缓存都将使用此配置 -->
<cache-template name="defaultCache">
<expiry>
<ttl unit="minutes">30</ttl>
</expiry>
<heap unit="entries">1000</heap>
</cache-template>
<!-- 为特定缓存使用默认模板 -->
<cache alias="users" uses-template="defaultCache"/>
<cache alias="products" uses-template="defaultCache">
<heap unit="entries">5000</heap>
</cache>
</config>
5 在 Service 中使用缓存注解
现在你可以在你的 Service 方法上使用 Spring 的缓存注解了。
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;
@Service
@CacheConfig(cacheNames = "users") // 类级别缓存配置,指定默认的缓存名
public class UserService {
// @Cacheable: 在方法执行前,会根据 key 查找缓存。
// 如果找到,则直接返回缓存结果,不执行方法体。
// 如果没找到,则执行方法体,并将返回结果存入缓存。
@Cacheable(key = "#id")
public User getUserById(Long id) {
System.out.println("正在从数据库查询用户 ID: " + id);
// 模拟数据库查询
return new User(id, "User-" + id);
}
// @CachePut: 每次都会执行方法体,并将返回结果更新/放入缓存。
// 主要用于更新操作。
@CachePut(key = "#user.id")
public User updateUser(User user) {
System.out.println("正在更新用户: " + user);
// 模拟数据库更新
return user;
}
// @CacheEvict: 执行方法体,并根据 key 从缓存中移除数据。
// 主要用于删除操作。
@CacheEvict(key = "#id")
public void deleteUser(Long id) {
System.out.println("正在删除用户 ID: " + id);
// 模拟数据库删除
}
// @CacheEvict 的 allEntries 属性,表示清空整个缓存
@CacheEvict(allEntries = true)
public void clearAllUsers() {
System.out.println("正在清空所有用户缓存");
}
}
注解说明:
@Cacheable: 查询缓存,有则返回,无则执行并存入。@CachePut: 更新缓存,每次都执行并存入。@CacheEvict: 删除缓存,执行方法后删除指定 key 或全部缓存。@CacheConfig: 类级别注解,可以统一指定cacheNames、keyGenerator等,避免在每个方法上重复。
高级特性
- 磁盘溢出:通过在
resources配置中添加disk或offheap,当堆内存不足时,Ehcache 会自动将不常用的数据移出堆内存,防止 OOM。offheap(堆外内存) 比disk(磁盘) 速度快得多。 - 事件监听:可以为缓存添加事件监听器,在元素被创建、更新、移除或过期时执行自定义逻辑。
// API 方式 cache.getRuntimeConfiguration().registerCacheEventListener(new MyCacheEventListener(), EventType.CREATED, EventType.UPDATED);
- 缓存集群/分布式:Ehcache 提供了企业级解决方案 Ehcache Clustered (基于 Terracotta),可以将缓存数据在多个节点之间同步,实现高可用和负载均衡,这是一个商业产品,功能强大。
- JSR-107 (JCache) 支持:Ehcache 3.x 是 JSR-107 的参考实现,这意味着你不仅可以用 Ehcache 的原生 API,还可以使用标准的 JCache API (
javax.cache),这提高了代码的可移植性。
最佳实践与注意事项
- Key 的设计:Key 应该是简单、不可变且高效的,推荐使用基本类型、String 或简单的 POJO,避免使用复杂的对象作为 Key,并确保其
hashCode()和equals()方法实现正确。 - Value 的大小:缓存的是“计算结果”或“频繁访问的数据”,而不是“大对象”,过大的 Value 会消耗大量内存,并降低缓存效率。
- 合理设置过期时间:为不同类型的数据设置合适的 TTL,热点数据可以设置长一点,会话信息可以设置短一点。
- 监控:对缓存进行监控至关重要,监控命中率、内存使用情况、元素数量等指标,可以帮助你判断缓存是否有效,并及时调整配置。
- 序列化:Ehcache 3.x 默认使用 Java 序列化,对于性能要求高的场景,可以考虑使用更高效的序列化方式,如 Kryo、Protostuff 或 Jackson。
CacheManager的生命周期:确保在应用关闭时(如 Spring 的@PreDestroy方法中)调用cacheManager.close(),以释放资源。- 选择合适的版本:
- 新项目:直接使用 Ehcache 3.x,并优先考虑与 Spring Boot 集成。
- 维护旧项目:如果项目使用的是 Ehcache 2.x,并且没有升级的迫切需求,可以继续使用它,因为它非常稳定,升级到 3.x 需要修改 API 和配置方式。
希望这份详细的指南能帮助你全面掌握 Java 中使用 Ehcache 的方法!
