Spring循环依赖问题,Spring是如何解决循环依赖的?
文章目录
- 一、什么是循环依赖
- 1、代码实例
- 2、重要信息
- 二、源码分析
- 1、初始化Student
- 对Student中的ClassRoom进行Autowire操作
- 2、Student的自动注入ClassRoom时,又对ClassRoom的初始化
- 3、ClassRoom的初始化,又执行自动注入Student的逻辑
- 4、Student注入ClassRoom
- 5、初始化完成的Bean放入一级缓存
- 三、总结
- 1、二级缓存的用处
- 2、汇总流程图!
- 参考资料
一、什么是循环依赖
多个bean之间相互依赖,形成了一个闭环。 比如:A依赖于B、B依赖于C、C依赖于A。

注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。
1、代码实例
public class ClassRoom {private String name;@Autowiredprivate Collection<Student> students;// get set
}public class Student {private Long id;private String name;@Autowiredprivate ClassRoom classRoom;// get set
}
public class CircularReferencesDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();// 注册 Configuration Classcontext.register(CircularReferencesDemo.class);// 如果设置为 false,则抛出异常信息如:currently in creation: Is there an unresolvable circular reference?// 默认值为 truecontext.setAllowCircularReferences(true);// 启动 Spring 应用上下文context.refresh();System.out.println("Student : " + context.getBean(Student.class));System.out.println("ClassRoom : " + context.getBean(ClassRoom.class));// 关闭 Spring 应用上下文context.close();}@Beanpublic static Student student() {Student student = new Student();student.setId(1L);student.setName("张三");return student;}@Beanpublic static ClassRoom classRoom() {ClassRoom classRoom = new ClassRoom();classRoom.setName("C122");return classRoom;}
}
我们看到以上实例ClassRoom和Student是互相依赖的。
Spring默认是打开处理循环依赖的开关的,我们可以使用setAllowCircularReferences方法手动设置关闭循环依赖。
开启解决循环依赖之后,以上代码是可以执行没有问题的。关闭循环依赖,以上就会出现循环依赖的异常,如果确认项目中没有循环依赖的问题可以关闭,以提高性能。
2、重要信息
本文的源码都是基于Spring5.2.2版本进行分析的,其他版本或许有些许出入,但是差别不大。
本文所说的循环引用,都是指的是单例Bean,原型Bean不在此分析中。
阅读本文需要明白一些前置知识点:
(1)单例的Bean初始化时机-finishBeanFactoryInitialization方法执行时:
Spring应用上下文生命周期详解及源码分析,Spring IOC容器启动及关闭过程超详细解释(上)
(2)Bean的生命周期需要熟悉:
Spring Bean生命周期——从源码角度详解Spring Bean的生命周期(上)
Spring Bean生命周期——从源码角度详解Spring Bean的生命周期(下)
(3)@Autowire注入流程
@Autowire源码分析,@Autowire是怎么实现依赖注入的
二、源码分析
我们先从IOC容器启动时执行的finishBeanFactoryInitialization方法开始入口,此方法是单例Bean的创建入口,然后执行preInstantiateSingletons方法正是开始创建单例Bean。
// org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {// 省略上面代码// Instantiate all remaining (non-lazy-init) singletons.// 关键方法!完成BeanFactory的初始化finishBeanFactoryInitialization(beanFactory); // 省略下面代码
// org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {// 省略上面代码// Instantiate all remaining (non-lazy-init) singletons.// 关键方法!初始化单例的BeanbeanFactory.preInstantiateSingletons();
}
preInstantiateSingletons方法在DefaultListableBeanFactory中进行了实现。
// org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);if (bean instanceof FactoryBean) {final FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}else { // 关键方法!获取Bean的入口getBean(beanName);}}}// 省略下面代码
1、初始化Student
第一个首先是初始化我们的Student类,getBean会调用doGetBean,然后调用getSingleton方法(重点!解决循环依赖最重要的三级缓存):
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName); // 一级缓存if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName); // 二级缓存if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); // 三级缓存if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
第一次调用getSingleton方法不会获取到Bean,获取的是null,走下面的单例bean创建逻辑:
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean// 省略上面代码
// Create bean instance.
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args); // 关键方法!创建Bean,开始正式进入Bena的生命周期}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}// 省略下面代码
createBean方法会调用doCreateBean方法,doCreateBean方法会创建bean的实例,然后完成属性的初始化(@Autowire还未开始),然后会判断如果该Bean正在创建,就会调用addSingletonFactory方法:
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean// 省略上面创建bean的实例,然后完成属性的初始化(@Autowire还未开始)的代码
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}// 省略下面@Autowire的代码
我们会发现,此处对allowCircularReferences 进行了判断,假如说我们打开了循环依赖的开关,会走这个逻辑。
addSingletonFactory方法,通过传入的单例对象工厂,
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) { // 判断一级缓存如果不存在this.singletonFactories.put(beanName, singletonFactory); // 放入三级缓存this.earlySingletonObjects.remove(beanName); // 从二级缓存删掉(其实并没有)this.registeredSingletons.add(beanName); // 放入正在注册的单例Bean列表}}
}
我们注意到此时Student中的classRoom并没有被注入,但是id和name属性已经被初始化了。

