1. 配置
1.1 基本配置
下面基本算是使用Ehcache的xml最简配置了。
1<?xml version="1.0" encoding="UTF-8"?> 2<ehcache name="mycache-manager" updateCheck="false"> 3 <!-- 磁盘缓存位置 --> 4 <diskStore path="java.io.tmpdir/ehcache"/> 5 6 <!-- 默认缓存 --> 7 <defaultCache 8 maxEntriesLocalHeap="1000" 9 eternal="false" 10 timeToIdleSeconds="3600" 11 timeToLiveSeconds="3600" 12 overflowToDisk="false"> 13 </defaultCache> 14 15 <!-- 用户缓存策略 --> 16 <cache name="userCache" 17 maxEntriesLocalHeap="2000" 18 eternal="false" 19 timeToIdleSeconds="600" 20 timeToLiveSeconds="0" 21 overflowToDisk="false" 22 statistics="true"> 23 </cache> 24</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 通过编程式配置
1@Test 2public void createCache(){ 3 CacheManager cacheManager = CacheManager.create(); 4 Cache cache = new Cache("cacheName", 1000, true, false, 120, 120); 5 CacheConfiguration config = cache.getCacheConfiguration(); 6 config.setClearOnFlush(true); 7 config.setMaxEntriesLocalHeap(100); 8 cacheManager.addCache(cache); 9}
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依赖
1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4</dependency> 5 6<dependency> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-data-jpa</artifactId> 9</dependency> 10<dependency> 11 <groupId>mysql</groupId> 12 <artifactId>mysql-connector-java</artifactId> 13 <scope>runtime</scope> 14</dependency> 15 16 <!--Spring Boot应用程序提供缓存支持--> 17<dependency> 18 <groupId>org.springframework.boot</groupId> 19 <artifactId>spring-boot-starter-cache</artifactId> 20</dependency> 21 22<!--Ehcache缓存实现--> 23<dependency> 24 <groupId>net.sf.ehcache</groupId> 25 <artifactId>ehcache</artifactId> 26</dependency> 27<!--JSR-107缓存规范--> 28<dependency> 29 <groupId>javax.cache</groupId> 30 <artifactId>cache-api</artifactId> 31</dependency>
依赖中忽略了spring-boot-starter-parent,选择自己喜欢的版本加入:
1<parent> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-parent</artifactId> 4 <version>2.4.1</version> 5</parent>
3.2 spring 配置
1logging.config=classpath:logback.xml 2 3spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 4spring.datasource.url=jdbc:mysql://localhost:3306/data?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 5spring.datasource.username=tim 6spring.datasource.password=123456 7 8spring.jpa.database=MySQL 9spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect 10spring.jpa.show-sql=true 11spring.jpa.hibernate.ddl-auto=update 12 13spring.datasource.hikari.maximum-pool-size=20 14spring.datasource.hikari.minimum-idle=5 15 16spring.cache.jcache.config=classpath:ehcache.xml
3.3 Ehcache配置
1<?xml version="1.0" encoding="UTF-8"?> 2<ehcache name="mycache-manager" updateCheck="false"> 3 4 <!-- 缓存数据磁盘位置 --> 5 <diskStore path="java.io.tmpdir"/> 6 7 <!-- 默认缓存 --> 8 <defaultCache 9 maxEntriesLocalHeap="1000" 10 eternal="false" 11 timeToIdleSeconds="3600" 12 timeToLiveSeconds="3600" 13 overflowToDisk="false"> 14 </defaultCache> 15 16 <!-- 用户缓存策略 --> 17 <cache name="userCache" 18 maxEntriesLocalHeap="2000" 19 eternal="false" 20 timeToIdleSeconds="60" 21 timeToLiveSeconds="120" 22 overflowToDisk="false" 23 diskPersistent="true"> 24 </cache> 25</ehcache>
将diskPersistent设置为true,方便执行不同Test的时候,还保留之前的缓存。
这里使用的是Ehcache的2.x版本,3.x版本做了很多的变化,实现了JSR-107,如果可以尽量选择3.x的版本。
Ehcache配置类:
1@Configuration 2@EnableCaching 3public class EhcacheConfiguration { 4 5 @Bean(name = "ehCacheCacheManager") 6 public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){ 7 return new EhCacheCacheManager(bean.getObject()); 8 } 9 10 @Bean 11 public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){ 12 EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean(); 13 ClassPathResource classPathResource = new ClassPathResource("ehcache.xml"); 14 cacheManagerFactoryBean.setConfigLocation (classPathResource); 15 cacheManagerFactoryBean.setShared(true); 16 return cacheManagerFactoryBean; 17 } 18}
使用@EnableCaching注解开启Spring缓存。
3.4 启动类
1import org.springframework.boot.SpringApplication; 2import org.springframework.boot.autoconfigure.SpringBootApplication; 3import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 4 5@SpringBootApplication 6@EnableJpaRepositories(basePackages = {"vip.mycollege.jpa.mysql.repository"}) 7public class Start { 8 public static void main(String[] args) { 9 SpringApplication.run(Start.class, args); 10 } 11}
3.5 实体类
1import javax.persistence.Column; 2import javax.persistence.Entity; 3import javax.persistence.GeneratedValue; 4import javax.persistence.GenerationType; 5import javax.persistence.Id; 6import javax.persistence.Table; 7import java.io.Serializable; 8 9@Entity 10@Table(name = "user") 11public class User implements Serializable { 12 13 @Id 14 @GeneratedValue(strategy = GenerationType.SEQUENCE) 15 private Integer id; 16 private Integer age; 17 @Column(length = 20) 18 private String name; 19 20 public Integer getId() { 21 return id; 22 } 23 24 public void setId(Integer id) { 25 this.id = id; 26 } 27 28 public Integer getAge() { 29 return age; 30 } 31 32 public void setAge(Integer age) { 33 this.age = age; 34 } 35 36 public String getName() { 37 return name; 38 } 39 40 public void setName(String name) { 41 this.name = name; 42 } 43 44 @Override 45 public String toString() { 46 return "User{" + 47 "id=" + id + 48 ", age=" + age + 49 ", name='" + name + '\'' + 50 '}'; 51 } 52}
实体类要实现Serializable,Ehcache持久化对象到磁盘需要。
3.6 Repository
1import org.springframework.data.repository.CrudRepository; 2import vip.mycollege.jpa.mysql.entity.User; 3 4public interface UserRepository extends CrudRepository<User,Integer> { 5 6}
3.7 缓存逻辑
1import org.springframework.cache.annotation.CacheEvict; 2import org.springframework.cache.annotation.CachePut; 3import org.springframework.cache.annotation.Cacheable; 4import org.springframework.stereotype.Service; 5import vip.mycollege.jpa.mysql.entity.User; 6import vip.mycollege.jpa.mysql.repository.UserRepository; 7 8import javax.annotation.Resource; 9 10@Service 11public class EhcacheService { 12 13 @Resource 14 private UserRepository userRepository; 15 16 @Cacheable(value="userCache", key="'user:' + #id") 17 public User findUserById(Integer id) { 18 System.out.println("execute findUserById"); 19 return userRepository.findById(id).get(); 20 } 21 22 @Cacheable(value="userCache", condition="#id < 3") 23 public User findCacheConditionUserById(Integer id) { 24 System.out.println("execute findCacheConditionUserById"); 25 return userRepository.findById(id).get(); 26 } 27 28 @CacheEvict(value="userCache",key="'user:' + #user.id") 29 public void deleteUser(User user) { 30 System.out.println("execute deleteUser"); 31 userRepository.deleteById(user.getId()); 32 } 33 34 @CacheEvict(value="userCache", allEntries=true) 35 public void deleteAllUserCache() { 36 System.out.println("execute deleteAllUserCache"); 37 System.out.println("delete all cache"); 38 } 39 40 @CachePut(value = "userCache",key = "'user:'+#user.id") 41 public User updateUser(User user) { 42 System.out.println("execute updateUser"); 43 user.setAge(100); 44 userRepository.save(user); 45 return user; 46 } 47}
可以自己生成一些数据,然后用下面的测试类来测试不同缓存的效果。
3.8 测试类
1import org.junit.Test; 2import org.junit.runner.RunWith; 3import org.springframework.boot.test.context.SpringBootTest; 4import org.springframework.test.context.junit4.SpringRunner; 5import vip.mycollege.jpa.mysql.entity.User; 6 7import javax.annotation.Resource; 8 9 10@RunWith(SpringRunner.class) 11@SpringBootTest 12public class EhcacheServiceTest { 13 14 @Resource 15 private EhcacheService ehcacheService; 16 17 @Test 18 public void findUserById() { 19 User user = ehcacheService.findUserById(1); 20 System.out.println(user); 21 } 22 23 @Test 24 public void findCacheConditionUserById() { 25 User user = ehcacheService.findCacheConditionUserById(5); 26 System.out.println(user); 27 } 28 29 @Test 30 public void deleteUser() { 31 User user = new User(); 32 user.setId(1); 33 ehcacheService.deleteUser(user); 34 } 35 36 @Test 37 public void deleteAllUserCache() { 38 ehcacheService.deleteAllUserCache(); 39 } 40 41 @Test 42 public void updateUser() { 43 User user = new User(); 44 user.setId(1); 45 ehcacheService.updateUser(user); 46 } 47}
4. 文档资料
Ehcache2.9文档 Ehcache3.8文档 Ehcache快速开始 Ehcache示例 SpringBoot缓存文档