EhCache 是一个比较成的Java缓存框架,最早从 hibernate 发展而来,是进程中的缓存系统,它提供了内存、磁盘文件存储,以及分布式存储方案等多种灵活的cache管理方案。
Sprging boot 对 Ehcache 的使用提供支持,所以在 spring boot 中只需要简单配置即可使用Ehcache实现数据缓存处理。
相关注解
@CacheConfig
用于标注在类上,可以存放该类中所有缓存的公有属性,比如设置缓存的名字
1 2 3 4 5
| @CacheConfig(cacheName = "users") public interface UserService( )
|
配置了该数据访问对象中返回的内容将存储于名为 users 的缓存对象中,我们也可以不使用该注解,直接通过 @cacheable 自己配置缓存集的名字来定要。
@Cacheable
应用到读取数据的方法上,即可缓存的方法,如查找方法,先从缓存中读取,如果没有再调用相应方法获取数据,然后把数据添加到缓存中。该注解主要有下面借个参数。
- value、cacheNames:两个等同的参数(cacheNames为 spring4新增,作为value的别名),用于指定缓存存储的集合名。由于spring4中新增了@CacheConfig,因此在spring3中原本必须有的value属性,也成了非必需项了。
- key:缓存对象存储在 Map 集合中的 key 值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需要使用 SpEl 表达式,比如
@Cacheable(key = "#p0")
使用函数的第一个参数作为缓存的key值。
- condition:缓存对象的条件,非必需,也需要使用SpEl表达式,只有满足表达式条件的内容才会被缓存,比如:
@Cacheable(key = "#p0", condition = "#p0.length() < 3")
,表示只有当第一个参数的长度小于3的时候才会被缓存。
- unless:另外一个缓存条件参数,非必需,也需要使用SpEL表达式。它不同于 condition 参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断,所以它可以通过对 result 进行判断。
- keyGenerator:用于指定 key 生成器,非必需。若需要指定一个自定义的 key 生成器,我们需要去实现
org.springframework.cache.interceptor.KeyGenerator
接口,并使用该参数来指定。需要注意的是,该参数与key是互斥的。
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用。
- cacheResolver:用于指定使用哪个缓存解析器,非必需。需要通过
org.springframework.cache.interceptor.CacheResolver
接口来实现自己的缓存解析器,并用该参数指定。
@CachePut
应用到写数据的方法上,如 新增/修改方法(此放必须返回需要写到缓存中的数据),调用方法时会自动把相应的数据放入缓存,@CachePut的参数与@Cacheable类似。
1 2 3 4
| @CachePut(value="users", key = "#user.id") public User add(User user) { }
|
@CacheEvict
应用到删除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据
1 2
| @cacheEvict(value = "uses", key="#id") void delete(final Integer id);
|
除了同@Cacheable一样的参数之外,@CacheEvict还有下面两个参数:
- allEntries:非必需,默认是false。当为 true 时,会移除所有数据。
- beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
@Caching
组合多个 Cache 注解使用,如
1 2 3 4 5 6 7
| @Caching( put = { @CachePut(value="user", key="#user.id") @CachePut(value="user", key="#user.username") @CachePut(value="user", key="#user.age") } )
|
环境配置
添加依赖
- ehcache
- spring-boot-starter-cache
pom.xml1 2 3 4 5 6 7 8
| <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
|
缓存配置
src/main/resources 目录下添加文件 ehcache.xml
,内容如下
ehcache.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| <ehcache name="mycache">
<diskStore path="c:\java\cache" />
<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
<cache name="users" eternal="false" maxElementsInMemory="100" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/> </ehcache>
|
springboot 缓存配置
application.yml1 2 3 4 5 6 7 8 9
|
spring: cache: encache: config: classpath:ehcache.xml
|
启动缓存
在项目的入口类中,添加 @EnableCaching
注解,启动缓存
1 2 3 4 5 6
| @SpringBootApplication @EnableCaching @MapperScan("com.xxx.dao") public calss Starter{ SpringApplicatin.run(Starter.class); }
|
使用缓存
bean对象实现序列化
目标缓存对象需要实现序列化,如实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package org.example.po;
import lombok.Data; import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank; import java.io.Serializable;
@Data public class User implements Serializable { private Integer userId;
@NotBlank(message = "用户名不能为空") private String userName;
@NotBlank(message = "密码不能为空") @Length(min =6, max = 10, message="密码长度至少6位但不超过10位") private String userPwd; }
|
使用注解缓存实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| package org.example.service;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import org.apache.commons.lang3.StringUtils; import org.example.dao.UserMapper; import org.example.po.User; import org.example.query.UserQuery; import org.example.util.AssertUtil; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import java.util.List; import java.util.Map;
@Service public class UserService { @Resource private UserMapper userMapper;
public User findUserByName(String userName){
return this.userMapper.findUserByName(userName); }
@Cacheable(value="users", key = "#userId") public User findUserById(Integer userId){ return this.userMapper.findUserById(userId); }
@Cacheable(value="users", key="#userQuery.userName + '-' + #userQuery.pageNum + '-' + #userQuery.pageSize") public PageInfo<User> findUserByParam(UserQuery userQuery){ PageHelper.startPage(userQuery.getPageNum(), userQuery.getPageSize()); List<User> userList = this.userMapper.findUserByParam(userQuery); return new PageInfo<User>(userList); }
@CachePut(value = "users", key = "#user.userId") public User addUser(User user) { AssertUtil.isTrue(user == null, "用户信息不能为空"); AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户姓名不能为空"); AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"密码不能为空"); AssertUtil.isTrue(this.findUserByName(user.getUserName()) != null, "用户名不能重复");
AssertUtil.isTrue(this.userMapper.addUser(user) < 1, "添加用户失败");
return user; }
@Transactional(propagation = Propagation.REQUIRED) @CachePut(value = "users", key = "#user.userId") public User updateUser(User user){ AssertUtil.isTrue(user == null, "用户信息不能为空"); AssertUtil.isTrue(user.getUserId() == null,"用户数据异常"); AssertUtil.isTrue(StringUtils.isBlank(user.getUserName()),"用户姓名不能为空"); AssertUtil.isTrue(StringUtils.isBlank(user.getUserPwd()),"密码不能为空"); User checkUser = this.userMapper.findUserByName(user.getUserName()); AssertUtil.isTrue(checkUser != null && !checkUser.getUserId().equals(user.getUserId()), "用户名已存在");
AssertUtil.isTrue(this.userMapper.updateUser(user) < 1, "修改用户失败"); return user; }
@CacheEvict(value = "users", key = "#userId") public void deleteUserById(Integer userId){ AssertUtil.isTrue( userId == null ||this.userMapper.findUserById(userId) == null, "用户不存在"); AssertUtil.isTrue(this.userMapper.deleteUserById(userId) < 1, "删除用户失败"); } }
|