一、案例准备

1.创建数据表(employee表)

2.创建Employee实体类封装数据库中的数据

  1. @AllArgsConstructor
  2. @NoArgsConstructor
  3. @Data
  4. @ToString
  5. public class Employee {
  6. private Integer id;
  7. private String lastName;
  8. private String email;
  9. private Integer gender; //1.男 2.女
  10. private Integer dId;
  11. }

3.编写EmployeeMapper接口(DAO测通)

  1. @Mapper
  2. public interface EmployeeMapper {
  3. @Select("select * from employee where id=#{id}")
  4. Employee getEmpById(Integer id);
  5. }

4.编写EmployeeService接口及其EmployeeServiceImpl实现类

  1. @Service
  2. public class EmployeeServiceImpl implements EmployeeService {
  3. @Autowired
  4. private EmployeeMapper employeeMapper;
  5. @Override
  6. @Cacheable(cacheNames = "emp",key = "#id",condition = "#id>0")
  7. public Employee getEmp(Integer id) {
  8. System.out.println("正在查询id为"+id+"号的员工");
  9. Employee emp = employeeMapper.getEmpById(id);
  10. return emp;
  11. }
  12. }

5.编写EmployeeController类

  1. @RestController
  2. public class EmpController {
  3. @Autowired
  4. private EmployeeService employeeService;
  5. @GetMapping("/emp/{id}")
  6. public Employee getEmployee(@PathVariable("id") Integer id){
  7. Employee emp = employeeService.getEmp(id);
  8. return emp;
  9. }
  10. }

6.启动访问http://localhost:8080/emp/1

成功!


-----------------------------------------------分割线-------------------------------------------------------------


二、工作原理分析

1.查看springboot启动时,导入了哪些缓存组件

通过以往springboot相关的自动配置类可知与缓存相关的自动配置类为CacheAutoConfiguration

@Import:向容器中导入一些组件(通常导入的选择器以ImportSelector结尾)
ctrl+右键查看CacheConfigurationImportSelector源码

  1. static class CacheConfigurationImportSelector implements ImportSelector {
  2. @Override
  3. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  4. CacheType[] types = CacheType.values();
  5. String[] imports = new String[types.length];
  6. for (int i = 0; i < types.length; i++) {
  7. imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
  8. }
  9. return imports;
  10. }
  11. }
  12. }

打上断点,debug模式下运行

放行,查看return imports的结果


导入的组件如下

  1. * "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
  2. * "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
  3. * "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
  4. * "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
  5. * "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
  6. * "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
  7. * "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
  8. * "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
  9. * "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
  10. * "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"

2.是哪个缓存配置类生效呢?根据当前的场景进行分析

有两种方法可以得出哪个缓存配置类生效

第一种:源码分析(该方法只做简单说明)
随便打开一个缓存配置类,例如第一个GenericCacheConfiguration,查看源码如下

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnBean(Cache.class)
  3. @ConditionalOnMissingBean(CacheManager.class)
  4. @Conditional(CacheCondition.class)
  5. class GenericCacheConfiguration {
  6. @Bean
  7. SimpleCacheManager cacheManager(CacheManagerCustomizers customizers, Collection<Cache> caches) {
  8. SimpleCacheManager cacheManager = new SimpleCacheManager();
  9. cacheManager.setCaches(caches);
  10. return customizers.customize(cacheManager);
  11. }
  12. }

根据类上的
@ConditionalOnBean(Cache.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
注解进行判断该类是否生效
这些都是@Conditional注解的衍生注解,该注解是Spring4新推出的注解,判断是否满足某种条件,如果满足则给容器注册bean

第二种:查看自动配置报告
在application.properties配置文件中添加

  1. debug=true

运行发现这几个组件中只有SimpleCacheConfiguration生效了

3.分析核心类的作用

进去查看SimpleCacheConfiguration的源码

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnMissingBean(CacheManager.class)
  3. @Conditional(CacheCondition.class)
  4. class SimpleCacheConfiguration {
  5. @Bean
  6. ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
  7. CacheManagerCustomizers cacheManagerCustomizers) {
  8. ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
  9. List<String> cacheNames = cacheProperties.getCacheNames();
  10. if (!cacheNames.isEmpty()) {
  11. cacheManager.setCacheNames(cacheNames);
  12. }
  13. return cacheManagerCustomizers.customize(cacheManager);
  14. }
  15. }

可以看出给容器中注册了一个ConcurrentMapCacheManager缓存管理器
查看源码分析创建Cache的具体细节

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

  1. protected Cache createConcurrentMapCache(String name) {
  2. SerializationDelegate actualSerialization = this.isStoreByValue() ? this.serialization : null;
  3. return new ConcurrentMapCache(name, new ConcurrentHashMap(256), this.isAllowNullValues(), actualSerialization);
  4. }
  5. }

