【Spring从成神到升仙系列 五】从根上剖析 Spring 循环依赖

news2024/10/7 12:22:01
  • 👏作者简介:大家好,我是爱敲代码的小黄,独角兽企业的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) 缓存

不要着急,我们继续往下讲

假如我们现在 MyDemo1AOP 动态代理,如果我们再按照上面的方式去进行缓存,会造成什么结果?

我们 MyDemo2 中的成员变量 MyDemo1 是未经动态代理的,这样使用 MyDemo1 时,实际上也是非动态代理的对象,这样是不被允许的!

为什么会有上面的问题呢?
在这里插入图片描述
根本原因在于:我们的属性注入的阶段在我们的执行初始方法(AOP)之前,缓存池中的半实例化对象不是我们代理对象

那怎么解决这个问题呢——没错,还是加缓存

我们再加一层缓存,该缓存的作用:如果我们半实例化的对象是代理对象,那么我们得到其代理对象

在这里插入图片描述

如上所示,整体的业务如上,我们详细的聊一聊 Spring 源码对于循环依赖的处理

四、循环依赖 Spring 源码剖析

我们以属性注入的例子来进行源码解析:

在我们讲解之前,我介绍一下三级缓存各自的功能:

  • 一级缓存(singletonObject):存储的是所有创建好了的单例Bean
  • 二级缓存(earlySingletonObjects):完成实例化,但是还未进行属性注入及初始化的对象
  • 三级缓存(singletonFactories):提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

这三个缓存非常重要,必须要记住。

当我们使用 ApplicationContext context = new GenericXmlApplicationContext("application.xml"); 启动时,会进行我们 Bean 的创建

这里只说最关键的步骤,整体的步骤可见:Spring IOC 源码剖析

整体流程如下:
在这里插入图片描述

步骤一:查询 MyDemo1 是否存在

此时的缓存:
在这里插入图片描述

我们直接跳到这里:AbstractBeanFactory246

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // Step1:查询MyDemo1缓存是否存在
    Object sharedInstance = getSingleton(beanName);
    
    // 如果是单例的bean
    if (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 半实例化放至缓存中

我们直接跳到 AbstractAutowireCapableBeanFactory580

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 的缓存是否存在

我们直接跳到这里:AbstractBeanFactory246

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // Step4:查询MyDemo2缓存是否存在
    Object sharedInstance = getSingleton(beanName);
    
    // 如果是单例的bean
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            return createBean(beanName, mbd, args);
        });
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
}

步骤五:将 MyDemo2 半实例化放至缓存中

我们直接跳到 AbstractAutowireCapableBeanFactory580

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

