Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager 接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;本文我们就来介绍下SpringCache的具体使用。
一、缓存中的重要概念
@Cacheable/@CachePut/@CacheEvict 主要的参数
二、SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档
注意:
当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如
@Cacheable(key = “targetClass + methodName +#p0”)
使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value=“users”, key="#id")
@Cacheable(value=“users”, key="#p0")
三、SpringCache的使用
1.导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
2.然后在启动类注解@EnableCaching开启缓存
3.创建业务类
package com.dpb.springboot.service; import com.dpb.springboot.pojo.User; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; /** * @program: springboot-13-cache * @description: * @author: 波波烤鸭 * @create: 2019-11-27 21:25 */ @Service public class UserService { /** * @Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存 * 此处的User实体类一定要实现序列化public class User implements Serializable,否则会报java.io.NotSerializableException异常。 * @param userName * @return */ @Cacheable(value = "userCache" , key = "#userName") public User getUserByName(String userName){ System.out.println("数据库查询...." + userName); return getFromDB(userName); } /** * 清除一条记录 * @param user */ @CacheEvict(value = "userCache",key = "#user.name") public void updateUser(User user){ System.out.println("数据更新了。。。。数据库"); updateDB(user); } /** * allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 * beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true, * 则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 */ @CacheEvict(value = "userCache",allEntries = true,beforeInvocation = true) public void reload(){ // } private User getFromDB(String userName){ System.out.println("查询数据库..." + userName); return new User(666,userName); } private void updateDB(User user){ System.out.println("更新数据..." + user.getName()); } }
4.创建缓存实现类
我们自定义一个基于内存的缓存实现 Cache接口,并实现相关的方法
package com.dpb.springboot.cache; import org.springframework.cache.Cache; import org.springframework.cache.support.SimpleValueWrapper; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; /** * @program: springboot-13-cache * @description: * @author: 波波烤鸭 * @create: 2019-11-27 21:32 */ public class MyCache implements Cache { // 缓存的 key private String name; // 保存缓存数据的容器 private Map<String,Object> store = new ConcurrentHashMap<>(); public MyCache() { } public MyCache(String name) { this.name = name; } @Override public String getName() { return this.name; } public void setName(String name) { this.name = name; } /** * 返回数据容器 * @return */ @Override public Object getNativeCache() { return store; } /** * 返回缓存数据 * @param key * @return */ @Override public ValueWrapper get(Object key) { ValueWrapper result = null; Object thevalue = store.get(key); if(thevalue != null){ result = new SimpleValueWrapper(thevalue); System.out.println("执行了缓存查询...命中" + key); }else{ System.out.println("执行了缓存查询...没有命中" + key); } return result; } /** * 返回缓存数据 基于泛型 * @param key * @param aClass * @param <T> * @return */ @Override public <T> T get(Object key, Class<T> aClass) { return aClass.cast(store.get(key)); } @Override public <T> T get(Object o, Callable<T> callable) { return null; } /** * 保存缓存数据 * @param o * @param o1 */ @Override public void put(Object o, Object o1) { // System.out.println("数据缓存了..." + o); store.put((String)o,o1); } /** * 清除一条缓存数据 * @param key */ @Override public void evict(Object key) { System.out.println("移走了元素:" + key); store.remove(key); } /** * 清空所有的数据 */ @Override public void clear() { store.clear(); } }
5.配置缓存管理器
Spring的配置文件中如下配置
6.测试代码
package com.dpb.springboot; import com.dpb.springboot.pojo.User; import com.dpb.springboot.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot13CacheApplicationTests { @Autowired private UserService service ; @Test void contextLoads() { System.out.println("第一次查询..."); service.getUserByName("hello"); System.out.println("第二次查询..."); service.getUserByName("hello"); System.out.println("*************"); // 更新记录 User user1 = service.getUserByName("user1"); // 开始更新其中一个 user1.setId(1111); service.updateUser(user1); // 更新后再查询 service.getUserByName("user1"); // 再次查询 service.getUserByName("user1"); // 更新所有 service.reload(); System.out.println("清空了所有的缓存..."); service.getUserByName("user1"); service.getUserByName("user2"); service.getUserByName("user1"); service.getUserByName("user2"); } }
测试结果
第一次查询... 执行了缓存查询...没有命中hello 数据库查询....hello 查询数据库...hello 数据缓存了...hello 第二次查询... 执行了缓存查询...命中hello ************* 执行了缓存查询...没有命中user1 数据库查询....user1 查询数据库...user1 数据缓存了...user1 数据更新了。。。。数据库 更新数据...user1 移走了元素:user1 执行了缓存查询...没有命中user1 数据库查询....user1 查询数据库...user1 数据缓存了...user1 执行了缓存查询...命中user1 清空了所有的缓存... 执行了缓存查询...没有命中user1 数据库查询....user1 查询数据库...user1 数据缓存了...user1 执行了缓存查询...没有命中user2 数据库查询....user2 查询数据库...user2 数据缓存了...user2 执行了缓存查询...命中user1 执行了缓存查询...命中user2
搞定~