可以看出返回时创建了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缓存之源码解析的更多相关文章

  1. Springboot集成RabbitMQ之MessageConvert源码解析

    问题 最近在使用RabbitMq时遇到了一个问题,明明是转换成json发送到mq中的数据,消费者接收到的却是一串数字也就是byte数组,但是使用mq可视化页面查看数据却是正常的,之前在使用过程中从未遇 ...

  2. 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置applica ...

  3. SpringBoot集成jsp(附源码)+遇到的坑

    1.大体步骤 (1)       创建Maven web project: (2)       在pom.xml文件添加依赖: (3)       配置application.properties支持 ...

  4. SpringBoot(1.5.6.RELEASE)源码解析

    转自 https://www.cnblogs.com/dylan-java/p/7450914.html 启动SpringBoot,需要在入口函数所在的类上添加@SpringBootApplicati ...

  5. springboot ---> spring ioc 注册流程 源码解析 this.prepareContext 部分

    现在都是在springboot 中 集成 spirng,那我们就从springboot 开始. 一:springboot 启动main 函数 public static void main(Strin ...

  6. springboot自动扫描添加的BeanDefinition源码解析

    1. springboot启动过程中,首先会收集需要加载的bean的定义,作为BeanDefinition对象,添加到BeanFactory中去. 由于BeanFactory中只有getBean之类获 ...

  7. Springboot集成cache的key生成策略

    代码接上文:深度理解springboot集成redis缓存之源码解析 ## 1.使用SpEL表达式 @Cacheable(cacheNames = "emp",key = &quo ...

  8. [源码解析] PyTorch 流水线并行实现 (6)--并行计算

    [源码解析] PyTorch 流水线并行实现 (6)--并行计算 目录 [源码解析] PyTorch 流水线并行实现 (6)--并行计算 0x00 摘要 0x01 总体架构 1.1 使用 1.2 前向 ...

  9. [源码解析] PyTorch 分布式(1)------历史和概述

    [源码解析] PyTorch 分布式(1)------历史和概述 目录 [源码解析] PyTorch 分布式(1)------历史和概述 0x00 摘要 0x01 PyTorch分布式的历史 1.1 ...

随机推荐

  1. LGP7840题解

    给出一种新的理解方式,本质上和正解是一致的. 首先我们现在已经有了一个森林,我们现在要给他加一条边,加哪一条边是最优的呢? 假设加的边是 \((u,v)\),那么 \(((d[u]+1)^2-d[u] ...

  2. 【面经】MySql常见问题

    1. 数据库三范式是什么? 1. 第一范式(1NF):字段具有原子性,不可再分.(所有关系型数据库系统都满足第一范式数据库表中的字段都是单一属性的,不可再分) 2. 第二范式(2NF)是在第一范式(1 ...

  3. Gin 09 HTTP 重定向

    gin http 重定向有两种方法,重写url 和 不重写的跳转.两种方法,gin 通过两个内置方法实现: demo package main import ( "github.com/gi ...

  4. Numpy库基础___四

    Numpy数据存取 •数据的csv文件的存取 只能有效存取和读取一维和二维数据 a = np.arange(100).reshape(5,20) #用delimiter分割,默认为空格 np.save ...

  5. Linux安全加固手册

    1      身份鉴别 1.1         密码安全策略 操作系统和数据库系统管理用户身份鉴别信息应具有不易被冒用的特点,口令应有复杂度要求并定期更换. 设置有效的密码策略,防止攻击者破解出密码 ...

  6. 项目构建工具之maven01

    Maven 是一个项目管理工具,可以对 Java 项目进行构建.依赖管理.Maven 也可被用于构建和管理各种项目,例如 C#,Ruby,Scala 和其他语言编写的项目.Maven 曾是 Jakar ...

  7. 简单了解一下pinia的结构

    随着 Vue3 的正式转正,Pinia 也渐渐火了起来.所以要更新一下自己的知识树了.这里主要是看看新的状态是什么"形态". 状态的容器还是"reactive" ...

  8. 论文翻译:2021_Joint Online Multichannel Acoustic Echo Cancellation, Speech Dereverberation and Source Separation

    论文地址:https://arxiv.53yu.com/abs/2104.04325 联合在线多通道声学回声消除.语音去混响和声源分离 摘要: 本文提出了一种联合声源分离算法,可同时减少声学回声.混响 ...

  9. Java9至17的新特性总结

    总览 讲讲Java 9-17 的一些语法糖和一些新发布的jeps, 重点讲讲JVM的垃圾回收器 时间线 SpringBoot 为什么选择Java17这个版本.我估计跟下面这个图有关系. Java 8 ...

  10. Kafka 消费者是否可以消费指定分区消息?

    Kafa consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥 ...