Spring的核心模块:Bean的生命周期(内含依赖循环+业务场景)。

news2024/11/27 14:38:47

Bean的生命周期

    • 前言
    • 为什么要学习Bean的生命周期
    • 前置知识
      • Spring Post-processor(后置处理器)
      • Aware接口
    • 简单介绍
    • Bean的实例化过程
      • 为什么会有bean的实例化?
      • 过程
    • Bean的初始化阶段
      • 为什么会有Bean的初始化?
      • Bean的初始化目的是什么?
    • 依赖注入
      • 基本概念
      • 依赖注入的几种方式
      • 重点问题——依赖循环
        • 那么,为什么会出现循环引用问题呢?
      • 如何解决Spring依赖循环问题
        • Spring自身的策略——提供三级缓存
      • 生命周期总结
    • 总结

前言

上篇文章我们介绍了Spring的一个核心模块BeanDifinition。
那么趁热打铁,我们再来复习一下Bean的整个生命周期是如何工作的。
Spring具有高度的灵活性和可扩展性,在Spring框架中,有一些对象是由Spring容器管理的,而这些对象的生命周期受Spring容器的管理。
我们先回进行一些前置知识的学习,帮助我们更好的理解。
您的关注是我最大的动力,我会不断打磨自己的笔记和知识点,希望可以一直给您带来帮助,关注交个朋友吧。

为什么要学习Bean的生命周期

在 Spring 框架中,Bean 是一个非常重要的概念,Bean 是 Spring 中的基本组件,它们由 Spring 容器创建、管理和维护,Bean 的生命周期是在容器中创建、初始化、使用和销毁的过程。
理解 Bean 的生命周期可以帮助开发人员更好地利用 Spring 框架,更好地管理和控制应用程序的组件。
具体而言呢,学习Bean的生命周期可以帮助我们理解:
1.理解 Spring 容器如何创建、管理和维护 Bean,从而可以更好地设计和实现应用程序中的组件。
2.理解 Bean 生命周期中各个阶段的意义,从而可以在 Bean 的生命周期的不同阶段进行一些必要的处理,比如在初始化阶段执行一些特定的操作。
3.学习如何自定义 Bean 生命周期中的各个阶段,从而可以控制 Bean 的创建和销毁过程。
后面等我们学习后,这三点我们会通过业务场景分别举例说明,确保我们已经掌握了。

前置知识

Spring Post-processor(后置处理器)

如果不太了解。麻烦先看这篇文章:后置处理器

Aware接口

如果不太了解。麻烦先看这篇文章:Aware接口

如果这两块不太了解,务必去看一下,大概会花费30分钟时间去理解,磨刀不误砍柴工。

简单介绍

Spring的生命周期是从Bean实例化之后,即通过反射创建出对象之后,到Bean成为一个完整对象,最终存储到单例池中,这个过程被称为Spring Bean的生命周期。Spring Bean的生命周期大体上分为三个阶段:

  • Bean的实例化阶段:Spring框架会取出BeanDefinition信息进行判断当前Bean的范围是否是singleton的,是否是延迟加载的,是否是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化;
  • Bean的初始化阶段:Bean创建之后还仅仅是个“半成品”,还需要对Bean实例的属性进行填充,执行一些Aware接口方法,执行BeanPostProcessor方法,执行InitializingBean接口的初始化方法,执行自定义初始化init方法等。该阶段是Spring最具技术含量和复杂度等阶段,Aop增强功能,后面的Spring注解功能等。
  • Bean的完成阶段:经过初始化阶段,Bean就成了一个完整的SpringBean,被存储到单例池singletonObjects中去了,即完成了SpringBean的整个生命周期。

Bean的实例化过程

为什么会有bean的实例化?

Bean的实例化是创建Bean对象,并将其放置在Sprig容器中,以便在需要时可以使用它们。
Bean的实例化使得开发者可以通过声明Bean定义来描述Bean的行为和属性,无需关心具体的实例化过程。

过程

Spring容器根据Bean的定义读取配置文件或者注解信息,找到Bean的定义。
2.
创建BeanDefinition对象:在加载配置文件后,Spring会为每个Bean定义一个BeanDefinition对象。BeanDefinition对象包含了Bean的信息,例如Bean的类名、依赖关系等。
3.
实例化Bean:在BeanDefinition对象创建完成后,Spring会使用BeanDefinition对象中的信息来创建Bean实例。Bean实例化的方式有多种,例如通过构造函数、工厂方法、对象池等方式来创建Bean实例。。
4.
如果Bean的作用域为singleton且不是懒加载模式,Spring容器会在容器启动时进行实例化。
如果作用域为prototype或者是singleton 且为懒加载模式,Spring容器会在第一次请求该Bean的时候进行实例化。
换句话说:Bean实例化后,Spring会根据Bean的作用域(scope)进行相应的处理,如果Bean的作用域是singleton,Spring会在容器初始化时创建Bean实例并将其放入容器中;如果Bean的作用域是prototype,Spring会在每次请求时创建新的Bean实例。

1.Bean的定义:
假设有一个名为 “userService” 的 Bean,在配置文件中的定义如下所示:

<bean id="userService" class="com.example.UserService">
    <property name="userDao" ref="userDao" />
</bean>

2.Bean的实例化
当容器启动时,如果 “userService” Bean 是 singleton 作用域且不是懒加载模式,则容器会进行实例化。
如果是 prototype 作用域或者是 singleton 且懒加载模式,则容器会在第一次请求该 Bean 的时候进行实例化。
3.依赖注入
在上述 Bean 定义中,“userService” Bean 的构造函数需要一个名为 “userDao” 的依赖对象。Spring 容器会自动进行依赖注入,例如在上述例子中,容器会先实例化名为 “userDao” 的 Bean,然后将该 Bean 注入到 “userService” Bean 的构造函数中。

Spring 容器在销毁 MyBean 实例之前,会调用其 cleanup() 方法进行清理操作。我们需要在 Bean 类中定义该方法。注意,如果 Bean 同时实现了 DisposableBean 接口和指定了 destroy-method 属性,则 destroy-method 属性中指定的方法会覆盖接口中定义的方法。

总体来说:
Bean 的实例化过程比较复杂,其中涉及到多个步骤和相关的接口和类。这些步骤和接口可以帮助我们在 Bean 的生命周期中执行自定义的逻辑和操作,增加了 Spring 框架的灵活性和扩展性。

Bean的初始化阶段

为什么会有Bean的初始化?

在Spring框架中,Bean的初始化是必要的,因为Spring的核心思想是IoC(控制反转)和DI(依赖注入)。在IoC中,对象的创建和维护不再由应用程序负责,而是由框架来完成。这意味着,应用程序只需要声明它需要哪些对象,而不必关心它们的创建和维护。

为了实现这个目标,Spring容器需要能够创建和管理所有声明的Bean。在容器启动时,Spring会读取配置文件(或注解)中的Bean定义,并创建相应的Bean实例。这个过程称为Bean的初始化。
Bean的初始化是Spring框架实现IoC和DI的必要步骤之一。

Bean的初始化目的是什么?

初始化节点:Bean实例化之后,属性注入之前,以及使用之前执行的操作。
目的:使用Bean之前,对Bean进行一些配置,检查,预处理等操作,以确保能够正确地工作。
意义:Bean的初始化给了我们一个机会,可以在Bean初始化之前或者之后对Bean进行一些扩展操作(例如:实现BeanPostProcessor接口的后置处理器,可以在Bean实例化后、属性注入前或后、初始化方法前或后等各个阶段对Bean进行操作。)。
大致的流程为:
1.Bean实例的属性填充:
当Spring容器创建一个bean实例时,它会自动注入该实例的依赖项,这些依赖项可以是其他的bean实例或者是基本类型的值。属性注入可以通过构造函数注入、setter方法注入或字段注入来完成。
2.Aware接口属性注入:
当一个bean实现了Aware接口时,Spring容器在创建bean的过程中会自动调用相应的接口方法,并将相关的依赖项传递给这些方法。这样可以让bean在创建时得到一些额外的信息,例如ApplicationContext、BeanFactory等。
3.BeanPostProcessor的before()方法回调
4.InitializingBean接口的初始化方法回调
5.自定义初始化方法init回调
6.BeanPostProcessor的after()方法回调

