JSR简介源码剖析
总结
JSR简介
JSR 全称为 Java Specification Requests
Spring生态从3.1开始支持使用Java Caching(JSR-107)注解来简化开发
介绍Spring提供的概念和注解
简单使用
启动类开启缓存支持
//开启缓存支持
@EnableCaching
public class SpringCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCacheApplication.class, args);
}
}
service 标注@Cacheable
/*
* @Cacheable: 缓存查询:会将被该注解标注的方法的返回值存到缓存中
* value/cacheNames: 指定缓存的名称,cacheManager 是管理多个 cache ,以名称进行区分
* key: 缓存数据时指定的key值(key,value),默认是该方法的参数值,也可以使用spEL来计算key的值
* keyGenerator: key的生成策略,和 key. 进行二选一;自定义keyGenerator
* cacheMenager: 指定缓存管理器 redis:employee ehcache:employee
* cacheResolver: 功能和cacheMenager相同,二选一即可
* condition: 条件判断:满足这个条件后才会进行缓存
* unless: 否定条件:满足这个条件后,不进行缓存
* sync: 是否使用异步模式进行缓存 true
* 1.condition/unless 同时满足时,不进行缓存
* 2.sync值为true时,unless不被支持
*/
@Cacheable(value = {"employee"}, key = "#id",condition = "#id > 0",unless = "#result == null")
@Override
public Employee findEmployeeById(Integer id) {
return employeeMapper.selectByPrimaryKey(id);
}
其他注解使用
/*
* 更新缓存注解
* value -> 指定需要更新的缓存名称
* key -> 指定需要更新的缓存key
* ★★★ 切记此处一定将实体进行返回,否侧再次根据该id调用查询时会返回为null,或者再次根据该id执行更新时会报错
*/
@CachePut(value = {"emp"}, key = "#employee.id")
public Employee updateEmployee(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
return employee;
}
/*
* 删除缓存注解
* value -> 指定需要删除的缓存名称
* key -> 指定需要删除的缓存key
* allEntries -> 是否清楚指定缓存中的所有键值对,默认为false ,与key 二选一
* beforeInvocation -> 在被标注的方法前执行还是后执行 默认为false -> 即在方法执行后执行,但是如果在delete后出现异常,
* 缓存清除将不起作用.可将值修改为true -> 即方法执行前先清除缓存
*/
@CacheEvict(value = {"emp"}, key = "#id")
public void deleteEmployee(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
@Service
/*
* 被@CacheConfig 标注后该类中所有方法的缓存名称将统一为 emp
* 方法上的 cacheNames 和 value 同样效果
* 但是在 @CacheConfig 中只能使用 cacheNames
*/
@CacheConfig(cacheNames = {"emp"})
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Override
@Cacheable(key = "#id", condition = "#id > 0", unless = "#result == null")
public Employee findEmployeeById(Integer id) {
return employeeMapper.selectByPrimaryKey(id);
}
@Override
/*
* 更新缓存注解
* value -> 指定需要更新的缓存名称
* key -> 指定需要更新的缓存key
* ★★★ 切记此处一定将实体进行返回,否侧再次根据该id调用查询时会返回为null,或者再次根据该id执行更新时会报错
*/
@CachePut(key = "#employee.id")
public Employee updateEmployee(Employee employee) {
employeeMapper.updateByPrimaryKeySelective(employee);
return employee;
}
@Override
/*
* 删除缓存注解
* value -> 指定需要删除的缓存名称
* key -> 指定需要删除的缓存key
* allEntries -> 是否清楚指定缓存中的所有键值对,默认为false ,与key 二选一
* beforeInvocation -> 在被标注的方法前执行还是后执行 默认为false -> 即在方法执行后执行,但是如果在delete后出现异常,
* 缓存清除将不起作用.可将值修改为true -> 即方法执行前先清除缓存
*/
@CacheEvict(key = "#id")
public void deleteEmployee(Integer id) {
employeeMapper.deleteByPrimaryKey(id);
}
}
源码分析
直接从启动项的 @SpringBootApplication 注解进入,在图中打下断点.
//进入流程
@SpringBootApplication -> @EnableAutoConfiguration -> AutoConfigurationImportSelector.class
双击shift并进入
进入该类中
所有的缓存类型
返回的是所有的缓存组件类及加载顺序
当项目没有额外导入缓存的相关jar时,默认使用的是 SimpleCacheConfiguration
可以随便打开一个缓存组件类,可以看出条件判断注解必须有RedisConnectionFactory.class才能加载Redis缓存组件类
只有SimpleCacheConfiguration才能创建
此时返回的是一个ConcurrentMapCacheManager,进入该类在getCache()处打下断点,debug放行,此时断点不卡住,项目启动完成,接下来发起请求.
首次执行cache为空
加锁并进行二次判断,继续为空,创建ConcurrentMapCache并put到map中,供下次调用是使用
此时进入 Cache 接口的实现类中 -> ConcurrentHashMap
在lookup()出打断点,通过表达式可以看出此时为null
断点继续放在put()上并继续执行,此时到DB中进行了首次查询,并将结果put()到当前缓存中
此时,首次查询结束,清空控制台并再次发起请求
此时可以看到通过’employee’的值取到了缓存,继续执行
再次执行lookup()方法,此时通过key获取到了缓存值,程序放行,直接返回
执行完毕,控制台没有任何sql输出
关于SimpleKey的生成策略,可以查看 SimpleKeyGenerator 类
总结
1.启动时默认使用 SimpleCacheConfiguration 组件类,并创建 ConcurrentMapCacheManager 对象存到容器中
2.底层数据结构为 ConcurrentHashMap 以key,value的形式存放缓存数据
3.整体执行流程:
1)通过指定的名字到CacheManager中获取相应的缓存,首次没有会进行相应的创建
2)去cache中查找缓存,无参数请求时默认的key为new SimpleKey(),单个参数时为该参数值,多个参数时对应调用SimpleKey相应的有参构造
3)没有查到缓存调用目标方法继续执行,从DB中查询数据
4)将数据put到当前缓存中
end