深度理解springboot集成cache缓存之源码解析
一、案例准备
1.创建数据表(employee表)

2.创建Employee实体类封装数据库中的数据
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //1.男 2.女
private Integer dId;
}
3.编写EmployeeMapper接口(DAO测通)
@Mapper
public interface EmployeeMapper {
@Select("select * from employee where id=#{id}")
Employee getEmpById(Integer id);
}
4.编写EmployeeService接口及其EmployeeServiceImpl实现类
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeMapper employeeMapper;
@Override
@Cacheable(cacheNames = "emp",key = "#id",condition = "#id>0")
public Employee getEmp(Integer id) {
System.out.println("正在查询id为"+id+"号的员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
}
5.编写EmployeeController类
@RestController
public class EmpController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/emp/{id}")
public Employee getEmployee(@PathVariable("id") Integer id){
Employee emp = employeeService.getEmp(id);
return emp;
}
}
6.启动访问http://localhost:8080/emp/1

成功!
-----------------------------------------------分割线-------------------------------------------------------------
二、工作原理分析
1.查看springboot启动时,导入了哪些缓存组件
通过以往springboot相关的自动配置类可知与缓存相关的自动配置类为CacheAutoConfiguration

@Import:向容器中导入一些组件(通常导入的选择器以ImportSelector结尾)
ctrl+右键查看CacheConfigurationImportSelector源码
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
}
打上断点,debug模式下运行

放行,查看return imports的结果


导入的组件如下
* "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
* "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
2.是哪个缓存配置类生效呢?根据当前的场景进行分析
有两种方法可以得出哪个缓存配置类生效
第一种:源码分析(该方法只做简单说明)
随便打开一个缓存配置类,例如第一个GenericCacheConfiguration,查看源码如下
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class GenericCacheConfiguration {
@Bean
SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Collection<Cache> caches) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(caches);
return customizers.customize(cacheManager);
}
}
根据类上的
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
注解进行判断该类是否生效
这些都是@Conditional注解的衍生注解,该注解是Spring4新推出的注解,判断是否满足某种条件,如果满足则给容器注册bean
第二种:查看自动配置报告
在application.properties配置文件中添加
debug=true
运行发现这几个组件中只有SimpleCacheConfiguration生效了

3.分析核心类的作用
进去查看SimpleCacheConfiguration的源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
@Bean
ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers) {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManagerCustomizers.customize(cacheManager);
}
}
可以看出给容器中注册了一个ConcurrentMapCacheManager缓存管理器
查看源码分析创建Cache的具体细节

这一段代码是创建Cache的核心
#首先通过该类中的cacheMap属性获取缓存,参数为缓存名字(key-value)
#然后进行判断,如果cache为null,则上锁;再次获取如果为null,则根据本类中的createConcurrentMapCache方法创建Cache,然后将其放到缓存中

protected Cache createConcurrentMapCache(String name) {
SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;
return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);
}
}
可以看出返回时创建了ConcurrentMapCache对象,进去查看源码

lookup方法的作用是从缓存中获取数据
put方法的作用是是保存数据到缓存中
自此我们可以猜一下,ConcurrentMapCache该类的作用就是对缓存中的数据进行操作,如果缓存中没有数据,则从数据库查询,一并放到缓存中
总的来说ConcurrentMapCacheManager类的作用就是,先判断是否有某缓存,如果没有就创建该缓存,ConcurrentMapCache类从数据库中进行查询,一并将数据存储到ConcurrentMap集合中
4.运行流程

根据上面打上的四个断点,debug模式下启动

发现程序刚开始并没有走EmployeeServiceImpl的断点,而是走到了这个getCache方法,寻找name=emp的缓存

因为没有名为emp的缓存,所以会创建名为emp的缓存,继续放行

使用key去缓存中查找内容(key默认是方法的参数,浏览器访问的是1号员工信息,id=1即key=1),size=0,缓存中的数据为空

继续放行

直接调用业务方法去数据库中查询数据了,这是为什么呢,因为上个步骤在缓存中没有查询到数据,所以需要向数据库中要数据;继续放行

