目录
1 非注解方式
1.1 依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
1.2 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!-- 磁盘缓存位置 -->
<diskStore path="/home/zhangxt/ehcache" />
<!-- defaultCache,是默认的缓存策略 -->
<!-- 如果你指定的缓存策略没有找到,那么就用这个默认的缓存策略 -->
<defaultCache
<!-- external:缓存对象是否一直存在,如果设置为true的话,那么timeout就没有效果,缓存就会一直存在,一般默认就是false -->
eternal="false"
<!-- maxElementsInMemory:内存中可以缓存多少个缓存条目 -->
maxElementsInMemory="1000"
<!-- overflowToDisk:如果内存不够的时候,是否溢出到磁盘 -->
overflowToDisk="false"
<!-- diskPersistent:是否启用磁盘持久化的机制,在jvm崩溃的时候和重启之间 -->
diskPersistent="false"
<!-- timeToIdleSeconds:对象最大的闲置的时间,如果超出闲置的时间,可能就会过期 单位:秒 当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大-->
timeToIdleSeconds="300"
<!-- timeToLiveSeconds:对象最多存活的时间 单位:秒 当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是存活时间无穷大-->
timeToLiveSeconds="0"
<!-- memoryStoreEvictionPolicy:当缓存数量达到了最大的指定条目数的时候,需要采用一定的算法,从缓存中清除一批数据-->
<!--LRU:(Least Rencently Used)新来的对象替换掉使用时间算最近很少使用的对象。
<!--LFU:(Least Frequently Used)替换掉按命中率高低算比较低的对象。-->
<!--FIFO: (First In First Out)把最早进入二级缓存的对象替换掉。-->
memoryStoreEvictionPolicy="LRU" />
<!-- 手动指定的缓存策略 -->
<!-- 对不同的数据,缓存策略可以在这里配置多种 -->
<cache
<!-- name:缓存名称 -->
name="local"
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="300"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
1.3 EhcacheUtil工具类
方便操作CacheManager啦
public class EhCacheUtil {
//配置文件路径
private static final String path = "/ehcache.xml";
private URL url;
private CacheManager manager;
private static EhCacheUtil ehCache;
private EhCacheUtil(String path) {
url = getClass().getResource(path);
manager = CacheManager.create(url);
}
public static EhCacheUtil getInstance() {
if (ehCache == null) {
ehCache = new EhCacheUtil(path);
}
return ehCache;
}
public void put(String cacheName, String key, Object value) {
Cache cache = manager.getCache(cacheName);
Element element = new Element(key, value);
cache.put(element);
}
public Object get(String cacheName, String key) {
Cache cache = manager.getCache(cacheName);
Element element = cache.get(key);
return element == null ? null : element.getObjectValue();
}
public Cache get(String cacheName) {
return manager.getCache(cacheName);
}
public void remove(String cacheName, String key) {
Cache cache = manager.getCache(cacheName);
cache.remove(key);
}
public boolean replace(String cacheName, String key, Element value) {
Cache cache = manager.getCache(cacheName);
Element old = cache.get(key);
return cache.replace(old, value);
}
}
1.4 使用
@RequestMapping("/put")
public @ResponseBody String put(String key, String value){
EhCacheUtil.getInstance().put("local",key,value);
return "success";
}
@RequestMapping("/get")
public @ResponseBody Object get(String key){
Object obj = EhCacheUtil.getInstance().get("local",key);
return obj;
}
注意:
- 如果开启overflowToDisk,超过maxElementsInMemory数量的缓存会被写入到磁盘。
- 缓存的类要实现Serializable接口,数据才能写到磁盘。
- 关闭overflowToDisk时,按memoryStoreEvictionPolicy的算法清除旧缓存。
2 注解方式
2.1 @Cacheable
@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
2.1.1 属性
- value属性指定Cache名称
value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。
@Cacheable("cache1")//Cache是发生在cache1上的
public User find(Integer id) {
return null;
}
@Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的
public User find(Integer id) {
return null;
}
- key属性自定义key
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。
@Cacheable(value="users", key="#id")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public User find(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public User find(User user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public User find(User user) {
return null;
}
Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 | 描述 | 示例 |
---|---|---|
methodName | 当前方法名 | #root.methodName |
method | 当前方法 | #root.method. name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的class | #root.targetClass |
args | 当前方法参数组成的数组 | #root.args[0] |
caches | 当前被调用的方法使用的Cache | #root.caches[0].name |
当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。
@Cacheable(value={"users", "xxx"}, key="caches[1].name")
public User find(User user) {
return null;
}
- condition属性指定发生的条件
condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。
@Cacheable(value = "stu", key = "#id", condition="#user.id%2==0")
Student findById(Integer id);
2.2 @CacheEvict
@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
2.2.1 属性
value、key和condition的语义与@Cacheable对应的属性类似,下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。
- allEntries属性
allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
- beforeInvocation属性
清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {
System.out.println("delete user by id: " + id);
}
2.3 @CachePut
@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。@CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。
@CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {
return null;
}
2.4 使用
- springboot启动类上添加@EnableCaching注解开启缓存
- 添加缓存注解
@CacheConfig(cacheNames = "stu")
public interface UserService {
@CachePut(key = "#student.id")
Student save(Student student);
@CacheEvict(key = "#id")
void delete(Integer id);
@Cacheable(key = "#id")
Student findById(Integer id);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserRepository userRepository;
@Override
public Student save(Student student) {
System.out.println("save");
return userRepository.save(student);
}
@Override
public void delete(Integer id) {
System.out.println("delete");
userRepository.deleteById(id);
}
@Override
public Student findById(Integer id) {
System.out.println("findById");
return userRepository.findById(id).get();
}
}
- 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoTest {
@Resource
UserService userService;
@Test
public void findById(){
Student student = userService.findById(1);
System.out.println(student);
Student student1 = userService.findById(1);
System.out.println(student1);
}
}
输出
findById
Student{id=1, name='张三', sex='男'}
Student{id=1, name='张三', sex='男'}
“findById”只输出一次,说明第一次调用findById方法时执行了方法内容,并将返回值存入缓存。
第二次调用findById时并没有执行方法内容,而是直接从缓存中读取数据。