针对于上面的每个步骤,我们来看个例子就可以更加了解了。
1.
a. 构造函数注入:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getters and setters
}
<bean id="person" class="com.example.Person">
    <constructor-arg value="John Doe" />
    <constructor-arg value="30" />
</bean>

b.setter方法注入:

public class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // getters
}
<bean id="person" class="com.example.Person">
    <property name="name" value="John Doe" />
    <property name="age" value="30" />
</bean>

上面的例子中,我们定义了一个名为"person"的bean实例,并且通过字段注入了name和age属性。注入是通过使用@Value注解和XML属性元素的value属性来完成的。

注意,这些示例只是展示了属性填充的一些常见方式,实际上Spring还提供了其他的属性填充方式,例如注解注入和自动装配。
spring的自动装配,后面boot的时候我们会详细说。感兴趣可以点个关注哈哈。
3.字段注入

public class Person {
    @Value("John Doe")
    private String name;

    @Value("30")
    private int age;

    // getters
}
<bean id="person" class="com.example.Person">
    <property name="name" value="John Doe" />
    <property name="age" value="30" />
</bean>

2.Aware接口注入
如果 Bean 实现了 Aware 接口,例如 BeanNameAware 接口,则 Spring 容器会调用相应的方法将容器中的信息注入到该 Bean 中。例如:

public class MyBean implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
}

当容器实例化 “MyBean” Bean 时,会调用 setBeanName() 方法将该 Bean 在容器中的名称注入到 “beanName” 属性中。
3.BeanPostProcessor前置处理
如果 Bean 实现了 BeanPostProcessor 接口,例如:

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 在 Bean 初始化之前进行处理
        return bean;
    }
}

则 Spring 容器会在 Bean 实例化后,调用其 postProcessBeforeInitialization() 方法进行前置处理。例如可以在该方法中修改 Bean 的属性值或者生成代理对象等。
4.初始化方法
如果 Bean 实现了 InitializingBean 接口,例如:

public class MyBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 在 Bean 初始化完成之后进行操作
    }
}

或者指定了自定义的初始化方法,例如:

<bean id="myBean" class="com.example.MyBean" init-method="init">
</bean>

则 Spring 容器会在 Bean 实例化后,调用其 afterPropertiesSet() 方法或者指定的自定义初始化方法进行初始化操作。
6.BeanPostProcessor后置处理
如果 Bean 实现了 BeanPostProcessor 接口,例如:

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 在 Bean 初始化完成之后进行处理
        return bean;
    }
}

则 Spring 容器会在 Bean 初始化之后,调用其 postProcessAfterInitialization() 方法进行后置处理。例如可以在该方法中为 Bean 增加一些功能或者修改其属性值等。
8.DisposableBean

public class MyBean implements DisposableBean {
    @Override
    public void destroy() throws Exception {
        // 在 Bean 销毁之前进行清理操作
    }
}

当 Spring 容器销毁 MyBean 实例时,会调用其 destroy() 方法进行清理操作。开发人员可以在该方法中进行一些清理工作,例如关闭数据库连接、释放资源等。

另外,在 XML 配置文件中也可以通过指定 destroy-method 属性来定义 Bean 的销毁方法,例如:

<bean id="myBean" class="com.example.MyBean" destroy-method="cleanup">

依赖注入

基本概念