上一步从数据库中查询出数据之后,将数据传回前端页面展示并使用put方法将其放入到缓存中
此时缓存中已经有数据了,当我再次debug运行 就不会再从数据库中查询数据了
该博客仅为了记录自己的学习过程,理清技术点思路
深度理解springboot集成cache缓存之源码解析的更多相关文章
- Springboot集成RabbitMQ之MessageConvert源码解析
问题 最近在使用RabbitMq时遇到了一个问题,明明是转换成json发送到mq中的数据,消费者接收到的却是一串数字也就是byte数组,但是使用mq可视化页面查看数据却是正常的,之前在使用过程中从未遇 ...
- 详解SpringBoot集成jsp(附源码)+遇到的坑
本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置applica ...
- SpringBoot集成jsp(附源码)+遇到的坑
1.大体步骤 (1) 创建Maven web project: (2) 在pom.xml文件添加依赖: (3) 配置application.properties支持 ...
- SpringBoot(1.5.6.RELEASE)源码解析
转自 https://www.cnblogs.com/dylan-java/p/7450914.html 启动SpringBoot,需要在入口函数所在的类上添加@SpringBootApplicati ...
- springboot ---> spring ioc 注册流程 源码解析 this.prepareContext 部分
现在都是在springboot 中 集成 spirng,那我们就从springboot 开始. 一:springboot 启动main 函数 public static void main(Strin ...
- springboot自动扫描添加的BeanDefinition源码解析
1. springboot启动过程中,首先会收集需要加载的bean的定义,作为BeanDefinition对象,添加到BeanFactory中去. 由于BeanFactory中只有getBean之类获 ...
- Springboot集成cache的key生成策略
代码接上文:深度理解springboot集成redis缓存之源码解析 ## 1.使用SpEL表达式 @Cacheable(cacheNames = "emp",key = &quo ...
- [源码解析] PyTorch 流水线并行实现 (6)--并行计算
[源码解析] PyTorch 流水线并行实现 (6)--并行计算 目录 [源码解析] PyTorch 流水线并行实现 (6)--并行计算 0x00 摘要 0x01 总体架构 1.1 使用 1.2 前向 ...
- [源码解析] PyTorch 分布式(1)------历史和概述
[源码解析] PyTorch 分布式(1)------历史和概述 目录 [源码解析] PyTorch 分布式(1)------历史和概述 0x00 摘要 0x01 PyTorch分布式的历史 1.1 ...
随机推荐
- AC+AP组网无线WiFi网速超慢延迟卡顿问题解决
AP是什么? AP是Access Point的简称,即无线接入点,其作用是把局域网里通过双绞线传输的有线信号(即电信号)经过编译,转换成无线电信号传递给电脑.手机等无线终端,与此同时,又把这些无线终端 ...
- 03 Java的数据类型分为两大类 类型转换 八大基本类型
数据类型 强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用 Java的数据类型分为两大类 基本类型(primitive type) 数值类型 整数类型 byte占1个字节范围: ...
- 软件工程homework-002
博客信息 沈阳航空航天大学计算机学院2020软件工程作业 作业要求 https://edu.cnblogs.com/campus/sau/Computer1701-1705/homework/1058 ...
- 手把手教你写一个SpringMVC框架
一.介绍 在日常的 web 开发中,熟悉 java 的同学一定知道,Spring MVC 可以说是目前最流行的框架,之所以如此的流行,原因很简单:编程简洁.上手简单! 我记得刚开始入行的时候,最先接触 ...
- 面试突击32:为什么创建线程池一定要用ThreadPoolExecutor?
在 Java 语言中,并发编程都是依靠线程池完成的,而线程池的创建方式又有很多,但从大的分类来说,线程池的创建总共分为两大类:手动方式使用 ThreadPoolExecutor 创建线程池和使用 Ex ...
- Linux下查看端口占用进程号,程序名的方法
Linux下查看端口占用进程号,程序名的方法,方便我们查找什么进程导致系统变慢等需要.linux下查看端口占用情况: 1. 查看哪个进程占用了819端口: case9-sghfofo:/usr/loc ...
- pthread_once函数
http://blog.csdn.net/lmh12506/article/details/8452659 pthread_once()函数详解 在多线程环境中,有些事仅需要执行一次.通常当初始化应用 ...
- loj#6072 苹果树(折半搜索,矩阵树定理,容斥)
loj#6072 苹果树(折半搜索,矩阵树定理,容斥) loj 题解时间 $ n \le 40 $ . 无比精确的数字. 很明显只要一个方案不超过 $ limits $ ,之后的计算就跟选哪个没关系了 ...
- 深度学习训练过程中的学习率衰减策略及pytorch实现
学习率是深度学习中的一个重要超参数,选择合适的学习率能够帮助模型更好地收敛. 本文主要介绍深度学习训练过程中的6种学习率衰减策略以及相应的Pytorch实现. 1. StepLR 按固定的训练epoc ...
- 两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
对. 因为equals()方法可以用开发者重写,hashCode()方法也可以由开发者来重写,因此它们是否相等并没有必然的关系. 如果对象要保存在HashSet或HashMap中,它们的equals( ...