当前位置: 首页 > news >正文

浅谈Spring循环依赖

文章目录

    • 1.前言
    • 2.什么是循环依赖?
    • 3.两种Spring容器循环依赖
      • 3.1.构造器循环依赖(无法解决)
      • 3.2.setter循环依赖(可以解决)
      • 3.3.小结
    • 4.循环依赖检查
    • 5.循环依赖的处理
      • 5.1.单例setter循环依赖
      • 5.2.Spring解决循环依赖
      • 5.3. 循环依赖的本质
      • 5.4.what?问题的本质居然是two sum!
      • 5.5.单例构造器注入循环依赖
      • 5.6.原型模式循环依赖
    • 6.总结
      • 6.1.循环依赖的原因
      • 6.2.循环依赖的解决方案
      • 6.3.整个从创建bean到解决循环依赖的过程
      • 6.4.循环依赖建议
    • 7.Spring循环依赖图解

1.前言

想彻底弄清楚spring的循环依赖问题,首先得弄清楚

  1. 循环依赖是如何发生的
  2. spring又是如何检测循环依赖的发生的。
  3. 其次再探究spring如何解决循环依赖的问题

2.什么是循环依赖?

循环依赖就是循环引用,指两个或多个bean互相持有对方,比如说TestA引用TestB、TestB引用TestA,最终形成一个闭环。

注意:循环依赖不是指循环调用。

循环调用:指方法之间的环调用,循环调用是无解的,除非有终结条件,否则就是死循环,最终会导致内存溢出异常。

