一、JSR107
Java Caching定义了5个核心接口:
- CachingProvider定义了创建、配置、获取、管理何控制多个CacheingManageer,一个应用可以在运行期访问多个CachingProvider。
- CachingManager定义了创建、配置、获取、管理何控制多个唯一命名的Cache,这写Cache存在于CacheManager的上下文中,一个CacheManager仅被一个CacheProvider所拥有。
- Cache是一个类似Map的数据结构并临时存储以key未索引的值,一个cache仅被一个CacheManager所拥有
- Entry是每一个存储在Cache的key-value对。
- Expiry每一个存储在Cache中的条目有一个定义的有效期,一旦超过这个时间,条目未过期的状态。一旦过期,条目姜不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy。
二、缓存注解
Cache | 缓存接口,定义缓存操作,实现由:RedisCache、EhCacheCahe、ConcurrentMapCache |
---|---|
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存注解时key生成策略 |
serialize | 缓存数据时value序列化策略 |
1、@Cacheable
- cacheNames/values:指定缓存组件的名字,将方法的返回结果放在那个缓存中,是数组的形式,可以指定多个缓存
- key: 缓存数据使用的key,可以用它来指定,默认是使用方法的参数值
例如:1-方法返回值 可以使用SpEL表达式 #id;参数id的值 #a0 #p0 #root.args[0]
- keyGenerator: key的生成器,可以自己指定key的生成器组件id key/keyGenerator:二选一使用
- cacheManager/cacheResolver:指定缓存管理器;或则使用cacheResolver指定获取解析器
- condition:指定符合条件的情况下才缓存
- unless:否定缓存,当unless指定的条件为true,方法的返回值就不会被缓存,可以获取到结果进行判断
- sync:是否使用异步模式
运行流程:
@Cacheable:
-
在方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取,(CacheManagerz先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建
-
去Cache中查找缓存的内容,使用一个key,默认就是方法的参数,key是按照某种策略生成的;默认是使用keyGenerator生成的,
-
没有查到缓存就调用目标方法
-
将目标方法返回的结果,放进缓存中
SpringEL表达式:
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.methodName |
method | root object | 当前被调用的方法 | #root.method.name |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache | #root.caches[0].name |
argument name | evaluation context | 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; | #iban 、 #a0 、 #p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache put’的表达式 ’cache evict’的表达式beforeInvocation=false) | #result |
@Cacheable(cacheNames = {"emp"}/*, keyGenerator = "myKeyGenerator"*/)
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
2、@CachePut
运行时机:
1、先调用目标方法
2、将目标方法的结果缓存起来
/**
* @Description: 既调用方法,又更新缓存
* 先调用方法,然后将结果进行缓存
* key = "#employee.id"
* key = "#result.id" 返回值的id
* 更新和查询用的key必须一致
*/
@CachePut(value = "emp",/*key = "#employee.id"*/key = "#result.id")
public Employee updateEmp(Employee employee){
employeeMapper.updateEmp(employee);
return employee;
}
3、@CacheEvict
缓存清除,可以指定key删除
//allEntries:默认为false,为true时,指定清楚这个缓存中所有的数据
//beforeInvocation:缓存的清除是否在方法之前执行
/**
* @Description:@CacheEvict清除缓存
*/
@CacheEvict(value = "emp",key = "#id")
public void deleteEmp(Integer id){
employeeMapper.deleteEmpById(id);
}
4.@Caching
包含上面三个注解,适合复杂规则使用
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
例如:
//复杂规则使用
@Caching(
cacheable = {
@Cacheable(value = "emp",key = "#lastName")
},
put = {
@CachePut(value = "emp",key = "#result.id"),
@CachePut(value = "emp",key = "#result.email")
}
)
public Employee getEmpByLastName(String lastName){
return employeeMapper.getEmpByLastName(lastName);
}
5、@CacheConfig
指定公共属性
@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
指定key组件生成器:
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName()+"["+ Arrays.asList(objects).toString()+"]";
}
};
}s
三、整合redis作为缓冲
pom.xml文件添加:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
- 引入redis的starter,容器中保存的是RedisCacheManager
- RedisCacheManager创建RedisCache来作为缓冲组件
- 默认保存数据k-v都是object,利用序列化保存
@SpringBootTest
class Springboot01CacheApplicationTests {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate redisTemplate;
@Autowired
RedisTemplate<Object,Employee> employeeRedisTemplate;
/*
*Redis常见五种数据类型: String、List、Set、Hash(散列)、ZSet(有序集合)
*stringRedisTemplate.opsForValue()操作String;
*stringRedisTemplate.opsForList()操作List;
* stringRedisTemplate.opsForSet()操作Set;
* stringRedisTemplate.opsForHash()操作Hash;
* stringRedisTemplate.opsForZSet()操作Zset;
*/
@Test
public void test01(){
// stringRedisTemplate.opsForValue().append("msg","hello");
stringRedisTemplate.opsForList().leftPush("myList","1");
stringRedisTemplate.opsForList().leftPush("myList","2");
String msg = stringRedisTemplate.opsForValue().get("msg");
System.out.println("msg = " + msg);
// stringRedisTemplate.opsForList();
// stringRedisTemplate.opsForSet();
// stringRedisTemplate.opsForHash();
// stringRedisTemplate.opsForZSet();
// System.out.println("=========");
}
@Test
public void test02(){
Employee emp = employeeMapper.getEmpById(1);
employeeRedisTemplate.opsForValue().set("emp-01",emp);
}
@Test
void contextLoads() {
Employee emp = employeeMapper.getEmpById(1);
}
}
自定义cachemanager序列化为json
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory){
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
RedisCacheManager build = RedisCacheManager.builder(factory).cacheDefaults(configuration).build();
return build;
}
获取缓存并添加缓存
@Autowired
CacheManager cacheManager;
@Cacheable(cacheNames = "dept")
public Department getDeptById(Integer id){
System.out.println("查询部门" + id);
Department dept = departmentMapper.getDeptByid(id);
Cache dept1 = cacheManager.getCache("dept");
dept1.put("dept1",dept);
return dept;
}