对Student中的ClassRoom进行Autowire操作
我们回到doCreateBean方法,接下来会执行populateBean初始化属性值,会调用@Autowire的处理类AutowiredAnnotationBeanPostProcessor的postProcessProperties方法进行处理@Autowire注入。
// populateBean方法,此处省略部分代码PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;// 关键方法!最终调用AutowiredAnnotationBeanPostProcessor的postProcessPropertiesPropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}
}
// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs); // 关键方法}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;
}
// org.springframework.beans.factory.annotation.InjectionMetadata#inject
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {if (logger.isTraceEnabled()) {logger.trace("Processing injected element of bean '" + beanName + "': " + element);}element.inject(target, beanName, pvs); // 关键方法}}
}
inject方法此处需要自动注入的是属性,需要获取该属性的value值也就是ClassRoom(此时ClassRoom尚未初始化)。
// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
// 此处省略部分源码
try {// value就是需要获取的ClassRoomvalue = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
// org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);}else if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);}else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);}else {Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);if (result == null) {// 关键代码!会走该逻辑result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}
}
// doResolveDependency方法,省略部分源码,会调用resolveCandidate
if (instanceCandidate instanceof Class) {instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
resolveCandidate会执行beanFactory.getBean方法,又开始了对ClassRoom的getBean操作,开始处理ClassRoom。
//
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)throws BeansException {return beanFactory.getBean(beanName);
}
2、Student的自动注入ClassRoom时,又对ClassRoom的初始化
我们跳过上面的分析,来到addSingletonFactory方法:

我们发现,又将ClassRoom放入了三级缓存,此时三级缓存中有了Student和ClassRoom,但是都没有完成对ClassRoom和Student的自动注入工作。
3、ClassRoom的初始化,又执行自动注入Student的逻辑
此时,我们来到ClassRoom的populateBean属性初始化方法,仍然调用AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,来完成对Student的自动注入。
省略我们分析Student的自动注入步骤,我们继续分析,自动注入的过程仍然会调用resolveDependency方法,然后调用doResolveDependency方法,然后调用resolveCandidate方法,又通过beanFactory.getBean来获取Student。
还记得我们上面分析的,doGetBean方法中调用的getSingleton方法的逻辑,此时从三级缓存中已经能够获取到Student了,但是该Student的ClassRoom仍然是null的,但是能够获取到Studen实例,并不影响我们ClassRoom对Student的注入!
// org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.// 关键方法!Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}
从三级缓存中获取到Student后,将Student从三级缓存移出,放入二级缓存。


此时,在ClassRoom的初始化自动注入Student的过程,获取到了我们的Student(还未注入CLassRoom),在Inject方法中,继续执行会发现通过反射,将Student写入了ClassRoom的属性。
// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {value = resolvedCachedArgument(beanName, this.cachedFieldValue);}else {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(1);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();try {value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}synchronized (this) {if (!this.cached) {if (value != null || this.required) {this.cachedFieldValue = desc;registerDependentBeans(beanName, autowiredBeanNames);if (autowiredBeanNames.size() == 1) {String autowiredBeanName = autowiredBeanNames.iterator().next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());}}}else {this.cachedFieldValue = null;}this.cached = true;}}}if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value); // 通过反射写入属性}
}
此时完成了ClassRoom的初始化。
4、Student注入ClassRoom
此时ClassRoom在Student初始化过程,完成了初始化并注入Student实例,此时的Student还没有完成对ClassRoom的注入呢!
我们又回到Student的inject方法了(此时相当于是一个递归的过程)。
对ClassRoom的create的过程,会返回创建好的ClassRoom实例(初始化完成也注入完成),正好将ClassRoom注入到Student中。
此时完成对Student和ClassRoom的初始化过程。
也正式解决了Student和ClassRoom的循环依赖问题。
5、初始化完成的Bean放入一级缓存
我们再次回到doGetBean方法,这里调用了getSingleton方法,getSingleton方法最后面的finally中会调用addSingleton方法。
// Create bean instance.
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
addSingleton方法会将Bean放入一级缓存,同时删除二级缓存和三级缓存中的Bean,此时Bean初始化完成,同时也缓存完成,下次获取Bean直接从一级缓存获取即可,提高性能。
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
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);}
}
关于Bean的缓存请移步:
Spring中Bean会被缓存吗?Spring的Bean是如何被缓存的?
三、总结
Spring解决循环依赖,正式靠着这三级缓存完成的,相当于一个递归初始化的过程:

在 Spring 底层 IoC 容器 BeanFactory 中处理循环依赖的方法主要借助于以下 3 个 Map 集合:
singletonObjects(一级 Map),里面保存了所有已经初始化好的单例 BeanearlySingletonObjects(二级 Map),里面会保存从 三级 Map 获取到的正在初始化的 Bean,保存的同时会移除 三级 Map 中对应的 ObjectFactory 实现类,在完全初始化好某个 Bean 时会移除 二级 Map中对应的早期对象singletonFactories(三级 Map),里面保存了正在初始化的 Bean 对应的 ObjectFactory 实现类,调用其 getObject() 方法返回正在初始化的 Bean 对象(仅实例化还没完全初始化好)
而这三个缓存,其实也就是人们称的三级缓存,其实严格意义上并不能称为“缓存”,这三个缓存其实都是Map:

1、二级缓存的用处
此处解决循环依赖似乎并没有用到二级缓存,那么二级缓存是哪里用到的呢?
因为通过 三级 Map获取 Bean 会有相关 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 的处理,避免重复处理。
例如在循环依赖中一个 Bean 可能被多个 Bean 依赖, A -> B(也依赖 A) -> C -> A,当你获取 A 这个 Bean 时,后续 B 和 C 都要注入 A,没有上面的 二级 Map的话,三级 Map 保存的 ObjectFactory 实现类会被调用两次,会重复处理,可能出现问题。这就是为什么需要 3 个 Map,另外这样做在性能上也有所提升 。
也是为了处理AOP动态代理的问题,也是一个对象被多个对象重复依赖,导致重复创建的问题:
假如 A 需要进行 AOP,因为代理对象每次都是生成不同的对象,如果干掉第二级缓存,只有第一、三级缓存:
B 找到 A 时,直接通过三级缓存的工厂的代理对象,生成对象 A1。
C 找到 A 时,直接通过三级缓存的工厂的代理对象,生成对象 A2。
看到问题没?你通过 A 的工厂的代理对象,生成了两个不同的对象 A1 和 A2,所以为了避免这种问题的出现,我们搞个二级缓存,把 A1 存下来,下次再获取时,直接从二级缓存获取,无需再生成新的代理对象。
2、汇总流程图!