依赖注入(Dependency Injection,DI)是一种设计模式,它的核心思想是通过依赖将依赖关系从应用程序代码中抽离出来,由容器来负责和实例化和管理对象之间的依赖关系。
优点:
1.可测试性:通过将依赖项注入到组件中,可以轻松地模拟和替换这些依赖项,从而使得测试变得更加容易。
2.可扩展性:通过注入依赖项,组件的实现可以从外部配置和管理,从而使得应用程序更加灵活和可扩展。
3.可维护性:通过解耦组件之间的依赖关系,可以使得代码更加易于维护和修改。
4.可读性:通过使用依赖注入,可以使得组件之间的依赖关系更加明确和易于理解。

依赖注入的几种方式

Spring在进行属性填充的时候,会分如下几种情况:

  • 注入普通属性:
    String,int或存储基本类型的集合时,直接通过set方法的反射设置进去;
  • 注入单向对象引用属性:
    从容器中getBean获取后通过set方法反射设置进去,如果容器中没有,则先创建被注入对象Bean实例(完成整个生命周期)后,再进行注入操作;
  • 注入双向对象引用属性时:
    比较复杂了,双向对象引用属性指的是两个对象之间相互引用。涉及到了循环引用(循环依赖)问题,下面我们详细来聊一下这个问题。

需要注意的是,在使用Spring进行属性注入时,应该尽量避免使用双向对象引用属性,因为这样可能会导致复杂度的增加,代码难以维护。同时,需要根据实际情况选择合适的注入方式,保证代码的可读性和可维护性。

我们对于上面三种情况分别举出例子来说明:
1.注入普通属性的示例:

public class User {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // ...
}

<!-- XML配置文件中的配置 -->
<bean id="user" class="com.example.User">
    <property name="name" value="Alice"/>
    <property name="age" value="25"/>
</bean>

2.注入单向对象引用属性的示例:

public class Order {
    private User user;

    public void setUser(User user) {
        this.user = user;
    }

    // ...
}
<!-- XML配置文件中的配置 -->
<bean id="user" class="com.example.User">
    <property name="name" value="Alice"/>
    <property name="age" value="25"/>
</bean>
<bean id="order" class="com.example.Order">
    <property name="user" ref="user"/>
</bean>

3.注入双向对象引用属性的示例:

public class Department {
    private List<Employee> employees;

    public void addEmployee(Employee employee) {
        employees.add(employee);
        employee.setDepartment(this);
    }
    // ...
}
public class Employee {
    private Department department;

    public void setDepartment(Department department) {
        this.department = department;
    }
    // ...
}
<!-- XML配置文件中的配置 -->
<bean id="department" class="com.example.Department" lazy-init="true"/>

<bean id="employee1" class="com.example.Employee">
    <property name="department" ref="department"/>
</bean>

<bean id="employee2" class="com.example.Employee">
    <property name="department" ref="department"/>
</bean>

Department和Employee对象之间形成了双向引用关系,Department持有一个Employee的List,Employee持有一个Department的引用。

重点问题——依赖循环

那么,为什么会出现循环引用问题呢?

循环引用问题通常出现在双向引用关系中,例如A对象持有B对象的引用,B对象持有A对象的引用。在这种情况下,如果不加以限制,可能会导致对象的创建和销毁出现问题。
请添加图片描述

具体来说,当Spring容器在创建A对象时,需要先创建B对象并将B对象注入到A对象中。而在创建B对象时,又需要先创建A对象并将A对象注入到B对象中。这样就会形成一个循环依赖的情况,导致对象无法创建成功。

为了避免循环引用问题,可以使用一些技巧来限制对象之间的依赖关系。
例如,可以使用@Lazy注解来延迟注入,或者使用Setter方法注入来解决循环引用问题,当然spring自带的也有缓存也是解决方案。同时,需要根据实际情况选择合适的注入方式,保证代码的可读性和可维护性。
请添加图片描述

