5.1声道测试文件下载
5.1声道测试音频文件 aac解码 quicktime/mov容器
文件:590m.com/f/25127180-480829423-42a8bf
(访问密码:551685)
以下内容无关:
-------------------------------------------分割线---------------------------------------------
在研究 『 Spring 是如何解决循环依赖的 』 的时候,了解到 Spring 是借助三级缓存来解决循环依赖的。
同样在上一节留下了疑问:
循环依赖为什么要使用三级缓存?而不是使用二级缓存?
AOP 动态代理对循环依赖的有没有什么影响?
本篇文章也是围绕上面的内容进行展开。
笔记也在不断整理,之前可能会有点杂乱。
循序渐进,看一看什么是循环依赖?#
开始先简单回顾一下 Bean 的创建过程,当然小伙伴也可以直接阅读『 单例 Bean 的创建 』这篇文章。
不过考虑到阅读本文前再阅读上一篇文章、Debug 等等,会比较耗时,所以本篇文章前面一小部分会先对之前的文章内容做简要概括,也相当于对我自己学习的知识进行一个总结。
先来回顾一下三级缓存的概念。
singletonObjects: 一级缓存,存储单例对象,Bean 已经实例化,初始化完成。
earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。
singletonFactories: 三级缓存,存储 singletonFactory。
Bean 的创建过程#
@Service
public class CircularServiceA {
private String fieldA = “字段 A”;
}
单例 Bean 的创建过程
通过上面的流程,可以看出 Spring 在创建 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤:
实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的 fieldA 字段在这里就会赋值。
属性注入 populateBean: 可以理解为对 Bean 里面的属性进行赋值。(会依赖其他 Bean)
初始化 initializeBean: 执行初始化和 Bean 的后置处理器。
实例化赋值源码可以阅读:
BeanUtils.instantiateClass(constructorToUse)
如果要依赖其他 Bean 呢?#
那如果 CircularServiceA 依赖了其他 Bean 呢?
@Service
public class CircularServiceA {
private String fieldA = "字段 A";@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
}
A 依赖了 B
当 A 依赖了 B 的时候,在 createBeanInstance 这一步,并不会对 B 进行属性赋值。
而是在 populatedBean 这里查找依赖项,并创建 B。
循环依赖下的创建过程#
循环依赖的场景,在上一篇文章已经有所讲解,这里仅仅画图说明一下。
@Service
public class CircularServiceA {
private String fieldA = "字段 A";@Autowired
private CircularServiceB circularServiceB;
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;
}
A B 循环依赖
在 A 和 B 循环依赖的场景中:
B populatedBean 查找依赖项 A 的时候,从一级缓存中虽然未获取到 A,但是发现 A 在创建中。
此时,从三级缓存中获取 A 的 singletonFactory 调用工厂方法,创建 getEarlyBeanReference A 的早期引用并返回。
B 引用到 A ,B 就可以初始化完毕,然后 A 同样也可以初始化完毕了。
二级缓存能否解决循环依赖#
通过上面的图,仔细分析一下,其实把二级缓存拿掉,在 B 尝试获取 A 的时候直接返回 A 的实例,是不是也是可以的?
答案是:可以的!
但是为什么还是用三级缓存呢?
网上的很多资料说是和动态代理有关系,那就从动态代理的方面继续往下分析分析。
动态代理的场景#
在 JavaConfig(配置类) 上添加 @EnableAspectJAutoProxy 注解,开启 AOP ,通过 Debug 循序渐进看一看动态代理对循环依赖的影响。
动态代理下,Bean 的创建过程#
@Service
public class CircularServiceA {
private String fieldA = “字段 A”;
public void methodA() {System.out.println("方法 A 执行");}
}
@Aspect
@Component
public class AspectA {
@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {System.out.println("beforeA 执行");
}
}
只有 A 的情况下,给 A 添加切面,开始 Debug。
前面的流程都相同,在 initializeBean 开始出现差异。
这一步需要初始化 Bean 并执行 Bean 的后置处理器。
执行后置处理器
其中有一个处理器为: AnnotationAwareAspectJAutoProxyCreator 其实就是加的注解切面,会跳转到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 方法
postProcessAfterInitialization
如图所示:wrapIfNecessary 方法会判断是否满足代理条件,是的话返回一个代理对象,否则返回当前 Bean。
后续调用 getProxy 、createAopProxy 等等,最终执行到下面一部分。
最终会执行到这里,AOP 代理相关的就不细看了。
一路放行,直到 initializeBean 执行结束。
A 被替换为了代理对象
此时发现:A 被替换为了代理对象。
所以 doCreateBean 返回,以及后面放到一级缓存中的都是代理对象。
红框部分为差异
有循环依赖的动态代理#
这一次把循环依赖打开:
@Service
public class CircularServiceA {
private String fieldA = "字段 A";@Autowired
private CircularServiceB circularServiceB;public void methodA() {System.out.println("方法 A 执行");}
}
@Aspect
@Component
public class AspectA {
@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")
public void beforeA() {System.out.println("beforeA 执行");}
}
@Service
public class CircularServiceB {
@Autowired
private CircularServiceA circularServiceA;public void methodB() {}
}
@Aspect
@Component
public class AspectB {
@Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")
public void beforeB() {System.out.println("beforeB 执行");}
}
开始 Debug,前面的一些列流程,都和正常的没有什么区别。而唯一的区别在于,创建 B 的时候,需要从三级缓存获取 A。
此时在 getSingleton 方法中会调用:singletonObject = singletonFactory.getObject();
B 属性赋值时,从三级缓存获取 A
有时会比较疑惑 singletonFactory.getObject() 调用的是哪里?
三级缓存获取对象
所以这一块调用的是 getEarlyBeanReference,开始遍历执行 BeanPostProcessor。
getEarlyBeanReference
getEarlyBeanReference
看到 wrapIfNecessary 就明白了吧!这块会获取一个代理对象。
也就是说此时返回,并放到二级缓存的是一个 A 的代理对象。
这样 B 就创建完毕了!
到 A 开始初始化并执行后置处理器了!因为 A 也有代理,所以 A 也会执行到 postProcessAfterInitialization 这一部分!
判断二级缓存
但是在执行 wrapIfNecessary 之前,会先判断代理对象缓存是否有 A 了。
this.earlyProxyReferences.remove(cacheKey) != bean
但是这块获取到的是 A 的代理对象。肯定是 false 。 所以不会再生成一次 A 的代理对象。
代理 - 循环依赖
总结#
可以看到,循环依赖下,有没有代理情况下的区别就在:
singletonObject = singletonFactory.getObject();
在循环依赖发生的情况下 B 中的 A 赋值时:
无代理:getObject 直接返回原来的 Bean
有代理:getObject 返回的是代理对象
然后都放到二级缓存。
为什么要三级缓存?#
假设去掉三级缓存
去掉三级缓存之后,Bean 直接创建 earlySingletonObjects, 看着好像也可以。
如果有代理的时候,在 earlySingletonObjects 直接放代理对象就行了。
但是会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创建代理对象。
这么一想,是不是会对 Bean 的生命周期有影响。
同样,先创建 singletonFactory 的好处就是:在真正需要实例化的时候,再使用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是延迟实例化。
假设去掉二级缓存
如果去掉了二级缓存,则需要直接在 singletonFactory.getObject() 阶段初始化完毕,并放到一级缓存中。
B 和 C 都依赖 A
那有这么一种场景,B 和 C 都依赖了 A。
要知道在有代理的情况下 singletonFactory.getObject() 获取的是代理对象。
多次获取代理对象不同
而多次调用 singletonFactory.getObject() 返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。
那如果获取 B 到之后直接放到一级缓存,然后 C 再获取呢?
5.1声道测试文件下载相关推荐
- 使用Apifox解决测试文件下载出错问题
使用Apifox解决测试文件下载出错问题 1.出现问题 是因为数据格式不对 2.解决问题 点击发送并下载即可解决
- 视频测试文件下载地址
视频测试文件下载地址:Index of / 这是mplayer官方提供的各种视频格式的测试文件.
- 左右声道测试_小说:少年参加测试,直接挑战十只狗恐兽,众人见了惊呼:SS级...
穿过那个奇怪的走廊,韩意将那个队长给他的卡交给了走廊尽头转角边上的与刚才那几个人穿着一样,十六七岁左右,一脸浓妆,身材那是该挺的挺,该翘的翘,在加上那一抹媚笑,让整个看起来有些不伦不类的女子. 原本对 ...
- 声道测试音频_功率放大器测试方法
功率放大器测试方法 原创: wdele 电声在线 1月15日 这是我数年前拟定的,不过仍有参考价值. 一.功率放大器有关测量的条件的规定 1.1 额定条件 满足下列放大条件时,就认为放大器在额定条件下 ...
- 如何使用postman 测试文件下载和批量请求功能
一 文件下载 postman 测试工具很多时候只用于简单的get或post请求,普通的请求只要加入适当的参数点击send就可以正常请求并得到相应的响应结果. 当涉及到文件下载时,需要把点击send改为 ...
- 左右声道测试音频_关于制作左右声道音频的剪辑软件推荐
一款优秀的视频剪辑软件,不仅有高水平的视频制作功能,它的音频编辑功能也是必不可少的.Vegas就是这么一款软件,同时具备视频制作特效制作的同时,还能帮助制作轨道音频效果. 下面,就让小编带大家去学习, ...
- 分布式文件系统研究-测试-文件下载测试
文件下载 package com.learn.test.fastdfs;import org.csource.common.MyException; import org.csource.fastdf ...
- jmeter进行文件下载测试
Jmeter文件下载测试 文件下载服务器使用python -m http.server搭建的下载服务器,参考: https://blog.csdn.net/a200822146085/article/ ...
- c语言加密算法头文件下载(base64、md5、sha1)
1.base64 网上有一份开头就是 // Encoding lookup table char base64encode_lut[] = { 的base64.h, 在loadrunner中加密有b ...
- 银联在线支付---利用测试案例代码模拟支付应用(修改)
一.工程搭建 新建一个Web工程,命名为PayOnLine,把你下载好的案例代码拷贝到你的工程下,我的代码目录如下: acp_sdk.properties配置文件需要放在类根路劲下,里面的参数配置信息 ...
最新文章
- Swift 换肤方案的实现使用SwiftTheme框架
- SAP B/P 初步研究(二)
- unity3d中获得物体的size
- 使用 HTMLTestRunner.py
- Android 默认Tab标签大小及间距修改
- 【Linux系统编程学习】C库IO函数与系统IO函数的关系
- 在职场,没有永远的朋友,也没有永远的敌人,因为只有利益
- 为什么要处理自然语言? | NLP基础
- 谷歌浏览器不支持ocx控件
- html中正方形列表标签属性,CSS 列表样式
- 【modelsim和debussy】设置
- HR 面,我们该怎么办
- android电视 怎么调电视机的信号源,如何设置智能电视、盒子信号源?原来这么简单!具体方法如下...
- 快给你的对象做一个微信公众号播报吧-java版
- 【网络安全模型介绍】PDR、P2DR、PDR2、PDR2A、WPDRRC
- 网站变灰,首页变灰怎么实现?
- 恶搞版西游记,99%的人看完后当场含笑而死
- 一些常用工具下载链接
- Portfolio、program和project的联系和区别;它们与operation和OPM的关系
- 与君共勉---在中学部学习收获
热门文章
- 电力拖动自动控制系统复习(一)
- 【新书推荐】【2020】卫星通信(第三版)
- cl.g4r.win index.php,win7 wamp环境配置Oracle数据库连接
- 百度硬盘搜索 2.3 试用手记
- 把ICDAR2019-LSVT原始数据集转为REC任务识别数据
- 虚幻引擎4 DEMO打包下载
- 相亲交友App,婚恋交友App,开源源码可搭建H5公众号小程序App,支持个性化定制
- Linux:下载和安装
- Mac OS黑苹果系统安装工具及懒人版镜像文件 for macOS High Sierra/macOS Sierra
- Android 系统签名(.pk8、.pem) 制作成 storeFile