一. 概念
Ehcache是一个纯Java实现的进程内缓存框架,具有快速、精干的特点,是Hibernate中默认的CacheProvider。它是一个内存+文件的缓存框架,当内存不足的时候,会将溢出的数据序列化到文件中,当在需要使用时,再反序列到内存中。所以,通过Ehcache管理的数据,必须实现Serializable接口。
Ehcache是一个框架,既可以集成到本地应用中,做本地缓存,同样也可以独立架设。
Ehcache的缓存空间实际上就是JVM虚拟机中的一个连续的存储空间,这个缓存空间的管理可以通过配置文件来决定,并且可以划分成若干个相互独立的空间。
二. 应用
1. maven pom.xml中引入相关依赖
<!-- Spring boot缓存支持启动器(针对于所有内存型缓存框架的启动器,类似于驱动。至于需要缓存什么技术,那就需要独立引入依赖了) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- EHCache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2. EHCache配置文件
如果不清楚EHCache配置文件的写法,我们可以找到ehcache的jar包,打开ehcache-failsafe.xml,这份文件详细的说明了ehcache该如何配置
ehcache对配置文件的名称和路径都没有特殊要求,我放置在了resources/ehcache目录下。
ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 配置序列化文件存储的目录路径 -->
<diskStore path="java.io.temdir"/>
<!-- 默认缓存区,在不指定使用哪一个缓存区的时候,使用默认缓存区
maxElementsInMemory: 内存中最多缓存对象的数目
timeToIdleSeconds: 空闲的对象缓存时长多少秒 (空闲指的是没有任务外部请求访问该对象) 超出指定秒数后,ehcache会将其从内存中序列化到文件
timeToLiveSeconds: 缓存对象的生命周期(单位: 秒)
maxElementsOnDisk: 文件中最多缓存对象的数目
memoryStoreEvictionPolicy: 内存的缓存策略。 当内存不足时,ehcache通过相应的策略,决定序列化哪些对象。
1. LRU(Least Recently Used)
最近最少使用。把最近(单位时间)内,使用频率最少的对象从内存中序列化存储到文件内,从内存中腾出空间供新对象缓存使用。
2. LFU(Least Frequently Used)
最不经常使用 一段时间内,使用次数最少的对象从内中存序列化存储到文件内。
3. FIFO(First In First Out)
先进先出
注意,LRU和LFU的概念容易混淆。它们都会在单位时间内分析一个对象的去留,但LRU关心的是最后一次操作的时间距离当前准备时间的时长,而LFU针对的是使用次数。
比如,通过考虑当前1小时内的执行情况,决定缓存中对象A和对象B的去留。
若对象A在前10分钟被操作了1万次,在后50分钟没有被操作。对象B每隔10分钟就会被操作一次,并且在距离当前的前1分钟还被操作过一次。
因此,
若使用LRU,侧重于观察最后一次执行的时间距离当前时间的时长。显然,对象B保留,对象A序列化到文件内,并从内存中移除。
若使用LFU,侧重于观察单位时间内的执行次数,显然,对象A保留,对象B序列化到文件内,并从内存中移除。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="200"
maxElementsOnDisk="50000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
>
<persistence strategy="localTempSwap"/>
</defaultCache>
<!-- 自定义缓存区,可以通过name命名指定使用的缓存区 -->
<cache name="myCustomCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="200"
maxElementsOnDisk="50000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
>
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
3. application.properties全局配置文件中添加ehcache相关配置
spring.cache.ehcache.config=classpath:ehcache/ehcache.xml
4. main启动器中配置
@SpringBootApplication
@EnableCaching //代表Spring应用是否开启缓存策略
public class AppStarter {
public static void main(String[] args) {
SpringApplication.run(AppStarter.class, args);
}
}
5. 业务代码中的应用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 缓存一般用于查询操作
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
/**
* @Cacheable 注解
* 描述类: 代表当前类中所有有返回值的方法,返回的数据都缓存
* 描述方法: 代表当前方法的返回值缓存
* 注解属性:
* 1. value: 使用的是哪个缓存区。 缓存区的名称在ehcache配置文件中通过name属性定义。如果不定义,默认使用defaultCache
* 2. key: 缓存区中保存缓存数据的唯一标志,默认为空。如果不定义key,EHCache有特殊的定义方式,确保不同对象的key不重复。
* 如果方法有参数,EHCache会使用参数数据作为key,如果方法无参数,EHCache会根据方法的命名来生成一个唯一的GUID作为key
* 赋值可以使用SpringEL表达式
* 3. condition: 生效条件。当数据为true时,代表缓存。可以使用SpringEL表达式来赋值。
*/
@Override
@Cacheable(value = "myCustomCache", key = "'allUsers'") //由于是将一个固定的字符串作为key,因此要在字符串周围加上'',否则会被当成SpringEL表达式来解析
public List<User> findAll() {
return userDao.findAll();
}
@Override
@Cacheable(value = "myCustomCache", key="#page+#size") //比如传入的是findByPage(1,3) 那么存入缓存时的key就是13 这样便保证了key的不同
public Page<User> findByPage(int page, int size) {
Pageable pageable = new PageRequest(page-1, size);
return this.userDao.findAll(pageable);
}
@Override
@Cacheable(value = "myCustomCache", key="'#id'", condition = "#id < 3") //当且仅当id小于3时才会缓存数据
public User findById(Long id) {
return this.userDao.findOne(id);
}
/**
* @CacheEvict
* 管理缓存。描述了在何时需要清理缓存的哪些对象
* 注解属性:
* 1. value: 指定缓存区,默认为defaultCache缓存区。
* 2. key: 清理 哪一个缓存对象。 默认为空,代表不清理任何缓存对象
* 3. allEntries: 是否清理缓存中的所有对象 true|false 默认为false
* 4. condition: 满足时才清理缓存
*/
@Override
@CacheEvict(value= "myCustomCache", key="'allUsers'") //清理key为"allUsers"的缓存区
public void saveOrUpdate(User user) {
this.userDao.save(user);
}
@Override
@CacheEvict(value = "myCustomCache", allEntries=true) //清理所有对象 (所以就没必要写key了)
public void deleteById(Long id) {
this.userDao.delete(id);
}
}