为了更好懂一些,我们举一个订单系统的 业务场景:
假设有一个简单的订单系统,包括订单(Order)和用户(User)两个实体类。
其中,一个订单只属于一个用户,而一个用户可以有多个订单。
因此,订单类需要持有一个用户对象的引用,用户类需要持有一个订单列表的引用。

代码如下:

public class Order {
    private User user;

    public void setUser(User user) {
        this.user = user;
    }

    // ...
}

public class User {
    private List<Order> orders;

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }

    // ...
}

如果使用Spring进行属性注入时,可能会出现循环引用问题。具体来说,当Spring容器在创建User对象时,需要先创建Order对象并将Order对象添加到User对象的订单列表中。而在创建Order对象时,又需要先创建User对象并将User对象注入到Order对象中。这样就形成了一个循环依赖,导致对象创建失败。

如何解决Spring依赖循环问题

Spring自身的策略——提供三级缓存

在Bean实例属性填充上:
Spring提供了三级缓存存储完整Bean实例半程品Bean实例,用于解决循环引用问题。
DefaultListableBeanFactory的上四级父类DefaultSingletonBeanRegistry中提供了如下三个Map:

/** Cache of singleton objects: bean name to bean instance. */
//1.存储单例Bean成品的容器,即实例化和初始化都完成的Bean,称之为“一级缓存”
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
//2.单例Bean的工厂池,缓存半成品对象,对象未被引用,使用时再通过工厂创建Bean,称之为“三级缓存”
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
//3.早期Bean单例池,缓存半成品对象,且当前对象已经被其他对象引用了,称之为“二级缓存”。
	private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

我们首先给一个图来看:
在这里插入图片描述
这里简单聊一下大概的过程:
1.首先一个userService实例化创建完毕之后,并不是直接存到singletonFactories三级缓存里面,这个userService会用ObjectFactory进行一个包装,换句话说就是为它创建一个ObjectFactory,然后添加到三级缓存中,我们来看一下这个类:

@FunctionalInterface
public interface ObjectFactory<T> {

	/**
	 * Return an instance (possibly shared or independent)
	 * of the object managed by this factory.
	 * @return the resulting instance
	 * @throws BeansException in case of creation errors
	 */
	T getObject() throws BeansException;

}

所以等到我们真正去用的时候,再去调用里面的getObject方法。
我们也很清楚的明白,这个bean是未被引用的。
2.我们判断容器中是否有UserDao时,原先是从单例池找,但现在有了三级缓存(这里指的是三个缓存,即:一级,二级,三级),就会把三级缓存整个找下来看有没有UserDao。
3.找不到后,进去下半部分的路线,直到执行到判断(容器中是否有UserService),发现第三级缓存(这里只单指第三级缓存)之前存过了,那么就进行注入。
4.UserService被注入了,那么就放入到二级缓存

完整流程如下:

  1. UserService实例化对象,但还没有初始化,将UserService存储到第三级缓存。
  2. UserService属性注入,需要UserDao,从缓存中获取,没有UserDao;
  3. UserDao实例化对象,但尚未初始化,将UserDao存储到三级缓存;
  4. UserDao属性注入,需要UserService,从三级缓存获取UserService,UserService从三级缓存移入二级缓存。
  5. UserDao执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。
  6. UserService注入UserDao。
  7. UserService执行其他生命周期过程,最终成为一个完成Bean,存储到一级缓存,删除二三级缓存。

这块目前我们只说原理,源码后面等我们聊完refresh后,我们再一块梳理一下整个Bean生命周期的源码流程。

生命周期总结

一张图,无需多言,这个老师真的非常牛!!!!非常清晰。
如果觉得哪里看不明白,欢迎留言。
请添加图片描述

总结

Spring Bean生命周期上文涉及的大多是原理,等到后面源码篇时,我们会聊里面具体的代码实现。如果你对后面的内容感兴趣,麻烦点个关注,后面更新后方便更快查看。

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

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

相关文章

线性和非线性最小二乘问题的常见解法总结

