在springboot3.x中使用Ehcache3.x
在springboot3.x中使用Ehcache3.x
Ehcache是一个纯Java的进程内缓存框架,它具有快速、精干等特点,被广泛应用于提升应用性能和减少与后端数据库的交互次数。
主要功能和特点
内存和磁盘存储:Ehcache支持内存和磁盘两种存储方式,当内存缓存达到上限时,可以将数据溢出到磁盘上,从而避免数据丢失。
多种缓存策略:提供了基于时间、基于访问和基于大小的多种缓存失效策略,以及LRU(最近最少使用)、LFU(最少使用)和FIFO(先进先出)等缓存驱逐策略。
持久化缓存:支持将数据持久化到磁盘或其他数据源,以确保应用程序重启后能够从持久化存储中恢复数据。
丰富的API和工具类:提供了易于使用的API和工具类,方便完成缓存的读写和管理。
与多种框架和技术集成:如Spring、Hibernate、MyBatis等,使得用户可以更加便捷地使用Ehcache。
架构和原理
CacheManager:缓存管理器,是Ehcache的核心组件之一,用于管理缓存实例。它可以从配置文件中加载缓存配置,创建和管理多个缓存实例。
Cache:缓存是实际存储数据的容器,每个缓存实例都有唯一的名称,并由CacheManager创建和管理。缓存通常使用哈希表作为底层数据结构,以实现快速查找缓存项。
缓存项(Cache Entry):缓存中的数据单元,每个缓存项通常包含一个键和一个值,键用于唯一标识缓存项,值是实际的数据。
LRU算法:管理缓存项的访问顺序,确保最近使用的数据项保持在缓存的前部,而最不常使用的数据项位于缓存的尾部。
存储方式
堆存储:将缓存数据存储在Java堆内存中,存取速度快,但容量有限。
堆外存储:基于NIO的DirectByteBuffers实现,存储在堆外内存上,不受GC影响,可以保证响应时间的稳定性,但内存分配开销比堆内存大。
磁盘存储:将数据存储在磁盘上,保障服务重启后内存数据能够重新从磁盘上加载,读取效率最低。
优缺点
优点:
- 速度快:采用高效的缓存策略,能够实现快速的数据访问和读写。
- 可扩展:支持分布式缓存,可以方便地扩展到多台服务器上。
- 可靠性高:内置了多种缓存策略,支持数据持久化和恢复,提供了完善的故障检测和纠正机制。
缺点:
- 使用磁盘cache时,非常占用磁盘空间。
- 节点重启时,可能存在缓存丢失的问题。
- 无法大量存储,受限于JVM。
- 集群多节点缓存不一致问题。
Spring Boot中配置和使用
在Spring Boot中使用Ehcache组件通常包括以下几个步骤:
- 引入jar包:在Spring Boot项目中引入Ehcache的jar包依赖。
- 配置ehcache.xml:在项目resource的目录下新建ehcache.xml配置文件,并加入相关配置,如缓存名称、内存和磁盘存储配置、缓存失效策略等。
- 使用Ehcache缓存:通过Spring Boot提供的@Cacheable、@CachePut、@CacheEvict等注解来简化缓存操作。
springboot中可用的注解
在springboot中需要缓存的方法上使用Spring Cache提供的注解,如@Cacheable、@CachePut和@CacheEvict。
Ehcache3.x的配置项
Ehcache3.x的XML配置项提供了灵活的方式来定义缓存的行为和特性。
参数 |
|
|
| 说明 |
persistence | 直接配置 | |||
| directory | 数据存储目录 | ||
cache | 缓存配置 | |||
| alias | 缓存名称 | ||
| key-type | 缓存 key 的类型 | ||
| value-type | 缓存 value 的类型 | ||
| expiry | 缓存过期配置 | ||
| tti | 缓存中条目的最大空闲时间 | ||
| ttl | 缓存中条目的最大存活时间 | ||
| resources | 资源配置 | ||
| heap | 堆内存配置 | ||
| unit | 大小单位 | ||
| offheap | 堆外存配置 | ||
| unit | 大小单位 | ||
| disk | 磁盘配置 | ||
| persistence | 是否持久化 | ||
| unit | 大小单位 |
示例配置
以下是一个简单的Ehcache3.x 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.x.xsd">
<!-- 指定缓存目录 -->
<persistence directory="${java.io.tmpdir}/cache-data"/>
<!-- 缓存模板 -->
<cache-template name="default">
<key-type>java.lang.String</key-type>
<value-type>java.lang.Object</value-type>
<expiry>
<ttl unit="seconds">600</ttl>
</expiry>
<resources>
<heap unit="entries">2000</heap>
<offheap unit="MB">100</offheap>
</resources>
</cache-template>
<!-- 具体的缓存实例,继承自default模板 -->
<cache alias="sample" uses-template="default"/>
<!-- 另一个具体的缓存实例,继承自default模板但覆盖了过期时间 -->
<cache alias="authority_service" uses-template="default">
<expiry>
<ttl unit="hours">1</ttl>
</expiry>
</cache>
</config>
在这个示例中,我们定义了一个名为"default"的缓存模板,并设置了键的类型、值的类型、过期策略和资源配置。然后,我们定义了两个具体的缓存实例,它们分别继承了"default"模板,但其中一个覆盖了过期时间。
springboot + ehcache缓存范例
引入依赖
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
<!--开启缓存支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
配置Ehcache
在src/main/resources目录下创建ehcache.xml文件,并配置缓存的相关信息,如缓存名称、内存和磁盘存储配置、缓存失效策略等。
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults enable-statistics="true"/>
</service>
<cache alias="address" >
<key-type>java.lang.String</key-type>
<value-type>java.lang.Object</value-type>
<expiry>
<ttl unit="seconds">120</ttl> <!-- 2分钟过期 -->
</expiry>
<resources>
<heap unit="entries">10000</heap> <!-- 堆内存限制为10000个单位 -->
<offheap unit="MB">50</offheap>
</resources>
</cache>
</config>
application文件中配置
spring:
cache:
type: jcache
配置类
在配置类上添加@EnableCaching注解以启用缓存功能:
@EnableCaching
@Configuration
public class CacheManagerConfig {
@Bean
JCacheCacheManager ehcacheConfiguration() {
// 缓存配置
try {
CachingProvider cachingProvider = Caching.getCachingProvider();
URL url = getClass().getResource("/ehcache.xml");
CacheManager cacheManager = cachingProvider.getCacheManager(
url.toURI(),
getClass().getClassLoader());
// jcache
JCacheCacheManager JCache = new JCacheCacheManager();
JCache.setCacheManager(cacheManager);
return JCache;
}catch (URISyntaxException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
SpringCache提供了一些实现帮助我们整合 Ehcache 2.x、Caffeine、JSR-107(例如Ehcache3)等框架。SpringCache在JCache包下为JSR-107提供了一个默认实现JCacheCacheManger,这里通过配置类进行了设置。
缓存注解使用
添加service类:
package cn.jet.demoehcache.service;
import cn.jet.demoehcache.entity.Address;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @ 基本功能:
* @ program:demo-Ehcache
* @ author:Jet
* @ create:2025-02-14 10:56:06
**/
@Service
public class AddrService {
@Cacheable(value = "address")
public List<Address> getAddressList() {
List<Address> addressList = new ArrayList<>();
for(int i = 0;i<10;i++) {
Address addr = Address.builder()
.id(i+1)
.province("省份" + (i+1))
.city("城市" + (i+1))
.district("县区" + (i+1))
.street("街道" + (i+1))
.build();
addressList.add(addr);
}
return addressList;
}
}
单元测试
@SpringBootTest
public class AddrServiceTest {
@Autowired
private AddrService addrService;
@Test
public void getAddressListTest() {
List<Address>addressList1 = addrService.getAddressList();
System.out.println(addressList1.size());
List<Address>addressList2 = addrService.getAddressList();
System.out.println(addressList2.size());
}
}
编码使用
添加service类:
Spring本身就定义了缓存接口Cache和管理缓存控制器CacheManager 。注意引入路径。
org.springframework.cache.Cache
org.springframework.cache.CacheManager
添加服务类:
package cn.jet.demoehcache.service;
import cn.jet.demoehcache.entity.Address;
import jakarta.annotation.Resource;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @ 基本功能:
* @ program:demo-Ehcache
* @ author:Jet
* @ create:2025-02-14 15:24:35
**/
@Service
public class AddrServiceB {
@Resource
CacheManager cacheManager;
public List<Address> getAddressList() {
List<Address> addressList = new ArrayList<>();
//获取缓存对象,"address" 就是 ehcache.xml 中 <cache> 标签的 alias
Cache cache = cacheManager.getCache("address");
//获取刚刚存入的值
Cache.ValueWrapper res = cache.get("addressList");
//如果没有或者过期就为null
if (null != res) {
//这里获取 ehcache.xml 中 <cache> value-type 定义的类型,可以直接强转。
addressList = (List<Address>) res.get();
}else {
for (int i = 0; i < 10; i++) {
Address addr = Address.builder()
.id(i + 1)
.province("省份" + (i + 1))
.city("城市" + (i + 1))
.district("县区" + (i + 1))
.street("街道" + (i + 1))
.build();
addressList.add(addr);
}
//存入缓存
cache.put("addressList", addressList);
}
return addressList;
}
}
单元测试
@SpringBootTest
public class AddrServiceBTest {
@Autowired
private AddrServiceB addrServiceB;
@Test
public void getAddressListTestB() {
List<Address>addressList1 = addrServiceB.getAddressList();
System.out.println(addressList1.size());
List<Address>addressList2 = addrServiceB.getAddressList();
System.out.println(addressList2.size());
}
}
清除缓存
清除缓存中指定条目:
try {
javax.cache.CacheManager cm = ((JCacheCacheManager) cacheManager).getCacheManager();
javax.cache.Cache<String, Object> cache = cm.getCache("address");
for (TransmitPointType item : TransmitPointType.values()) {
cache.remove(item.getCacheKey());
}
}catch (Exception e) {
e.printStackTrace();
throw new ECException(ErrorCode.OPERATE_ERROR.getCode(),e.getMessage());
}
或者:
Cache cache = cacheManager.getCache("address");
cache.evict(Object key)
根据提供的键(key)移除缓存中的一个特定条目。它只会影响指定的缓存项,其他缓存数据不会受到影响。
清除缓存中所有条目:
cacheManager.getCache("aa").clear();
它会移除当前缓存中的所有数据,无论这些数据是否已经过期或是否正在被使用。