【Spring从成神到升仙系列 五】从根上剖析 Spring 循环依赖
- 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主
- 📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙、Spring从成神到升仙系列
- 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
- 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人
- 📝联系方式:hls1793929520,加我进群,大家一起学习,一起进步👀

文章目录
- Spring 循环依赖源码解析
- 一、引言
- 二、循环依赖场景
- 1、有参构造引起的循环依赖
- 2、属性注入引起的循环依赖
- 三、循环依赖的原因
- 1、有参构造失败的原因
- 2、属性注入成功的原因
- 2.1 AOP导致的循环依赖
- 四、循环依赖 Spring 源码剖析
- 步骤一:查询 MyDemo1 是否存在
- 步骤二:将 MyDemo1 半实例化放至缓存中
- 步骤三、四:查询 MyDemo2 的缓存是否存在
- 步骤五:将 MyDemo2 半实例化放至缓存中
- 步骤六:从缓存中获取 MyDemo1
- 步骤七:将 MyDemo2 生成的实例化放至 singletonObject 中
- 步骤八:将 MyDemo1 生成的实例化放至 singletonObject 中
- 五、总结
Spring 循环依赖源码解析
一、引言
对于Java开发者而言,关于 Spring ,我们一般当做黑盒来进行使用,不需要去打开这个黑盒。
但随着目前程序员行业的发展,我们有必要打开这个黑盒,去探索其中的奥妙。
本期 Spring 源码解析系列文章,将带你领略 Spring 源码的奥秘
本期源码文章吸收了之前 Kafka 源码文章的错误,将不再一行一行的带大家分析源码,我们将一些不重要的部分当做黑盒处理,以便我们更快、更有效的阅读源码。
废话不多说,发车!

本文流程图可关注公众号:爱敲代码的小黄,回复:循环依赖 获取
贴心的小黄为大家准备的文件格式为 POS文件,方便大家直接导入 ProcessOn 修改使用
二、循环依赖场景
我们上几篇文章讲解了 IOC、AOP的源码实现,如果没有看过的同学可以去看一下:
- Spring IOC 源码剖析
- Spring AOP 源码剖析
如果上面的文章你已经熟悉了,那么对于循环依赖的理解就会变得很简单,甚至你自己都能够想明白整个运行原理
我们首先介绍一下循环依赖的场景
我们在委托 Spring 进行对象的创建时,会遇到下面的情况:
1、有参构造引起的循环依赖
MyDemo1:
public class MyDemo1 {public MyDemo2 myDemo2;public MyDemo1(MyDemo2 myDemo2) {this.myDemo2 = myDemo2;}
}
MyDemo2:
public class MyDemo2 {public MyDemo1 myDemo1;public MyDemo2(MyDemo1 myDemo1) {this.myDemo1 = myDemo1;}
}
xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="myDemo1" class="cn.hls.demo1.MyDemo1"><constructor-arg value="myDemo2"/></bean><bean id="myDemo2" class="cn.hls.demo1.MyDemo2"><constructor-arg value="myDemo1"/></bean></beans>
测试用例:
public class TestMain {public static void main(String[] args) {ApplicationContext context = new GenericXmlApplicationContext("application.xml");MyDemo1 myDemo1 = (MyDemo1) context.getBean("myDemo1");myDemo1.show();}
}
运行,不出所料,我们会报错:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'myDemo1': Requested bean is currently in creation: Is there an unresolvable circular reference?
2、属性注入引起的循环依赖
MyDemo1:
public class MyDemo1 {public MyDemo2 myDemo2;public void show() {System.out.println("我是" + MyDemo1.class.getName());}public void setMyDemo2(MyDemo2 myDemo2) {this.myDemo2 = myDemo2;}public MyDemo2 getMyDemo2() {return myDemo2;}
}
MyDemo2:
public class MyDemo2 {public MyDemo1 myDemo1;public void show() {System.out.println("我是" + MyDemo2.class.getName());}public MyDemo1 getMyDemo1() {return myDemo1;}public void setMyDemo1(MyDemo1 myDemo1) {this.myDemo1 = myDemo1;}
}
xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="myDemo1" class="cn.hls.demo1.MyDemo1"><property name="myDemo2" ref="myDemo2"/></bean><bean id="myDemo2" class="cn.hls.demo1.MyDemo2"><property name="myDemo1" ref="myDemo1"/></bean></beans>
测试用例:
public class TestMain {public static void main(String[] args) {ApplicationContext context = new GenericXmlApplicationContext("application.xml");MyDemo1 myDemo1 = (MyDemo1) context.getBean("myDemo1");MyDemo2 myDemo2 = (MyDemo2) context.getBean("myDemo2");myDemo1.show();myDemo2.show();}
}
运行,我们竟然发现,这种是可以正常执行的
我是cn.hls.demo1.MyDemo1
我是cn.hls.demo1.MyDemo2
到这里,有没有一点点惊讶、一点点懵逼、一点点卧槽
如果有的话,那这篇文章将带你解析为什么两种方式不同的注入方式
一种可能正常运行,一种不能正常运行
三、循环依赖的原因
这里我们搬出 IOC 源码中的流程图:

我们分别聊一下有参构造场景下和有参注入场景下的不同
1、有参构造失败的原因
我们通过上图看到,如果一个类需要通过有参构造创建实例化,那么需要得到其构造方法的入参:

整体情况如上所示,我们总是重复性的循环,MyDemo1 的实例化创建依赖 MyDemo2,而 MyDemo2 的实例化创建又需要依赖 MyDemo1,这样就导致了死循环并无法解决。
所以,当我们的 Spring 察觉到有参构造导致的循环依赖时,会进行报错,这种的循环依赖也是没有办法解决的。
2、属性注入成功的原因

大家看这张图,可能会疑惑,这不也造成了循环依赖嘛,怎么这种方式没报错
我们想想这种属性注入导致的循环依赖能不能靠其他的方式去解决,加缓存可不可以

我们来看这种解决方式:
- 我们
MyDemo1调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中 MyDemo1调用属性注入时,会去缓存池中寻找MyDemo2的实例,若找不到的话,则调用CreateBean方法创建MyDemo2的实例MyDemo2调用无参构造生成实例(不是完全的实例)时,将其放至我们的缓存池中MyDemo2调用属性注入时,会去缓存池中寻找MyDemo1的实例,找到之后之前,执行后续的方法生成对应的实例化- 这个时候我们的
MyDemo1已经得到了MyDemo2的实例化数据了,直接执行初始方法创建实例即可
通过上述这种方式,我们已经将 属性注入 的循环依赖问题用加一层缓存的方式解决掉了
而这个缓存也被我们称作 提前暴露(earlySingletonObjects) 的缓存
2.1 AOP导致的循环依赖
我们上面可以看到,我们用一层 提前暴露(earlySingletonObjects) 的缓存解决了属性注入导致的循环依赖问题
这时候你可能会说:小黄,小黄,不是三级缓存嘛,你这咋就讲了一个 提前暴露(earlySingletonObjects) 缓存
不要着急,我们继续往下讲
假如我们现在 MyDemo1 被 AOP 动态代理,如果我们再按照上面的方式去进行缓存,会造成什么结果?
我们 MyDemo2 中的成员变量 MyDemo1 是未经动态代理的,这样使用 MyDemo1 时,实际上也是非动态代理的对象,这样是不被允许的!
为什么会有上面的问题呢?

根本原因在于:我们的属性注入的阶段在我们的执行初始方法(AOP)之前,缓存池中的半实例化对象不是我们代理对象
那怎么解决这个问题呢——没错,还是加缓存
我们再加一层缓存,该缓存的作用:如果我们半实例化的对象是代理对象,那么我们得到其代理对象