参考资料
spring如何解决循环依赖
源码深度解析,Spring 如何解决循环依赖?
极客时间《小马哥讲 Spring 核心编程思想》
相关文章:
Spring循环依赖问题,Spring是如何解决循环依赖的?
文章目录一、什么是循环依赖1、代码实例2、重要信息二、源码分析1、初始化Student对Student中的ClassRoom进行Autowire操作2、Student的自动注入ClassRoom时,又对ClassRoom的初始化3、ClassRoom的初始化,又执行自动注入Student的逻辑4、Student注入Class…...
更改SAP GUI登录界面信息
在SAP GUI的登录界面,左部输入登录信息如客户端、用户名、密码等,右部空余部分可维护一些登录信息文本,如登录的产品、客户端说明及注意事项等,此项操作详见SAP Notes 205487 – Own text on SAPGui logon screen 维护文档使用的…...
分布式微服务架构下网络通信的底层实现原理
在分布式架构中,网络通信是底层基础,没有网络,也就没有所谓的分布式架构。只有通过网络才能使得一大片机器互相协作,共同完成一件事情。 同样,在大规模的系统架构中,应用吞吐量上不去、网络存在通信延迟、我…...
进大厂必备的Java面试八股文大全(2023最新精简易懂版,八股文中的八股文)
为什么同样是跳槽,有些人薪资能翻三倍?” 最近一个粉丝发出了灵魂拷问,类似的问题我收到过很多次,身边也确实有认识的同事、朋友们有非常成功的跳槽经历和收益,先说一个典型例子: 学弟小 A 工作一年半&am…...
都说测试行业饱和了,为什么我们公司给初级测试开到了12K?
故事起因: 最近我有个刚毕业的学生问我说:我感觉现在测试行业已经饱和了,也不是说饱和了,是初级的测试根本就没有公司要,哪怕你不要工资也没公司要你,测试刚学出来,没有任何的项目经验和工作经验…...
解决Idea启动项目失败,提示Error running ‘XXXApplication‘: Command line is too long
IDEA版本为:IntelliJ IDEA 2018.2 (Ultimate Edition)一、问题描述有时当我们使用IDEA,Run/Debug一个SpringBoot项目时,可能会启动失败,并提示以下错误。Error running XXXApplication: Command line is too long. Shorten comman…...
GB/T28181-2022针对H.265、AAC的说明和技术实现
GB/T28181-2022规范说明GB/T28181-2022相对来GB/T28181-2016针对H.265、AAC的更新如下:——更改了“联网系统通信协议结构图”,媒体流通道增加了 H.265、G.722.1、AAC(见 4.3.1,2016 年版的 4.3.1)。——增加了对 H.26…...
开关电源环路稳定性分析(11)——观察法找零极点
大家好,这里是大话硬件。 这篇文章主要是分享如何用观察法直接写出补偿网络中的零极点的表达式。 在前面的文章中,我们分别整理了OTA和OPA型的补偿网络,当时有下面的结论。 针对某个固定的补偿网络,我们可以用数学的方法推导补偿…...
焕新启航,「龙蜥大讲堂」2023 年度招募来了!13 场技术分享先睹为快
龙蜥大讲堂是龙蜥推出的系列技术直播活动,邀请龙蜥社区的开发者们分享围绕龙蜥技术展开,包括但不限于内核、编译器、机密计算、容器、储存等相关技术领域。欢迎社区开发者们积极参与,共享技术盛宴。往期回顾龙蜥社区技术系列直播截至目前已举…...
推广传单制作工具
临近节日如何制作推广活动呢?没有素材制作满减活动宣传单怎么办?小编教你如何使用在线设计工具乔拓云,轻松设计商品的专属满减活动宣传单,不仅设计简单,还能自动生成活动分享链接,只需跟着小编下面的设计步…...
软件设计(十一)数据结构(上)
线性结构 线性表 线性表是n个元素的有限序列,通常记为(a1,a2....an),特点如下。 存在唯一的一个称作“第一个”的元素。存在位移的一个称作“最后一个”的元素。除了表头外,表中的每一个元素均只有唯一的直接前趋除了表尾外&…...
https协议
文章目录对称加密方案非对称加密方案对称加密方案非对称加密方案对称加密方案非对称加密方案数字证书因为HTTP是明文传输,所以会很有可能产生中间人攻击(获取并篡改传输在客户端及服务端的信息并不被人发觉),HTTPS加密应运而生。 …...
深入浅出C语言——数据在内存中的存储
文章目录一、数据类型详细介绍1. C语言中的内置类型2. 类型的基本归类:二. 整形在内存中的存储1. 原码、反码、补码2. 大小端三.浮点数存储规则一、数据类型详细介绍 1. C语言中的内置类型 C语言的内置类型有char、short、int、long、long long、float、double&…...
在 Centos 上在线安装 GitLab
作为程序员,其中一个愿望是拥有一个自己的代码存储库。在支持私有部署的代码存储库产品中,GitLab 是比较著名的了,所以今天我总结了一下在 Centos 上安装 GitLab 的过程。 依赖 基础依赖 首先,需要安装部分基础的依赖ÿ…...
模型解释性:SHAP包的使用
本篇博客介绍另一种事后可解释性方法:SHAP(SHapley Additive exPlanation)方法。 1. Shapley值理论 Shapley值是博弈论中的一个概念,通过衡量联盟中各成员对联盟总目标的贡献程度,从而根据贡献程度来进行联盟成员的利益分配,避免…...
算法训练营 day45 动态规划 0-1背包理论 分割等和子集
算法训练营 day45 动态规划 0-1背包理论 分割等和子集 0-1背包理论 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 在下面的讲解中&…...
SSM框架
1.mybatis的底层原理 本质上就是使用反射和动态代理来实现对应的映射关系 2.日志级别 3.传递参数 单个参数的传递和多个参数的传递 Emp selectOne(Param(“xingming”) String name); List selectByCondition(Param(“name”) String name,Param(“sal”) double sal); 4.#和…...
教育行业需要什么样的客服系统?
某教育公司拥有素质教育、成人教育、智慧教育等多个业务板块,日常通过电商、线上媒体、线上线下授课等方式进行业务开展和品牌宣传,取得了非常不错的成绩,受到了很多人的好评反馈。 对于这样一个教育公司,客户来源广泛࿰…...
花房集团任命新首席财务官:已跌破IPO发行价,活跃用户下滑
上市刚满2个月,花椒母公司花房集团(HK:03611)的高管就发生了变更。2023年2月12日,花房集团披露的公告显示,董事会宣布赵磊为该公司首席财务官(CFO),自2023年2月10日起生效。 据贝多…...
儿童绘本馆图书借阅租赁知识付费小程序源码交流
1.分类图书 2.书单推荐 4.会员卡次、期限购买 5.借阅时间选择 6.积分签到 7.优惠Q领取 前端uniapp开发 后端thinkphp开发 完全开源 <template> <view class"sp-section sp-index"> <!-- search --> <view class&qu…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