package com.bruce.test;import java.io.File;public class TestFile {static int count=0;public static void main(String[] args) {File f=new File("D:\\");showFiles(f);System.out.println("文件总数是:"+count);}public static void showFiles(File f){//获取所有的文件和文件目录File[] files = f.listFiles();for (File file : files) {if(file.isFile()){if(file.getName().endsWith(".jpg")){count++;System.out.println(file.getAbsolutePath());}}else{//目录showFiles(file);}}}
}

3.两种Spring容器循环依赖

  • 构造器循环依赖
  • setter方法循环依赖

就是A类里面有B类的引用b,B类里面有C类的引用c,C类里面有A类的引用a,如下图所示
在这里插入图片描述

3.1.构造器循环依赖(无法解决)

A类

package com.bruce.spring2021;public class A {private B b;public A (B b){this.b = b;}public B getB() {return b;}public void setB(B b) {this.b = b;}
}

B类

package com.bruce.spring2021;public class B {private C c;public B (C c){this.c = c;}public C getC() {return c;}public void setC(C c) {this.c = c;}
}

C类

package com.bruce.spring2021;public class C {private A a;public C (A a){this.a = a;}public A getA() {return a;}public void setA(A a) {this.a = a;}
}

applicationContext.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="A" class="com.bruce.spring2021.A" ><constructor-arg name="b" ref="B"/></bean><bean id="B" class="com.bruce.spring2021.B" ><constructor-arg name="c" ref="C"/></bean><bean id="C" class="com.bruce.spring2021.C" ><constructor-arg name="a" ref="A"/></bean></beans>

主函数入口

package com.bruce.spring2021;import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSpring {@Testpublic void test1(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");}}

运行main函数,报错截图
在这里插入图片描述

3.2.setter循环依赖(可以解决)

A类

public class A {public A() {}private B b;public B getB() {return b;}public void setB(B b) {this.b = b;}
}

B类

public class B {public B() {}private C c;public C getC() {return c;}public void setC(C c) {this.c = c;}
}

C类

public class C {public C() {}private A a;public A getA() {return a;}public void setA(A a) {this.a = a;}
}

applicationContext.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="A" class="com.bruce.spring2021.A" ><property name="b" ref="B"/></bean><bean id="B" class="com.bruce.spring2021.B" ><property name="c" ref="C"/></bean><bean id="C" class="com.bruce.spring2021.C" ><property name="a" ref="A"/></bean></beans>

最后输出

package com.bruce.spring2021;import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSpring {@Testpublic void test2(){ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext1.xml");A a = ctx.getBean("A", A.class);//System.out.println(a);//System.out.println(a.getB());//System.out.println(a.getB().getC());//System.out.println(a.getB().getC().getA());}
}

3.3.小结

通常来说,如果问Spring内部如何解决循环依赖,一定是单默认的单例Bean中,属性互相引用的场景。比如几个Bean之间的互相引用:
在这里插入图片描述

甚至自己“循环”依赖自己:
在这里插入图片描述
先说明前提:原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常

if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);
}

原因很好理解,创建新的A时,发现要注入原型字段B,又创建新的B发现要注入原型字段A…

这就套娃了, 你猜是先StackOverflow还是OutOfMemory

Spring怕你不好猜,就先抛出了BeanCurrentlyInCreationException

基于构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。

那么默认单例的属性注入场景,Spring是如何支持循环依赖的?

4.循环依赖检查

<bean id="a" class="A"><property name="b" ref="b">
<bean/><bean id="b" class="B"><property name="a" ref="a">
<bean/>

无论单例还是原型模式(下文①代表图中步骤1),spring都有对应的集合(singletonsCurrentlyInCreation)保存当前正在创建的beanName,标识该beanName正在被创建。
在这里插入图片描述

在bean创建前,
①检测当前bean是否在创建中,如果不在创建中则
②将beanName加入集合,往下创建bean。在bean创建前,检测到当前的bean正在创建,则说明发生循环依赖,抛出异常。最后记得当bean创建完时将beanName移出集合

在这里插入图片描述

5.循环依赖的处理

5.1.单例setter循环依赖

spring注入属性的方式有多种,但是只有一种循环依赖能被解决:单例setter依赖注入

spring解决循环依赖的做法是未等bean创建完就先将实例曝光出去,方便其他bean的引用。同时还提到了三级缓存,最先曝光到第三级缓存singletonFactories中。简单的说,就是spring先将创建好的实例放到缓存中,让其他bean可以提前引用到该对象。

示例

// 第一种 注解方式
public class A {@Autowiredprivate B b;
}public class B {@Autowiredprivate A a;
}// ===========================
// 第二种 xml配置方式
public class A {private B b;// getter setter
}public class B {private A a;// getter setter
}<bean id="a" class="A"><property name="b" ref="b">
<bean/><bean id="b" class="B"><property name="a" ref="a">
<bean/>

分析
在这里插入图片描述
其中跟循环依赖检测对比,新添加的几个关键节点已经用黄色标识出来,这里有几个重点给大家画一下。

  • 提前曝光,如果用c语言的说法就是将指针曝光出去,用java就是将引用对象曝光出去。也就是说即便a对象还未创建完成,但是在④实例化过程中new A()动作已经开辟了一块内存空间,只需要将该地址抛出去b就可以引用的到,而不管a后期还会进行初始化等其他操作

  • 已经了解了提前曝光的作用,而相比而言⑤曝光的时机也非常的重要,该时机发生在④实例化之后,⑥填充与⑯ 初始化之前。spring循环依赖之所以不能解决实例化注入的原因正式因为注入时机在曝光之前所导致

5.2.Spring解决循环依赖

首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。
在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:

  • singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
  • singletonFactories 映射创建Bean的原始工厂
  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。

为什么成为后两个Map为垫脚石,假设最终放在singletonObjects的Bean是你想要的一杯“凉白开”。
那么Spring准备了两个杯子,即singletonFactoriesearlySingletonObjects来回“倒腾”几番,把热水晾成“凉白开”放到singletonObjects中。
闲话不说,都浓缩在图里。

在这里插入图片描述

5.3. 循环依赖的本质

上文了解完Spring如何处理循环依赖之后,让我们跳出“阅读源码”的思维,假设让你实现一个有以下特点的功能,你会怎么做?

  • 将指定的一些类实例为单例
  • 类中的字段也都实例为单例
  • 支持循环依赖

举个例子,假设有类A:

@Component
public class A {@Autowiredprivate B b;
}
// 类B:@Component
public class B {@Autowiredprivate A a;@Transactionpublic void show(){}
}

说白了让你模仿Spring:假装A和B是被@Component修饰, 并且类中的字段假装是@Autowired修饰的,处理完放到Map中。其实非常简单,笔者写了一份粗糙的代码,可供参考:

 /*** 放置创建好的bean Map*/private static Map<String, Object> cacheMap = new HashMap<>(2);public static void main(String[] args) {// 假装扫描出来的对象Class[] classes = {A.class, B.class};// 假装项目初始化实例化所有beanfor (Class aClass : classes) {getBean(aClass);}// checkSystem.out.println(getBean(B.class).getA() == getBean(A.class));System.out.println(getBean(A.class).getB() == getBean(B.class));}@SneakyThrowsprivate static <T> T getBean(Class<T> beanClass) {// 本文用类名小写 简单代替bean的命名规则String beanName = beanClass.getSimpleName().toLowerCase();// 如果已经是一个bean,则直接返回if (cacheMap.containsKey(beanName)) {return (T) cacheMap.get(beanName);}// 将对象本身实例化Object object = beanClass.getDeclaredConstructor().newInstance();// 放入缓存cacheMap.put(beanName, object);// 把所有字段当成需要注入的bean,创建并注入到当前bean中Field[] fields = object.getClass().getDeclaredFields();for (Field field : fields) {field.setAccessible(true);// 获取需要注入字段的classClass<?> fieldClass = field.getType();String fieldBeanName = fieldClass.getSimpleName().toLowerCase();// 如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可// 如果缓存没有 继续创建field.set(object, cacheMap.containsKey(fieldBeanName)? cacheMap.get(fieldBeanName) : getBean(fieldClass));}// 属性填充完成,返回return (T) object;}

这段代码的效果,其实就是处理了循环依赖,并且处理完成后,cacheMap中放的就是完整的“Bean”了
在这里插入图片描述
这就是“循环依赖”的本质,而不是“Spring如何解决循环依赖”。

5.4.what?问题的本质居然是two sum!

看完笔者刚才的代码有没有似曾相识?没错,和two sum的解题是类似的。不知道two sum是什么梗的,笔者和你介绍一下:two sum是刷题网站leetcode序号为1的题,也就是大多人的算法入门的第一题。常常被人调侃,有算法面的公司,被面试官钦定了,合的来。那就来一道two sum走走过场。

问题内容是:给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引。比如:给定

nums = [2, 7, 11, 15],

target = 9 那么要返回 [0, 1],因为2 + 7 = 9这道题的优解是,一次遍历+HashMap:

class Solution {public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++) {int complement = target - nums[i];if (map.containsKey(complement)) {return new int[] { map.get(complement), i };}map.put(nums[i], i);}throw new IllegalArgumentException("No two sum solution");}
}

先去Map中找需要的数字,没有就将当前的数字保存在Map中,如果找到需要的数字,则一起返回。

和笔者上面的代码是不是一样?

先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。

5.5.单例构造器注入循环依赖

上面已经剧透了这个方式是不得行的,原因是依赖注入的时间点不对,他的依赖注入发生在构造器阶段,这个时候连实例都没有,内存都还没开辟完,当然也还没有进行提前曝光,因此不得行

示例

public class A {private B b;@Autowiredpublic A(B b) {this.b = b;}
}public class B {private  A a;@Autowiredpublic B(A a) {this.a = a}
}

分析
在这里插入图片描述
图上重点地方也用黄色标出了,问题的原因处在④实例化,实例化的过程是调用new A(B b);的过程,这时的A还未创建出来,根本是不可能提前曝光的,正是这个原因导致⑨无法获取到三级缓存,进而导致⑩异常的抛出

5.6.原型模式循环依赖

这此没有图了,因为原型模式每次都是重新生成一个全新的bean,根本没有缓存一说。这将导致实例化A完,填充发现需要B,实例化B完又发现需要A,而每次的A又都要不一样,所以死循环的依赖下去。唯一的做法就是利用循环依赖检测,发现原型模式下存在循环依赖并抛出异常.

6.总结

6.1.循环依赖的原因

beanA依赖beanB,beanB依赖beanA,导致两者都不能被创建,发生在填充属性的环节

6.2.循环依赖的解决方案

提前曝光机制+三级缓存

  • 提前曝光
    正常来说bean的创建过程有三步:实例化->填充属性->初始化,提前曝光就是实例化后填充属性前将bean放入缓存

在这里插入图片描述

总结一下循环依赖,spring只能解决setter注入单例模式下的循环依赖问题。要想解决循环依赖必须要满足2个条件:

  1. 需要用于提前曝光的缓存
  2. 属性的注入时机必须发生在提前曝光动作之后,不管是填充还是初始化都行,总之不能在实例化,因为提前曝光动作在实例化之后

理解了这2点就可以轻松驾驭循环依赖了。比如构造器注入是不满足第二个条件,曝光时间不对。而原型模式则是缺少了第一个条件,没有提前曝光的缓存供使用。

  • 三级缓存
singletonObjects:一级缓存,用于存放完全初始化好的bean,从改缓存中取出的bean可以直接使用
earlySingletonObjects:二级缓存,用于存放提前曝光的单例对象的cache,存放原始的bean对象(尚未填充属性)
singletoneFactories:三级缓存,存放提前曝光的bean的工厂,用于生产二级缓存提前曝光的实例urrentlyInCreation:正在创建的bean集合,在bean开始创建时放值,创建完成时移出
alreadyCreated:bean被创建完成后,会放进这个set集合

先从一级缓存获取bean->从二级缓存获取->如果工厂可以创建bean,就从三级缓存获取并且将三级缓存中的bean移到二级缓存

public interface ObjectFactory<T> {T getObject() throws BeansException;
}

加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

6.3.整个从创建bean到解决循环依赖的过程

context.getBean(A.class)->实例化->放入缓存->依赖注入B->getBean(B)->实例化B并放入缓存->B依赖注入A->getBean(A)获取到了缓存中的值并正常返回->B初始化成功->A初始化成功

6.4.循环依赖建议

业务代码中尽量不要使用构造器注入,三级缓存解决不了构造器循环依赖的问题

业务代码中为了简洁,尽量使用field注入而不是setter方法注入

7.Spring循环依赖图解

在这里插入图片描述

相关文章:

浅谈Spring循环依赖

文章目录1.前言2.什么是循环依赖&#xff1f;3.两种Spring容器循环依赖3.1.构造器循环依赖&#xff08;无法解决&#xff09;3.2.setter循环依赖&#xff08;可以解决&#xff09;3.3.小结4.循环依赖检查5.循环依赖的处理5.1.单例setter循环依赖5.2.Spring解决循环依赖5.3. 循环…...

华为OD机试题 - 拼接 URL(JavaScript)| 包含代码编写思路

最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 华为OD机试题 最近更新的博客使用说明拼接 URL题目…...

【FFMPEG】Filtering Introduction[翻译/举例]

Filtering Introduction Filtering in FFmpeg is enabled through the libavfilter library. FFmpeg中的Filtering可以通过libavfilter library来使用。 In libavfilter, a filter can have multiple inputs and multiple outputs. To illustrate the sorts of things that are…...

什么是IP65?仅仅是防水等级吗?看完本文直呼666!

IP65在硬件设备&#xff0c;准确的来说在电气设备中&#xff0c;这个参数很常见&#xff0c;但是作为网络技术的博主&#xff0c;为啥要介绍IP65&#xff1f; 这个很好解释&#xff0c;因为网络设备&#xff0c;比如路由器、交换机&#xff0c;还有服务器、监控等都是属于电气…...

Flask入门(10):数据库连接池

目录10.数据库连接池模式一模式二示例&#xff1a;使用数据库连接池进行登录验证10.数据库连接池 参考&#xff1a;https://www.cnblogs.com/wangkun122/articles/8992637.html 通过DBUtils实现数据库连接池 安装&#xff1a; pip install DBUtils1.2注意&#xff1a;pytho…...

华为OD机试C++实现 - 最小步骤数

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

数仓:用户行为类指标一网打尽

前言 用户行为分析是对用户在产品或触点上产生的行为及行为背后的数据进行分析&#xff0c;通过构建用户行为数据分析体系或者用户画像&#xff0c;来改变产品、营销、运营决策&#xff0c;实现精细化运营&#xff0c;指导业务增长。总之&#xff0c;很重要。 先来看下用户类…...

mysql数据库的主从复制

一、实现主从复制的方式。 异步复制&#xff1a;它是mysql默认的同步方式&#xff0c;从库通过io线程去拉取 bin log时&#xff0c;主库不需要关注这个时候是否有从库在同步数据&#xff0c;他只做自己的事情就可以了&#xff0c; 整个复制过程都是异步完成的 ; 半同步复制&…...

【极海APM32替代笔记】低功耗模式、WFI命令等进入不了休眠的可能原因(系统定时器SysTick一直产生中断)

【极海APM32替代笔记】低功耗模式、WFI命令等进入不了休眠的可能原因&#xff08;系统定时器SysTick一直产生中断&#xff09; 【STM32笔记】低功耗模式配置及避坑汇总 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模…...

一文搞懂秒杀系统,欢迎参与开源,提交PR,提高竞争力。早日上岸,升职加薪。

前言 秒杀和高并发是面试的高频考点&#xff0c;也是我们做电商项目必知必会的场景。欢迎大家参与我们的开源项目&#xff0c;提交PR&#xff0c;提高竞争力。早日上岸&#xff0c;升职加薪。 知识点详解 秒杀系统架构图 秒杀流程图 秒杀系统设计 这篇文章一万多字&#xff0c;…...

华为OD机试真题 用 C++ 实现 - 子序列长度 | 多看题,提高通过率

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

华为OD机试题 - 符合条件的子串长度(JavaScript)| 包含代码编写思路

最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 华为OD机试题 最近更新的博客使用说明符合条件的子…...

快速读懂网络拓扑图

快速读懂网络拓扑图几重常见的网络拓扑总线型拓扑简介优点缺点环型拓扑简介优点缺点星型拓扑简介优点缺点网络层级机构节点结点链路通路不同的连接线代表什么意思&#xff1f;不同颜色、粗细的直线代表什么意思&#xff1f;闪电线-串行链路几重常见的网络拓扑 总线型拓扑 简介…...

《上海市创新型企业总部认定和奖励管理办法》

各区人民政府、有关单位&#xff1a; 为加快推动上海创新型经济发展&#xff0c;支持各类高成长性企业和研发机构升级打造创新型企业总部&#xff0c;培育壮大更多高能级创新主体&#xff0c;为建设具有全球影响力的科技创新中心提供支撑&#xff0c;现将《上海市创新型企业总…...

LeetCode 160. 相交链表 -- 消除长度差

相交链表 简单 2K 相关企业 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意…...

《分布式技术原理与算法解析》学习笔记Day19

分布式通信&#xff1a;消息队列 什么是消息队列&#xff1f; 队列是一种具有先进先出特点的数据结构&#xff0c;消息队列是基于队列实现的、存储具有特定格式的消息数据。消息以特定格式放入这个队列的尾部后直接返回&#xff0c;不需要系统马上处理&#xff0c;之后有其他…...

云、安全、网络三位一体,Akamai 推出大规模分布式边缘和云平台 Akamai Connected Cloud

出品 | CSDN 云计算 云服务市场规模在持续增长。 基于网络技术积累与优势&#xff0c;与布局边缘计算之后&#xff0c;巨头 Akamai 在继续推进它的技术与产品进程。近日&#xff0c;Akamai 正式推出大规模分布式边缘和云平台 Akamai Connected Cloud&#xff0c;包含云计算、安…...

生产者消费者模型(多线程工作)

目录 1.模型前提 2.阻塞队列&#xff08;消费场所&#xff09; 3. 实验 4.有关效率 1.模型前提 以单生产者对单消费者为例子&#xff1a; 前提一&#xff1a;有一个缓冲区作为消费场所。 前提二&#xff1a;有两种功能不同的线程分别具有消费与生产的能力。 前提三&…...

InnoDB锁

1、共享排他锁 Shared and Exclusive Locks--共享锁&#xff08;SLock&#xff09;&#xff0c;允许持有该锁的事务读取一行数据--排它锁&#xff08;XLock&#xff09;&#xff0c;允许持有该锁的事务删除或者更新一行数据特性&#xff1a;--行级锁--如果一个事务持有当前行的…...

Java Stream、File、IO 超详细整理,适合新手入门

目录 Java Stream Java File Java IO Java Stream Java Stream 是 Java 8 中引入的一种新的抽象数据类型&#xff0c;它允许开发人员使用函数式编程的方式来处理集合数据。 使用 Java Stream 可以方便地进行过滤、映射、排序和聚合等操作。下面是一个简单的示例&#xff1a;…...

华为OD机试真题Python实现【寻找密码】真题+解题思路+代码(20222023)

寻找密码 题目 小王在进行游戏大闯关,有一个关卡需要输入一个密码才能通过,密码获得的条件如下: 在一个密码本中,每一页都有一个由 26 个小写字母组成的若干位密码, 从它的末尾开始依次去掉一位得到的新密码也在密码本中存在。 请输出符合要求的密码,如果由多个符合要求…...

springboot和springframework版本依赖关系

springboot和springframework版本依赖关系 springboot版本springframework版本发布时间1.0.x1.0.0.RELEASE4.0.3.RELEASE2014.041.0.1.RELEASE4.0.3.RELEASE2014.041.0.2.RELEASE4.0.3.RELEASE2014.041.1.x1.1.0.RELEASE4.0.5.RELEASE2014.061.1.1.RELEASE4.0.5.RELEASE2014.0…...

Java-多线程-增强篇-锁 强化 第一篇

今天我们来学一下锁 会持续保持更新 欢迎追更哈 Java - 多线程 - 锁和提升 第1篇 首先强调一点&#xff1a;Java多线程的锁都是基于对象的&#xff0c;Java中的每一个对象都可以作为一个锁。同时&#xff0c;类锁也是对象锁&#xff0c;类是Class对象 Java8锁 核心思想 关键…...

Java static+private实现单例模式

1. 单例模式介绍 在Java中单例设计模式准确来说是&#xff0c;类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法。 2. 实现思路 如果我们要让类在一个虚…...

华为OD机试 - 查找充电设备组合(Python)【2023-Q1 新题】

华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 查找充电设备组合…...

Authing 入选德勤“中国明日之星”企业榜单

近日&#xff0c;德勤发布“德勤中国明日之星”榜单&#xff0c;该项目致力于发掘和表彰蓬勃成长、持续创新、积极承担社会责任的卓越企业。该榜单1995 年创立至今&#xff0c;被业界誉为“全球高成长企业的标杆”。Authing 凭借在 IDaaS&#xff08;身份云&#xff09; 领域突…...

单片机嵌入式操作系统内核

1、前后台系统&#xff0c;协作式内核系统&#xff0c;与占先式内核系统&#xff0c;有什么不同呢&#xff1f; 记得在 21IC 上看过这样的比喻, 你(小工)在用厕所&#xff0c;经理在外面排第一&#xff0c;老板在外面排第二。 如果是前后台&#xff0c;不管是谁&#xff0c;都…...

C语言——柔性数组

目录0. 前言1. 思维导图2. 柔性数组的特点3. 柔性数组的使用4. 柔性数组的优势5. 结语0. 前言 柔性数组是在C99标准时引入&#xff1a; 结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫柔性数组成员。 代码示例&#xff1a; typedef struct flexible_arr {int a…...

web人脸登录

1&#xff0c;需要腾讯人脸第三方 腾讯人脸第三方申请步骤 2&#xff0c;相关表结构设计 create database faceDB;use faceDB; -- 人脸表 create table face( fid int primary key auto_increment COMMENT 主键, face_base longtext COMMENT 图片数据 base_64编码, create_tim…...

C++数字

目录 一、什么是数字 二、定义数字 三、数学运算 四、随机数 一、什么是数字 通常&#xff0c;当我们需要用到数字时&#xff0c;我们会使用原始的数据类型&#xff0c;如 int、short、long、float 和 double 等等。这些用于数字的数据类型&#xff0c;其可能的值和数值范围…...