我们直接跳到这里:AbstractBeanFactory246

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){
    // Step6:从缓存中获取 MyDemo1 
    Object sharedInstance = getSingleton(beanName);
    
    // 如果是单例的bean
    if (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都要熟悉IOCAOP的源码,甚至手写 AOP 的实现

但通过这篇文章,我相信,99% 的人应该都可以理解了 Spring 循环依赖 的实现

那么如何证明你真的理解了 Spring 循环依赖 呢,我这里出个经典的题目,大家可以想一下:为什么Spring要用三级缓存,二级不可以嘛?

如果你能看到这,那博主必须要给你一个大大的鼓励,谢谢你的支持!

喜欢的可以点个关注,Spring 系列到此正式结束了~

  • 【Spring从成神到升仙系列 一】2023年再不会动态代理,就要被淘汰了
  • 【Spring从成神到升仙系列 二】2023年再不会 IOC 源码,就要被淘汰了
  • 【Spring从成神到升仙系列 三】2023年再不会 AOP 源码,就要被淘汰了
  • 【Spring从成神到升仙系列 四】从源码分析 Spring 事务的来龙去脉

后续博主应该会更新 dubbo 或者 并发编程 的系列文章,

我是爱敲代码的小黄,独角兽企业的Java开发工程师,CSDN博客专家,Java领域新星创作者,喜欢后端架构和中间件源码。

我们下期再见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/441836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot+Vue家乡特色推荐系统

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

【李老师云计算】HBase+Zookeeper部署及Maven访问(HBase集群实验)

索引 前言1. Zookeeper1.1 主机下载Zookeeper安装包1.2 主机解压Zookeeper1.3 ★解决解压后文件缺失1.4 主机配置Zookeeper文件1.4.1 配置zoo_sample.cfg文件1.4.2 配置/data/myid文件 1.5 主机传输Zookeeper文件到从机1.6 从机修改Zookeeper文件1.6.1 修改zoo.cfg文件1.6.2 修…

一文带你了解MySQL的前世今生,架构,组成部分,特点,适用场景

文章目录 一、MySQL的由来二、MySQL的架构2.1 客户端2.2 服务器 三、 MySQL的主要组成部分3.1 连接管理器3.2 查询缓存3.3 解析器3.4 查询优化器3.5 执行器3.6 存储引擎 四、MySQL的特点五、MySQL的应用场景六、总结 一、MySQL的由来 MySQL最初是由瑞典公司MySQL AB的Michael …

4年功能测试,我一进阶python接口自动化测试,跳槽拿了20k......

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 很多人在这求职市…

让ChatGPT告诉你Java的发展前景

Java版电商购物系统项目实战 最近很多人问我Java的发展前景怎么样&#xff1f;该怎么学Java基础&#xff1f;java这么卷还该不该学等等。那今天老王以电商场景为例&#xff0c;再结合ChatGPT的回答和大家聊的一下Java有哪些应用前景和技术层面的落地方案。&#xff08;在收获干…

【Spring】-- 02 -- Spring中Bean的配置、作用域

一、Bean的配置 Spring用于生产和管理Spring容器中的Bean&#xff0c;需要开发者对Spring的配置文件进行配置。在实际开发中&#xff0c;最常采用XML格式的配置方式&#xff0c;即通过XML文件来注册并管理Bean之间的依赖关系。 在Spring中&#xff0c;XML配置文件的根元素是…

iOS问题记录 - Xcode 14.3版本运行项目报错

文章目录 前言开发环境问题描述问题分析解决方案最后 前言 看到Xcode有新版本&#xff0c;没忍住点了升级&#xff0c;然后问题来了。 开发环境 macOS 13.3Xcode: 14.3 问题描述 Xcode 14.2版本运行项目一切正常&#xff0c;升级到14.3版本后运行报错。 运行到模拟器的报…

【PWN刷题__ret2text】——CTFHub之 简单的 ret2text

萌新第一阶段自然是了解做题的套路、流程&#xff0c;简单题要多做滴 目录 前言 一、checksec查看 二、IDA反汇编 三、exp编写 前言 经典的ret2text流程 一、checksec查看 64位程序&#xff0c;什么保护都没有&#xff0c;No canary found——可以栈溢出控制返回 二、IDA反汇…

“MySQL5.6”、“索引优化”,其实都是索引下推

如果你在面试中&#xff0c;听到“MySQL5.6”、“索引优化” 之类的词语&#xff0c;你就要立马get到&#xff0c;这个问的是“索引下推”。 什么是索引下推 索引下推(Index Condition Pushdown&#xff0c;简称ICP)&#xff0c;是MySQL5.6版本的新特性&#xff0c;它能减少回…

学习实践-Alpaca-Lora (羊驼-Lora)(部署+运行+微调-训练自己的数据集)

Alpaca-Lora模型GitHub代码地址 1、Alpaca-Lora内容简单介绍 三月中旬&#xff0c;斯坦福发布的 Alpaca &#xff08;指令跟随语言模型&#xff09;火了。其被认为是 ChatGPT 轻量级的开源版本&#xff0c;其训练数据集来源于text-davinci-003&#xff0c;并由 Meta 的 LLaMA …

OpenAI对实现强人工智能AGI的规划:《Planing for AGI and beyond》

OpenAI对实现AGI的长期和短期的计划&#xff1a;《Planing for AGI and beyond》 返回论文和资料目录 原文地址 1.导读 OpenAI最近这些年发布了很多令人印象深刻的模型&#xff0c;毫无疑问&#xff0c;OpenAI已经走在了人工智能领域的最前沿。但是很多人只注意到这些模型&…

Nacos Docker Kubernetes ⽣态

博主介绍&#xff1a;✌全网粉丝4W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面…

概率密度函数的非参数估计方法

概率密度函数的非参数估计方法 1. Parzen窗方法2. kn近邻估计 \qquad 直接由样本来估计概率密度 p ( x ) p(\boldsymbol{x}) p(x) 的方法&#xff0c;称为非参数方法 (non-parametric method) \text{(non-parametric method)} (non-parametric method)。 \quad ● \quad 概率…

数学建模第三天:数学建模算法篇之线性规划及matlab的实现

目录 一、前言 二、线性规划简介 1、线性规划模型介绍与特征 2、线性规划模型的一般形式 三、单纯形法 1、标准化 2、单纯形法解题 四、matlab解决问题1、matlab线性规划函数 2、解题代码 一、前言 数学建模&#xff0c;本意就是用来解决生活中的问题&#xff0c;我们今…

二叉树的前中后序遍历写法归纳

如题&#xff0c;对应力扣题目如下&#xff1a; 144.二叉树的前序遍历145.二叉树的后序遍历94.二叉树的中序遍历 1.递归 1.1 先序遍历 根 -> 左 -> 右 所以,这个递归函数先打印根节点的值,然后递归地遍历左子树,最后递归地遍历右子树。如果传入的根节点是空,则直接返回…

Linux学习记录—— 이십일 进程间通信(3)信号量和消息队列

文章目录 1、消息队列2、信号量1、了解概念2、信号量理解 3、接口4、理解IPC 1、消息队列 两个进程ab之间系统维护一个队列结构&#xff0c;a进程往队列里放信息&#xff0c;信息编号为1&#xff0c;b进程往队列里放信息&#xff0c;信息编号为2&#xff1b;之后开始读取数据的…

HADOOP伪分布式安装步骤

HADOOP安装步骤 一.创建Hadoop用户 二.更新apt和安装vim编辑器 更新apt: sudo apt-get install update安装VIM编辑器&#xff1a; apt install vim在弹出的提示中输入yes(y) 三、安装SSH和配置SSH无密码登录 apt install openssh-serverssh登录&#xff1a; ssh localh…

Vue2组件通信专题

组件通信专题 一、vue2中常用的6中组件通信方式 1. props 适用于的场景&#xff1a;父子组件通信 注意事项&#xff1a; 如果父组件给子组件传递数据&#xff08;函数&#xff09;&#xff1a;本质其实是子组件给父组件传递数据。 如果父组件给子组件传递数据&#xff08…

【致敬未来的攻城狮计划】— 连续打卡第七天:(电脑重装系统)学习RA产品家族选型手册

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

大数据hadoop课程实验总结

1一.安装hadoop 本门课程使用的是centos7.2 64位操作系统&#xff0c;原生hadoop2.7.7,java1.7版本。 安装centos7.2系统&#xff1a; 创建系统的同时创建一个名为hadoop的账户。这一步不难&#xff0c;此处就不再详说。 没有hadoop用户可以创建一个Hadoop用户&#xff1a; …