EhCache 集成
约 1013 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › Spring Boot › 缓存 › EhCache 集成
EhCache 是老牌 Java 本地缓存方案,支持堆内/堆外内存、磁盘持久化和分布式缓存,适合无外部缓存中间件的轻量场景。
起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<classifier>jakarta</classifier> <!-- Boot 3 需 jakarta 分类器 -->
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId> <!-- JCache (JSR-107) 标准 API -->
</dependency>配置文件(ehcache.xml)
src/main/resources/ehcache.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="users">
<key-type>java.lang.Long</key-type>
<value-type>com.example.demo.model.User</value-type>
<resources>
<heap unit="entries">1000</heap> <!-- 最多 1000 个条目 -->
<offheap unit="MB">50</offheap> <!-- 堆外内存 50MB -->
</resources>
<expiry>
<ttl unit="minutes">10</ttl> <!-- 存活时间 10 分钟 -->
</expiry>
</cache>
<!-- 磁盘持久化缓存 -->
<cache alias="bigData">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
<resources>
<heap unit="entries">100</heap>
<offheap unit="MB">10</offheap>
<disk unit="MB">500</disk> <!-- 磁盘 500MB -->
</resources>
<persistence>
<directory>/tmp/ehcache-data</directory> <!-- 持久化目录 -->
</persistence>
</cache>
</config>Spring Boot 配置
spring:
cache:
type: ehcache # 指定缓存类型
ehcache:
config: classpath:ehcache.xml # 配置文件路径使用方式与 Spring Cache 抽象一致:
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
return userRepository.findById(id).orElseThrow();
}EhCache vs Caffeine
| 对比维度 | Caffeine | EhCache |
|---|---|---|
| 设计定位 | 高性能本地缓存(内存) | 多级缓存(堆 + 堆外 + 磁盘) |
| 性能 | 极高(无锁 + W-TinyLFU 算法) | 较高 |
| 磁盘持久化 | 不支持 | 支持(可用于进程重启后恢复) |
| 分布式 | 不支持 | 支持(Terracotta 集群,但已罕见) |
| 配置方式 | Java DSL 或 caffeine.spec | XML 配置 |
| JCache 标准 | 兼容 | 原生兼容 |
| Spring Boot 自动配置 | CaffeineCacheManager | EhCacheCacheManager |
选型建议:纯内存、追求极致性能 → Caffeine;需要磁盘持久化或堆外内存 → EhCache。
多级缓存架构(Caffeine + Redis)
┌─────────────────────────────────────────────────────┐
│ 应用层 │
│ │
│ @Cacheable("userCache") │
│ │ │
│ ▼ │
│ CacheInterceptor │
│ │ │
│ ┌─────┴──────┐ │
│ │ Caffeine │ ← 本地缓存(极快,毫秒级 1-5μs) │
│ │ (L1) │ │
│ └─────┬──────┘ │
│ │ 未命中 │
│ ▼ │
│ ┌────────────┐ │
│ │ Redis │ ← 分布式缓存(快,千微秒级 1-5ms) │
│ │ (L2) │ │
│ └─────┬──────┘ │
│ │ 未命中 │
│ ▼ │
│ ┌────────────┐ │
│ │ 数据库 │ ← 最终数据源(慢,10-100ms) │
│ └────────────┘ │
└─────────────────────────────────────────────────────┘多级缓存需要解决缓存一致性:
- 写操作:更新数据库后同步更新 Redis 和 Caffeine(或直接清除本地缓存,下次读取时回填)
- 缓存过期:Caffeine 设较短 TTL(如 5 分钟),Redis 设较长 TTL(如 30 分钟)
- 缓存穿透保护:Bloom Filter 或
null值短时间缓存
// 简化版多级缓存实现
public class MultiLevelCache implements Cache {
private final Cache localCache; // CaffeineCache
private final Cache remoteCache; // RedisCache
@Override
public ValueWrapper get(Object key) {
// 1. 查本地
ValueWrapper value = localCache.get(key);
if (value != null) return value;
// 2. 查远端,回填本地
value = remoteCache.get(key);
if (value != null) {
localCache.put(key, value.get());
}
return value;
}
@Override
public void put(Object key, Object value) {
localCache.put(key, value);
remoteCache.put(key, value);
}
@Override
public void evict(Object key) {
localCache.evict(key);
remoteCache.evict(key);
}
}生产意识:多级缓存的一致性是最大难点——本地缓存更新后如果其他实例未收到通知,会读到旧数据。常见方案:①通过 Redis Pub/Sub 或消息队列广播缓存失效事件;②Caffeine TTL 设很短(如 1-2 分钟),牺牲少量命中率换取接近最终一致性;③写操作后直接清除本地缓存而非更新,让下次读穿透重新填充。
小结
- EhCache 支持堆内 + 堆外 + 磁盘三级存储,配置灵活。
- Caffeine 性能更高,适合纯内存场景;EhCache 适合需要磁盘持久化的场景。
- 多级缓存(Caffeine + Redis)可同时利用本地速度与分布式共享能力。
- 易错:EhCache 3.x 在 Spring Boot 3.x 下需要
classifier: jakarta和javax.cache:cache-api依赖。磁盘持久化目录需要确保应用有读写权限,容器化场景下持久化目录应挂载为卷。EhCache 的 TTL 和 TTI 区别:TTL(存活时间,写入后固定时长过期)、TTI(空闲时间,最后一次读取后时长过期)。 - 思考任务:在现有项目中将缓存从 Caffeine 切换为 EhCache,配置堆外内存 100MB + 磁盘持久化,用 JVisualVM 或 Actuator cache 端点验证内存使用情况。
上一节:缓存抽象与 Redis 集成
下一节:任务管理