线性和非线性最小二乘问题的各种解法 先看这篇博客&#xff0c;非常好&#xff1a;线性和非线性最小二乘问题的各种解法 1. 线性最小二乘问题有最优解 但是面对大型稀疏矩阵的时候使用迭代法效率更好。 迭代法 有Jacobi迭代法、 Seidel迭代法及Sor法 【数值分析】Jacobi、Se…

[ubuntu][GCC]gcc源码编译

1.下载gcc安装包 https://ftp.gnu.org/gnu/gcc/ 选择一个需要的gcc版本&#xff0c;下载。 2.下载依赖包 查看下载的gcc安装包中contrib文件夹下的download_prerequisites文件&#xff0c;查看需要的依赖包版本。 根据download_prerequisites中红框位置的信息&#xff0c;在下…

JSON.stringify()的5种使用场景

JSON.stringify() 方法将一个JavaScript对象或值转换为JSON字符串&#xff0c;如果指定了一个replacer函数&#xff0c;则可以选择性地替换值&#xff0c;或者指定的replacer是数组&#xff0c;则可选择性地仅包含数组指定的属性。 语法如下&#xff1a; JSON.stringify(value…

电子技术课程设计基于FPGA的音乐硬件演奏电路的设计与实现

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;乐曲电路 免费获取完整无水印论文报告&#xff08;包含电路图&#xff09; 文章目录一、设计任务要求二、总体框图三、选择器件四、功能模块五、总体设计电路图六、结束语一、设计任务要求 1、课程设计题目 设计一个乐曲演…

【Flutter从入门到入坑之三】Flutter 是如何工作的

【Flutter从入门到入坑之一】Flutter 介绍及安装使用 【Flutter从入门到入坑之二】Dart语言基础概述 【Flutter从入门到入坑之三】Flutter 是如何工作的 本文章主要以界面渲染过程为例&#xff0c;介绍一下 Flutter 是如何工作的。 页面中的各界面元素&#xff08;Widget&…

使数组和能被P整除[同余定理+同余定理变形]

同余定理同余定理变形前言一、使数组和能被P整除二、同余定理变形总结参考资料前言 同余定理非常经典&#xff0c;采用前缀和 map&#xff0c;当两个余数前缀和为一个值时&#xff0c;则中间一段子数组刚好对P整除。但是能否找到前面是否有一段子数组和可以对P整除呐&#xf…

认识CSS之元素显示模式

&#x1f31f;所属专栏&#xff1a;前端只因变凤凰之路&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新前端的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简…

深度学习训练营之数据增强

深度学习训练营学习内容原文链接环境介绍前置工作设置GPU加载数据创建测试集数据类型查看以及数据归一化数据增强操作使用嵌入model的方法进行数据增强模型训练结果可视化自定义数据增强查看数据增强后的图片学习内容 在深度学习当中,由于准备数据集本身是一件十分复杂的过程,…

Python 中 KeyError: 0 exception 错误

Python “KeyError: 0” 异常是在我们尝试访问不包含0 这个键的时候去访问该键而引起的。 要解决该错误&#xff0c;请在尝试访问字典之前在字典中设置键&#xff0c;或者如果键不存在&#xff0c;则使用 dict.get() 获取默认值。 下面是一个产生上述错误的示例 my_dict {1…

KDZD互感器二次负载测试仪

一、概述 电能计量综合误差过大是电能计量中普遍存在的一个关键问题。电压互感器二次回路压降引起的计量误差往往是影响电能计量综合误差的因素。所谓电压互感器二次压降引起的误差&#xff0c;就是指电压互感器二次端子和负载端子之间电压的幅值差相对于二次实际电压的百分数…

五分钟了解JumpServer V2.* 与 v3 的区别

一、升级注意项 1、梳理数据。JumpServer V3 去除了系统用户功能&#xff0c;将资产与资产直接绑定。当一个资产名下有多个同名账号&#xff0c;例如两个root用户时&#xff0c;升级后会自动合并最后一个root&#xff0c;不会同步其他root用户。升级前需保证每一个资产只拥有一…