如上所示,整体的业务如上,我们详细的聊一聊 Spring 源码对于循环依赖的处理
四、循环依赖 Spring 源码剖析
我们以属性注入的例子来进行源码解析:
在我们讲解之前,我介绍一下三级缓存各自的功能:
- 一级缓存(singletonObject):存储的是所有创建好了的单例Bean
- 二级缓存(earlySingletonObjects):完成实例化,但是还未进行属性注入及初始化的对象
- 三级缓存(singletonFactories):提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
这三个缓存非常重要,必须要记住。
当我们使用 ApplicationContext context = new GenericXmlApplicationContext("application.xml"); 启动时,会进行我们 Bean 的创建
这里只说最关键的步骤,整体的步骤可见:Spring IOC 源码剖析
整体流程如下:

步骤一:查询 MyDemo1 是否存在
此时的缓存:

我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step1:查询MyDemo1缓存是否存在Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (mbd.isSingleton()) {// 直接创建bean即可,注意 getSingleton 方法sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}// Step1:从三级缓存中查询 MyDemo1 是否被缓存
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 一级缓存查询Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 二级缓存查询singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 三级缓存查询ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}// 这里记住一个操作:在我们创建bean结束之后,会调用 addSingleton 该方法
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}afterSingletonCreation(beanName);}if (newSingleton) {addSingleton(beanName, singletonObject);}return singletonObject;
}
步骤二:将 MyDemo1 半实例化放至缓存中
我们直接跳到 AbstractAutowireCapableBeanFactory 的 580 行
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){// 是否需要提前暴露boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));// 如果需要提前暴露,则放入到我们的三级缓存里面if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
}// 将未完全实例化的 MyDemo1 放至缓存中
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 三级缓存this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);// 这个主要是记录当前注册的对象(不太重要)this.registeredSingletons.add(beanName);}}
}// 这个是重点:生成动态代理对象的地方,我们后面会讲
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}
此时的缓存:

步骤三、四:查询 MyDemo2 的缓存是否存在
我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step4:查询MyDemo2缓存是否存在Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}
步骤五:将 MyDemo2 半实例化放至缓存中
我们直接跳到 AbstractAutowireCapableBeanFactory 的 580 行
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){// 是否需要提前暴露boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));// 如果需要提前暴露,则放入到我们的三级缓存里面if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}
}
此时的缓存:

步骤六:从缓存中获取 MyDemo1
我们直接跳到这里:AbstractBeanFactory 的 246 行
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){// Step6:从缓存中获取 MyDemo1 Object sharedInstance = getSingleton(beanName);// 如果是单例的beanif (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {return createBean(beanName, mbd, args);});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}
}
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) {// 这里获取的是 MyDemo1 的缓存,我们之前已经放入过Object sharedInstance = getSingleton(beanName);
}protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {// 【重点】从三级缓存中取到ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 调用 getEarlyBeanReference 的方法生成对象singletonObject = singletonFactory.getObject();// 将生成的半实例对象放至二级缓存中this.earlySingletonObjects.put(beanName, singletonObject);// 删除掉三级缓存的信息this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}
我们来看一下 getEarlyBeanReference 做了什么、
- 如果是普通的类,没有被动态代理的,直接返回
bean即可 - 如果是动态代理的类,需要进行动态代理类的生成并返回
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 【重点】exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}public Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean);// 这里会生成动态代理类【AOP文章讲过】return wrapIfNecessary(bean, beanName, cacheKey);
}
到这里,我们的缓存的状态如下:

步骤七:将 MyDemo2 生成的实例化放至 singletonObject 中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {if (newSingleton) {addSingleton(beanName, singletonObject);}return singletonObject;
}// 当bean初始化完成之后
// 删除二级缓存、三级缓存,将其放入一级缓存中
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}
此时各缓存情况:

步骤八:将 MyDemo1 生成的实例化放至 singletonObject 中
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {if (newSingleton) {addSingleton(beanName, singletonObject);}return singletonObject;
}// 当bean初始化完成之后
// 删除二级缓存、三级缓存,将其放入一级缓存中
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}
此时各缓存情况:

到这里,我们的循环依赖的整体流程就被解决了
五、总结
又是一篇大工程的文章结束了
记得校招时候,当时对 Spring 懵懂无知,转眼间也被迫看了源码
更可怕的是,现在面试竟然百分之80都要熟悉IOC、AOP的源码,甚至手写 AOP 的实现
但通过这篇文章,我相信,99% 的人应该都可以理解了 Spring 循环依赖 的实现
那么如何证明你真的理解了 Spring 循环依赖 呢,我这里出个经典的题目,大家可以想一下:为什么Spring要用三级缓存,二级不可以嘛?
如果你能看到这,那博主必须要给你一个大大的鼓励,谢谢你的支持!
喜欢的可以点个关注,Spring 系列到此正式结束了~
- 【Spring从成神到升仙系列 一】2023年再不会动态代理,就要被淘汰了
- 【Spring从成神到升仙系列 二】2023年再不会 IOC 源码,就要被淘汰了
- 【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了
- 【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉
后续博主应该会更新 dubbo 或者 并发编程 的系列文章,
我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,Java领域新星创作者,喜欢后端架构和中间件源码。
我们下期再见。
相关文章:
【Spring从成神到升仙系列 五】从根上剖析 Spring 循环依赖
👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,阿里云专家博主📕系列专栏:Java设计模式、数据结构和算法、Kafka从入门到成神、Kafka从成神到升仙…...
设计模式之代理模式(C++)
作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 一、代理模式是什么? 代理模式是一种结构型的软件设计模式,在不改变原代码前提下,提供一个代理…...
c++11 标准模板(STL)(std::unordered_multimap)(三)
定义于头文件 <unordered_map> template< class Key, class T, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator< std::pair<const Key, T> > > class unordered…...
Linux进程控制-2
紧接着上篇博客出发,我们接着来讲述Linux中进程控制的内容。 目录 1.等待 1.1具体操作 1.等待 进程等待主要的作用在于:父进程创建子进程之后,等待子进程退出,获取子进程的退出码,释放子进程的资源,避…...
快速排序算法
一:快速排序思想 假设我们现在对“6 1 2 7 9 3 4 5 10 8”这个10个数进行排序。首先在这个序列中随便找一个数作为基准数(不要被这个名词吓到了,就是一个用来参照的数,待会你就知道它用来做啥的了)。为了方便ÿ…...
中华好诗词大学季第二季(四)
第七期 1,二十四友一朝尽,爱妾坠楼何足言出自许浑的《金谷园》,“爱妾”指的是谁 2,李白在《九月十日即事》借菊花表达自己的惋惜之情,请问九月十日是什么节日 A 后登高 B 菊花节 C 小重阳 3,贾宝玉在大观园里面题了“曲径通幽”…...
分布式系统容灾部署方案
本文主要以OceanBase部署来说明分布式系统容灾部署方案 分布式系统提供持续可用的服务尤为重要。 好的分布式系统根据需求提供不同等级的的高可用与容灾级别。 而在分布式系统中,数据库系统又是最核心最关键的系统。 我们以数据库分布式系统为主,考虑…...
Python 爬虫性能相关总结
这里我们通过请求网页例子来一步步理解爬虫性能 当我们有一个列表存放了一些url需要我们获取相关数据,我们首先想到的是循环 简单的循环串行 这一种方法相对来说是最慢的,因为一个一个循环,耗时是最长的,是所有的时间总和 代码…...
Baumer工业相机堡盟工业相机如何设置网口的IP地址(工业相机连接的网口设置IP地址步骤)
Baumer工业相机堡盟工业相机如何设置网口的IP地址(工业相机连接的网口设置IP地址步骤)Baumer工业相机Baumer工业相机设置网络端口IP地址匹配设置网络端口IP地址和工业相机IP地址匹配第一次打开CameraExplorer软件确认问题为IP地址不匹配问题打开网络连接…...
Android MediaCodec设置H264 Profile到High
H264 High Profile压缩率高,能降低码率,这里记录下MediaCodec Profile设置到High遇到的一些问题。 Android 4.1 就引入了MediaCodecInfo.CodecProfileLevel类,下面截取H264(AVC)的Profile和Level定义: /** Copyright (C) 2012 The Android O…...
QT之QSysInfo(查看电脑信息)
文章目录前言一、API使用总结前言 QSysInfo是Qt中用于获取有关运行应用程序的系统信息的类。 我们可以获取以下信息: 返回系统产品类型,如ios,windows,Linux等 返回当前系统的产品版本。 返回当前系统的内核类型。 返回当前系统的…...
中国塑料编织袋产业竞争状况及投资前景预测报告2023-2029年
中国塑料编织袋产业竞争状况及投资前景预测报告2023-2029年 KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK 《报告编号》: BG451639 《出版时间》: 2023年4月 《出版机构》: 中智正业研究院 免费售后 服务一年,具体内容及订购流程欢迎咨询客服人员 内容简介&…...
从头用脚分析FFmpeg源码 - av_read_frame
av_read_frame作用 /*** Return the next frame of a stream.* This function returns what is stored in the file, and does not validate* that what is there are valid frames for the decoder. It will split what is* stored in the file into frames and return one f…...
第17章_触发器
第17章_触发器 🏠个人主页:shark-Gao 🧑个人简介:大家好,我是shark-Gao,一个想要与大家共同进步的男人😉😉 🎉目前状况:23届毕业生,目前在某公…...
3956. 截断数组
3956. 截断数组 - AcWing题库 3956. 截断数组 【题目描述】 给定一个长度为 nn 的数组 a1,a2,…,ana1,a2,…,an。 现在,要将该数组从中间截断,得到三个非空子数组。 要求,三个子数组内各元素之和都相等。 请问,共有多少种不同…...
React Labs: 我们最近在做什么——2023 年 3 月
原文:https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023 React Server Components React Server Components(下文简称 RSC) 是由 React 团队设计的新应用程序架构。 我们首先在一个介绍性演讲和一个RFC中分享了我们对 RSC 的…...
文件系统设计详解
抽象的文件系统以目录的形式来组织文件,我们可以利用该文件系统来读取某个文件的内容,也可以对目录或者文件实施监控并及时获取变化的通知。 IChangeToken IChangeToken对象就是一个与某组监控数据相关联的“令牌”(Token)&#x…...
好看~立马启动python实现美女通通下
人生苦短,我用python一、环境版本使用二、代码实现思路三、代码展示:导入模块伪装(请求头)四、部分好看截图,更多的就自己去采集噜~吃饭放松的时候哇一不小心看见了很多好看的东西 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 独乐乐不如众乐乐…...
Git 安装设置
1、安装 安装以下三个软件: Git-2.13.3-64-bit.exe TortoiseGit-2.4.0.2-64bit.msi TortoiseGit-LanguagePack-2.4.0.0-64bit-zh_CN.msi 安装过程中不用填写、不用选择,全部点"下一步",完成后需要重启机器。 2、基本设…...
Python-闭包
介绍 Python的闭包是一种高级的编程技巧,它可以在函数内部定义另一个函数,并返回该函数的引用。这个内部函数可以访问外部函数的变量和参数,即使外部函数已经执行完毕 好处 1)闭包可以避免全局变量的污染,使得代码更…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...
RabbitMQ 各类交换机
为什么要用交换机? 交换机用来路由消息。如果直发队列,这个消息就被处理消失了,那别的队列也需要这个消息怎么办?那就要用到交换机 交换机类型 1,fanout:广播 特点 广播所有消息:将消息…...
