Java缓存接口标准JSR-107:Java Caching定义了5个核心接口,分别是CachingProvider(缓存提供者), CacheManager(缓存管理器), Cache(缓存组件), Entry (key-value对)和Expiry(条目有效期),他们的关系如下图所示。但是该缓存规范,整合难度较大。Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;
文档:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-caching
1、接口、缓存注解、key/value生成策略
Cache | 缓存接口,定义缓存操作。实现:RedisCache、EhCache等(根据缓存技术不同来实现Cache接口) |
CacheManager | 缓存接口,缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 可缓存的,标注方法,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 更新缓存 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
2、使用cache缓存步骤(不使用缓存时,每次访问都会连接数据库)
a、引入依赖
org.springframework.boot
spring-boot-starter-cache
b、开启基于注解的缓存 @EnableCaching
c、标注缓存注解@Cacheable:标注方法可缓存;@CacheEvict:缓存清除;@CachePut:缓存更新等;支持spel表达式,如下图;参考官方文档:https://docs.spring.io/spring/docs/5.1.0.BUILD-SNAPSHOT/spring-framework-reference/integration.html#cache
package com.example.cache.service;
import com.example.cache.bean.Employee;
import com.example.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
//@Service:将service加载到容器中
//CacheConfig:公共的缓存配置;cacheNames = "emp":之后的value="emp"就可以不用写
@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
/**
* @Cacheable:将方法的运行结果进行保存,以后再要相同的数据,直接从缓存中获取,不用调用方法
* CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每个缓存组件有自己唯一的名字
* 属性:(缓存数据时使用key-value的形式)
* cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存
* key:缓存数据使用的key;可以用她来指定。默认是使用方法参数的值 键值对:1-方法的返回值
* 参数id的值:#id相当于#root.args[0]
* keyGenerator:key的生成器;可以自己指定key的生成器组件id
* ——key和keyGenerator不可同时使用
* cacheManager:指定缓存管理器(从哪个缓存管理器中取);cacheResolver:指定缓存解析器
* condition:指定符合条件的情况下才缓存;condition="#id>0":id大于0的时候才缓存
* unless:否定缓存;当unless指定的true,方法的返回值就不会被缓存;可以获取到结果进行判断
* unless="#result == null":结果为空时不缓存
* sync:缓存是否使用异步模式
* @param id
* @return
*/
@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
/**
* @CachePut:既调用方法,又更新缓存数据;
* 修改了数据库的某个数据,同时更新缓存
* 运行过程:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
* 更新后重新查询出的数据是更新前的数据:
* ——(key默认使用方法参数的值)查询是的缓存是@Cacheable缓存的值,key是1;@CachePut更新后缓存的值:key是传入的employee对象
* ——所以要统一key:key="#employee.id";key="#result.id";
*/
@CachePut(value = "emp",key="#employee.id")
public Employee updateEmp(Employee employee){
employeeMapper.updateEmp(employee);
return employee;
}
/**
*@CacheEvict缓存清除
* key:指定要清楚的数据
* allEntries=true:指定清除这个缓存中的所有数据
* beforeInvocation = false;缓存的清除是否在方法之前执行;false:在方法执行之后清除
*/
@CacheEvict(value="emp",key="#id")
public void deleteEmp(Integer id){
}
/**
* 指定多个缓存规则
*/
@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);
}
}
3、整合redis进行缓存;开发中常使用缓存中间件:redis、memcahed、ehcache;
引入redis启动器
org.springframework.boot
spring-boot-starter-data-redis
配置redis信息
spring.redis.host=127.0.0.1
spring.redis.password=
使用redis
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCacheApplicationTests implements Serializable {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
StringRedisTemplate stringRedisTemplate;//key-value都是操作字符串
@Autowired
RedisTemplate redisTemplate;//key-value都是对象
//@Autowired
//RedisTemplate
自定义序列化器,改变默认的序列化规则;使其保存的数据为json格式,而不是序列化格式。
package com.example.cache.config;
import com.example.cache.bean.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.List;
@Configuration
public class MyRedisConfig {
//RedisTemplate
原理:默认使用缓存管理器CacheManager来创建Cache缓存组件,来给缓存中存取数据。加入redis后,开启了RedisCacheConfiguration。RedisCacheManager帮我们创建RedisCache来作为缓存组件。
4、缓存运行流程
@Cacheable:
a、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;(CacheManager先获取相应的缓存),如果没有Cache组件,第一次获取缓存会自动创建
b、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
key是按照某种策略生成的;默认是使用keyGenerator生成的;默认使用SimpleKeyGenerator生成key
SimpleKeyGenerator生成key的默认策略:
如果没有参数:key=new SimpleKey();如果有一个参数,key=参数的值;如果有多个参数:key= new SimpleKey(params);
c、没有查到缓存就调用目标方法;
d、将目标方法返回的结果放到缓存中
@Cacheable标注的方法执行之前来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,如果没有就运行方法,并将结果放入缓存。以后再来调用,就可以使用缓存中的数据;
5、缓存原理
a、自动配置类CacheAutoConfiguration给容器中导入组件
b、各缓存的配置类
c、默认生效的配置类:SimpleCacheConfiguration
d、给容器中注册了一个CacheManager:ConcurrentManager;可以获取和创建ConcurrentMapCache类型的缓存组件,他的作用是将数据保存在ConcurrentMap中。