1. 配置
1.1 基本配置
下面基本算是使用Ehcache的xml最简配置了。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="mycache-manager" updateCheck="false">
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/ehcache"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false">
</defaultCache>
<!-- 用户缓存策略 -->
<cache name="userCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache>
</ehcache>
1.2 ehcache
每个ehcache对应一个CacheManager,ehcache的name用来配置CacheManager的名称。
可以通过下面的方式获取:
CacheManager cacheManager = CacheManager.getCacheManager("mycache-manager");
1.3 diskStore
diskStore用来配置缓存数据保存的磁盘位置,常用的三个:
- user.home:用户主目录
- user.dir:用户当前工作目录
- java.io.tmpdir:默认临时文件路径
当然也可以使用绝对路径
1.4 defaultCache
默认缓存策略,当Ehcache没有找到缓存策略的时候,就会使用这个缓存策略,只能定义一个默认策略。
1.5 cache配置项
缓存策略,设置缓存超时时间,最大缓存数目等。
配置项
说明
name
缓存的名称,可以通过指定名称获取指定的某个Cache对象
eternal
true,永不过期,超时设置将被忽略,一些静态的配置数据可以设置为true
statistics
是否收集统计信息,如果需要监控缓存使用情况,应该设置为true,默认false,统计对性能有影响
clearOnFlush
内存数量最大时是否清除
overflowToDisk
内存不足时,是用磁盘进行缓存
diskPersistent
是否启用磁盘持久化的机制,JVM重启时可以加载之前缓存,默认值是false
timeToIdleSeconds
对象允许闲置时间,单位秒,超时过期,0表示可以一直空闲
timeToLiveSeconds
缓存数据最多存活时间,0表示不过期,timeToLiveSeconds不等于0时应该大于timeToIdleSeconds
maxElementsOnDisk
磁盘最大缓存多少cache数量
maxElementsInMemory
内存中允许存储的最大的元素个数,0代表无限个
maxEntriesLocalDisk
当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中
maxEntriesLocalHeap
堆内存中最大缓存对象数,0表示没有限制
diskSpoolBufferSizeMB
设置磁盘缓存的缓存区大小,默认是30MB,每个Cache都应该有自己的一个缓冲区
memoryStoreEvictionPolicy
缓存达到maxElementsInMemory限制时,内存缓存过期策略算法,LRU(最近最少使用,默认)、FIFO(先进先出)、LFU(最少访问次数)
diskExpiryThreadIntervalSeconds
检查磁盘缓存数据过期线程运行时间间隔,默认是120秒
1.6 通过编程式配置
@Test
public void createCache(){
CacheManager cacheManager = CacheManager.create();
Cache cache = new Cache("cacheName", 1000, true, false, 120, 120);
CacheConfiguration config = cache.getCacheConfiguration();
config.setClearOnFlush(true);
config.setMaxEntriesLocalHeap(100);
cacheManager.addCache(cache);
}
2. Spring与Ehcache
Spring对缓存的支持,是通过代理实现的,直接通过注解标就可以实现缓存,基本不需要处理太多和缓存相关的逻辑。
Spring的几个缓存相关的注解参数都支持SpEL表达式,下面是几个SpEL常用表达:
属性
位置
说明
示例
args
root
当前方法参数数组
#root.args[0]
method
root
当前方法
#root.method.name
target
root
当前被调用的对象
#root.target
caches
root
当前被调用的方法使用的Cache
#root.caches[0].name
methodName
root
当前方法名
#root.methodName
targetClass
root
当前被调用的对象的class
#root.targetClass
argument
context
当前被调用的参数,saveUser(User user)
#user.id
result
context
当前被调用的返回值
#result
2.1 @Cacheable
用@Cacheable注解的方法表示会缓存改方法的返回值。
当第一次调用这个方法时,方法的返回值会被缓存下来,在缓存的有效时间内,以后访问这个方法都直接返回缓存结果,不再执行方法中的代码段。
@Cacheable参数:
- value:缓存策略名称用于查找缓存位置,不能为空,Ehcache就是xml配置的cache的name,说明缓存数据放到哪个Cache中
- key:指定缓存使用的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL
- condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL
2.2 @CachePut
@CachePut和@Cacheable基本一样,但是它每次都会执行方法。一般用在更新方法上,这样可以同时更新缓存和数据库。
2.3 @CacheEvict
@CacheEvict用来删除缓存数据,它有参数:
- value:缓存策略名称用于查找缓存位置,不能为空
- key:缓存的key,默认为空
- condition:触发条件,只有满足条件的情况才会清除缓存,默认为空
- allEntries:是否清除全部缓存,默认为false,true表示清除value指定策略中的全部缓存
3. 实例与测试
3.1 maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--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>
<!--JSR-107缓存规范-->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
依赖中忽略了spring-boot-starter-parent,选择自己喜欢的版本加入:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
</parent>
3.2 spring 配置
logging.config=classpath:logback.xml
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/data?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=tim
spring.datasource.password=123456
spring.jpa.database=MySQL
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
spring.cache.jcache.config=classpath:ehcache.xml
3.3 Ehcache配置
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="mycache-manager" updateCheck="false">
<!-- 缓存数据磁盘位置 -->
<diskStore path="java.io.tmpdir"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false">
</defaultCache>
<!-- 用户缓存策略 -->
<cache name="userCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="60"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="true">
</cache>
</ehcache>
将diskPersistent设置为true,方便执行不同Test的时候,还保留之前的缓存。
这里使用的是Ehcache的2.x版本,3.x版本做了很多的变化,实现了JSR-107,如果可以尽量选择3.x的版本。
Ehcache配置类:
@Configuration
@EnableCaching
public class EhcacheConfiguration {
@Bean(name = "ehCacheCacheManager")
public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){
return new EhCacheCacheManager(bean.getObject());
}
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
ClassPathResource classPathResource = new ClassPathResource("ehcache.xml");
cacheManagerFactoryBean.setConfigLocation (classPathResource);
cacheManagerFactoryBean.setShared(true);
return cacheManagerFactoryBean;
}
}
使用@EnableCaching注解开启Spring缓存。
3.4 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages = {"vip.mycollege.jpa.mysql.repository"})
public class Start {
public static void main(String[] args) {
SpringApplication.run(Start.class, args);
}
}
3.5 实体类
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
@Entity
@Table(name = "user")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private Integer age;
@Column(length = 20)
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
实体类要实现Serializable,Ehcache持久化对象到磁盘需要。
3.6 Repository
import org.springframework.data.repository.CrudRepository;
import vip.mycollege.jpa.mysql.entity.User;
public interface UserRepository extends CrudRepository<User,Integer> {
}
3.7 缓存逻辑
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import vip.mycollege.jpa.mysql.entity.User;
import vip.mycollege.jpa.mysql.repository.UserRepository;
import javax.annotation.Resource;
@Service
public class EhcacheService {
@Resource
private UserRepository userRepository;
@Cacheable(value="userCache", key="'user:' + #id")
public User findUserById(Integer id) {
System.out.println("execute findUserById");
return userRepository.findById(id).get();
}
@Cacheable(value="userCache", condition="#id < 3")
public User findCacheConditionUserById(Integer id) {
System.out.println("execute findCacheConditionUserById");
return userRepository.findById(id).get();
}
@CacheEvict(value="userCache",key="'user:' + #user.id")
public void deleteUser(User user) {
System.out.println("execute deleteUser");
userRepository.deleteById(user.getId());
}
@CacheEvict(value="userCache", allEntries=true)
public void deleteAllUserCache() {
System.out.println("execute deleteAllUserCache");
System.out.println("delete all cache");
}
@CachePut(value = "userCache",key = "'user:'+#user.id")
public User updateUser(User user) {
System.out.println("execute updateUser");
user.setAge(100);
userRepository.save(user);
return user;
}
}
可以自己生成一些数据,然后用下面的测试类来测试不同缓存的效果。
3.8 测试类
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import vip.mycollege.jpa.mysql.entity.User;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class EhcacheServiceTest {
@Resource
private EhcacheService ehcacheService;
@Test
public void findUserById() {
User user = ehcacheService.findUserById(1);
System.out.println(user);
}
@Test
public void findCacheConditionUserById() {
User user = ehcacheService.findCacheConditionUserById(5);
System.out.println(user);
}
@Test
public void deleteUser() {
User user = new User();
user.setId(1);
ehcacheService.deleteUser(user);
}
@Test
public void deleteAllUserCache() {
ehcacheService.deleteAllUserCache();
}
@Test
public void updateUser() {
User user = new User();
user.setId(1);
ehcacheService.updateUser(user);
}
}
4. 文档资料
Ehcache2.9文档 Ehcache3.8文档 Ehcache快速开始 Ehcache示例 SpringBoot缓存文档