即时通讯系列-N-客户端如何在推拉结合的模式下保证消息的可靠性展示

结论先行 原则&#xff1a; server拉取的消息一定是连续的原则&#xff1a; 端侧记录的消息的连续段有两个作用&#xff1a; 1. 记录消息的连续性&#xff0c; 即起始中间没有断层&#xff0c; 2. 消息连续&#xff0c; 同时意味着消息是最新的&#xff0c;消息不是过期的。同…

Java学习-MySQL-创建数据库表

Java学习-MySQL-创建数据库表 SHOW DATABASESUSE school CREATE TABLE IF NOT EXISTS student( id INT(10) NOT NULL AUTO_INCREMENT COMMENT 学号, name VARCHAR(30) NOT NULL DEFAULT 匿名 COMMENT 姓名, pws VARCHAR(20) NOT NULL DEFAULT 123456 COMMENT 密码, sex VARCHA…

算法题--二叉树(判断是不是平衡二叉树、二叉树的中序遍历、二叉树最大深度、对称二叉树、合并二叉树)

目录 二叉树 题目 判断是不是平衡二叉树 题链接 解析 核心思想 答案 二叉树的中序遍历 原题链接 解析 核心思想 答案 二叉树最大深度、对称二叉树、合并二叉树 二叉树 该类题目的解决一般是通过节点的遍历去实现&#xff0c;一般是分两种。 一是递归&#xff08;…

【记录】日常|shandianchengzi的三周年创作纪念日

机缘 接触 CSDN 之前&#xff0c;我已经倒腾过 hexo 搭建 github 博客、本地博客、图床&#xff1b;   接触 CSDN 之后&#xff0c;我还倒腾过纸质笔记、gitee 博客、博客园、知乎、b站、Notion、腾讯文档、有道云笔记、XMind、飞书文档、简书等一系列创作平台&#xff0c;但…

SAPUI5开发01_01-Installing Eclipse

1.0 简要要求概述: 本节您将安装SAPUI 5,以及如何在Eclipse Juno中集成SAPUI 5工具。 1.1 安装JDK JDK 是一种用于构建在 Java 平台上发布的应用程序、Applet 和组件的开发环境,即编写 Java 程序必须使用 JDK,它提供了编译和运行 Java 程序的环境。 在安装 JDK 之前,首…

1635_fileno的简单使用

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 在看MIT的OS课程的时候发现自己动不动就因为只是的缺少而卡住&#xff0c;而这个学习占据了我工作之余很多的时间。现在都有一点觉得通关不了的感觉了&#xff0c;…

1. Qt Designer Studio界面介绍

1. 说明&#xff1a; Qt当中的Qt Quick框架使用QML语言来快速搭建优美的界面&#xff0c;但是对于单纯做界面的设计人员并不是很友好&#xff0c;还要让界面设计人员去消耗时间成本学习QML语法。Qt Designer Studio软件就是为了解决这个问题而设计的&#xff0c;工作人员不需要…

【Blender】Stability AI插件 - AI生成图像和动画

Stability AI 的官方插件允许 Blender 艺术家使用现有的项目和文本描述来创建新的图像、纹理和动画。 推荐&#xff1a;用 NSDT场景设计器 快速搭建3D场景。 1、安装Stability for Blender插件 首先&#xff0c;从这里下载最新版本的 Blender&#xff0c;然后转到 Addon Relea…

论文阅读笔记|大规模多标签文本分类

多标签文本分类&#xff08;Extreme Multi Label Classification, MLTC&#xff09;是自然语言处理领域中一个十分重要的任务&#xff0c;其旨在从一个给定的标签集合中选取出与文本相关的若干个标签。MLTC可以广泛应用于网页标注&#xff0c;话题识别和情感分析等场景。大规模…