1、JSR107(技术复杂性较大)
Java Caching定义了5个核心接口:Entry、Expiry、Cache、CachingProvider和CacheManager
(1)CacheProvider:定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以再运行期访问多个CacheProvider。
(2)CacheManager:定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CacheProvider所拥有。
(3)Cache:是一个类似Map的数据结构并临时存储以key为索引的值。一个Cache仅被一个CacheManager所拥有。
(4)Entry:是一个存储在Cache中的key-value对。
(5)Expiry:没一个存储在Cache中的条目有一个定义的有效期。一旦超过这个期限,条目为过期状态,将不可在进行访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
2、Spring缓存抽象
(1)使用Spring缓存抽象时我们需要关注以下两点:
A、确定方法需要被缓存以及它们的缓存策略
B、从缓存中读取之前缓存存储的数据
(2)几个重要的概念&缓存注解
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存(查询 ) |
@CacheEvict | 清空缓存(删除) |
@CachePut | 保证方法被调用,又希望结果被缓存(更新) |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
Serialize | 缓存数据时value序列化策略 |
(2)SpringBoot整合Cache
A、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
B、开启基于注解的缓存@EnableCaching
@MapperScan("com.chen.cache.mapper")
@SpringBootApplication
@EnableCaching
public class Springboot01CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01CacheApplication.class, args);
}
}
C、标注缓存注解即可
a、@Cacheable
/***
* 将方法的运行结果进行缓存;以后在查询相同的数据,直接从缓存中获取,不用调用方法
*
* CacheManager管理多个Cache组件,对缓存的真正的CURD操作在Cache组件中,且每一个缓存组件都有自己的名字;
* @Cacheable 的几个属性:
* CacheNames/value:指定缓存组件的名字
* key:缓存数据使用的key;默认使用方法参数值(id)-方法的返回值(Employee)
* keyGenerator:key的生成器;(key和keyGenerator不能同时指定)
* cacheManager:指定缓存管理器、cacheResolver:指定缓存解析器(两者二选一)
* condition:符合指定条件下才缓存;例:condition="#id>0"
* unless:否定缓存;为true不缓存
* sync:是否使用异步模式
* ☆3、@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存。
* 若没查到,则运行方法并将结果放入缓存。以后再来调用可以直接使用缓存中的数据
* @param id
* @return
*/
@Cacheable(cacheNames = "emp")
public Employee getEmpById(Integer id){
System.out.println("查询"+id+"号员工!");
return employeeMapper.getEmployeeById(id);
}
注: 其中@Cacheable的属性值的设定可以使用SpEL表达式:
b、@CachePut
/***
* @CachePut:既调用方法,又更新缓存数据
* 修改数据库某个数据,同时更新缓存
*
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
* 测试步骤:
* 1、查询1号员工,结果放到缓存中
* key=1; value=employee对象
* 2、更新1号员工
* 将方法的返回值也放进缓存
* 3、如果需要再次查询1号员工不需要进数据库直接获得更新后的数据,那么更新员工的时候需要指定跟查询员工时一样的key
* key="#employee.id" 使用传入参数的员工id
* key="#result.id" 使用返回值的id
*/
@CachePut(value = "emp",key = "#result.id")
public Employee updateEmp(Employee employee){
System.out.println("updateEmp:"+employee);
employeeMapper.updateEmp(employee);
return employee;
}
c、@CacheEvict
/***
* @CacheEvict:清除缓存
* key指定要删除哪个Cache
* allEntries = true:指定清除这个缓存中所有数据
* beforeInvocation:默认为false表示缓存的清除是否在方法之后执行,如果出现异常则不会清除
*/
@CacheEvict(value = "emp",key = "#id")
public int delteEmp(Integer id){
System.out.println("deleteEmp:"+id);
//employeeMapper.deleteEmp(id);
return id;
}
d、@Caching: 定义复杂的缓存规则
@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){
System.out.println("查询名为:"+lastName+"的员工!");
return employeeMapper.getEmployeeByLastName(lastName);
}
注:当通过lastName查询之后利用@Cacheable将结果添加到缓存中,另外还利用@CachePut在缓存中添加key为id和email的Cache,所以当用id或者email查询时会直接从缓存中取,但是通过lastName查询还是会查询数据库,因为@CachePut是先执行方法致使查询lastName时需要查询数据库。
d、@CacheConfig: 抽取缓存的公共配置
@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
...
...
...
@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){
System.out.println("查询名为:"+lastName+"的员工!");
return employeeMapper.getEmployeeByLastName(lastName);
}
}
原理:
1、自动配置类:CacheAutoConfiguration
2、缓存的配置类默认生效:SimpleCacheConfiguration->给容器中注册了一个ConcurrentMapCacheManager->可以获取和创建ConcurrentMapCache类型的缓存组件->将数据保存在ConcurrentMap<Object,Object>中;
运行流程:
1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取(ConcurrentManager先获取相应的缓存),第一次获取缓存如果没有获取到就会自动创建
2、去Cache中使用key(默认为传入的参数)查找缓存的内容
key是按照策略生成的;使用keyGenerator生成key,默认使用SimpleKeyGenerator生成key
SimpleKeyGenerator生成key的默认策略:
* 如果没有参数:key = new SimpleKey();
* 如果有一个参数:key = 参数值
* 如果与多个参数:key = new SimpleKey(params)
3、没有查到缓存就会调用目标方法去数据库获取,并将目标方法返回的结果放进缓存中
总结:
1、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
2、key使用keyGenerator生成,默认是SimpleKeyGenerator