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

spring源码bean生命周期篇 五 如何解决循环依赖

一.spring循环依赖1. 什么是循环依赖bean的生命周期前面的章节我们有讲解过大量的源码我们粗略的分为这几步spring扫描class获取BeanDefintionspring根据BeanDefintion实例化bean创建bean之前需要实例化对象实例化后填充原始对象中的属性依赖注入ServicepublicclassB{AutowiredpublicAa;}ServicepublicclassA{AutowiredprivateBb;}如下图所示以先创建bean a为例实例化后填充属性b时发现bean b还没被创建不在单例池(singletonObjects)中这时侯会去创建bean b在实例化对象之后需要填充属性a这时候发现spring 的单例池(singletonObjects) 中也没有bean a又需要创建bean A创建bean a 需要创建 bean b 创建bean a 需要创建 bean b不管先创建bean a 还是先创建bean b这样造成了死循环 如果不处理的话两个bean都无法创建这时侯我们该如何打破僵局呢singletonObjects中缓存的是已经经历了完整生命周期的bean对象2. 如何打破循环依赖的僵局在多数情况下在实例化得到的对象和最终的bean是同一个对象只是实例化的对象没有进行属性填充以及初始化等步骤。因此我们可以提前暴露bean的方式解决我们可以缓存实例化的得到的对象这样的步骤就如下图所示在spring源码中缓存早期bean容器名称为earlySigthonObjects1. bean a 实例化后放入存放早期bean的容器earlySigthonObjects中2. 创建bean a 的过程中需要填充属性b存放完整bean两个容器中都没有 bean b触发创建 bean b3. bean b 实例化 放入早期bean容器earlySigthonObjects中并且填充属性a拿出1步骤存放earlySigthonObjects的早期bean abean b顺利被创建4. bean a 即可创建完成以创建bean B为例,如下图所示3. Spring Aop怎么解决循环依赖在讲这个课题之前我们回忆一下bean周期在创建bean之前首先要找到对应的类并且加载实例化前执行 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation方法实例化实例化后执行InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation方法为属性赋值执行InstantiationAwareBeanPostProcessor.postProcessPropertiesInstantiationAwareBeanPostProcessor.postProcessPropertyValue后得到返回后进行applyPropertyValue方法如上图所示执行Aware接口的对应方法invokeAwareMethods初始化前执行BeanPostProcessor.postProcessBeforeInitialization方法初始化 执行初始化方法初始化后(执行BeanPostProcessor.postProcessAfterInitialization方法在初始化过程中BeanPostProcessor.postProcessAfterInitialization方法和BeanPostProcessor.postProcessBeforeInitialization中对bean可以进行处理返回新的bean代替原来的bean而AOP就是通过一个BeanPostProcessor来实现的这个BeanPostProcessor就是AnnotationAwareAspectJAutoProxyCreator它的父类是AbstractAutoProxyCreator而在设置了切面那么这个类最终就需要生成一个代理对象,在之前的章节我们也有讲过这个类。基于上面的场景想一个问题如下如果A的原始对象注入给B的属性之后A的原始对象进行了AOP产生了一个代理对象 此时就会出现对于A而言它的Bean对象其实应该是AOP之后的代理对象但实际上注入的是AOP代理之前的原始bean。如下图所示但是AOP可以说是Spring中除开IOC的另外一大功能而循环依赖又是属于IOC范畴的所以这两大功 能想要并存Spring需要特殊处理为了解决Spring Aop的循环依赖我们可以提前暴露AOP之后的代理对象来解决解决方案引入singletonFactories第三级缓存把AOP的步骤提前singletonFactories中存的是某个beanName对应的ObjectFactory在bean的生命周期中生成完原始对象之后就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口所以支持Lambda表达式() - getEarlyBeanReference(beanName, mbd,bean)getEarlyBeanReference对应的逻辑org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getEarlyBeanReferenceprotectedObjectgetEarlyBeanReference(StringbeanName,RootBeanDefinitionmbd,Objectbean){ObjectexposedObjectbean;if(!mbd.isSynthetic()hasInstantiationAwareBeanPostProcessors()){for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofSmartInstantiationAwareBeanPostProcessor){SmartInstantiationAwareBeanPostProcessoribp(SmartInstantiationAwareBeanPostProcessor)bp;exposedObjectibp.getEarlyBeanReference(exposedObject,beanName);}}}returnexposedObject;}org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.getEarlyBeanReferenceOverridepublicObjectgetEarlyBeanReference(Objectbean,StringbeanName){ObjectcacheKeygetCacheKey(bean.getClass(),beanName);//当前bean放入earlyProxyReferences代表已经进行了AOPthis.earlyProxyReferences.put(cacheKey,bean);returnwrapIfNecessary(bean,beanName,cacheKey);}protectedObjectwrapIfNecessary(Objectbean,StringbeanName,ObjectcacheKey){if(StringUtils.hasLength(beanName)this.targetSourcedBeans.contains(beanName)){returnbean;}//缓存是不需要AOP的对象直接返回if(Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))){returnbean;}if(isInfrastructureClass(bean.getClass())||shouldSkip(bean.getClass(),beanName)){this.advisedBeans.put(cacheKey,Boolean.FALSE);returnbean;}// 获取所有可以应用到当前bean的切面逻辑Object[]specificInterceptorsgetAdvicesAndAdvisorsForBean(bean.getClass(),beanName,null);if(specificInterceptors!DO_NOT_PROXY){this.advisedBeans.put(cacheKey,Boolean.TRUE);//创建代理对象ObjectproxycreateProxy(bean.getClass(),beanName,specificInterceptors,newSingletonTargetSource(bean));this.proxyTypes.put(cacheKey,proxy.getClass());returnproxy;}this.advisedBeans.put(cacheKey,Boolean.FALSE);returnbean;}publicObjectpostProcessAfterInitialization(NullableObjectbean,StringbeanName){if(bean!null){//进行AOP的不需要再次创建bean的代理对象ObjectcacheKeygetCacheKey(bean.getClass(),beanName);if(this.earlyProxyReferences.remove(cacheKey)!bean){returnwrapIfNecessary(bean,beanName,cacheKey);}}returnbean;}二.spring循环依赖源码详细解析singletonObjects缓存经过了完整生命周期的beanearlySingletonObjects缓存未经过完整生命周期的bean如果某个bean出现了循环依赖就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中这个bean如果要经过AOP那么就会把代理对象放入earlySingletonObjects中否则就是把原始对象放入earlySingletonObjects但是不管怎么样就是是代理对象代理对象所代理的原始对象也是没有经过完整生命周期的所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean。singletonFactories缓存的是一个ObjectFactory也就是一个Lambda表达式。在每个Bean 的生成过程中经过实例化得到一个原始对象后都会提前基于原始对象暴露一个Lambda表达 式并保存到三级缓存中这个Lambda表达式可能用到也可能用不到如果当前Bean没有出 现循环依赖那么这个Lambda表达式没用当前bean按照自己的生命周期正常执行执行完后 直接把当前bean放入singletonObjects中如果当前bean在依赖注入时发现出现了循环依赖 当前正在创建的bean被其他bean依赖了则从三级缓存中拿到Lambda表达式并执行 Lambda表达式得到一个对象并把得到的对象放入二级缓存(如果当前Bean需要AOP那么 执行lambda表达式得到就是对应的代理对象如果无需AOP则直接得到一个原始对象)。4. 其实还要一个缓存就是earlyProxyReferences它用来记录某个原始对象是否进行过AOP 了下图是先解决bean a 和bean b循环依赖的证据 先创建a的流程如图所示getBean(String name)getBean调用了两个getSingleton方法入参数只有一个beanName当前方法是去看看spring容器中有没有当前名为beanName的bean如果没有要判断当前bean是否在标记正在创建如果标记为创建才会去找二级缓存alowEarlyReference参数默认为true并二级缓存为null才会找三级缓存获取早期bean入参有两个参数一个为beanName一个为ObjectFactory主要是创建bean并把当前bean标记为正在创建处理bean A和Bean B循环依赖的流程,以先创建Bean A为例整个逻辑都在getBean(a)方法中1. 调用getSingleton(a)从一二三级缓存中找bean A这时候肯定是没有的,调用第二个getSingleton方法创建bean A并标记bean A正在创建2.创建bean A的过程中需要实例化A对象并根据实例化的对象生成ObjectFactory放入第三缓存3.创建bean A的过程中需要填充属性b 触发调用方法getBean(b)获取bean b4.调用getSingleton(b),一二三级缓存中找bean A这时候肯定是没有的这时候调用第二个getSingleton方法创建Bean B并标记bean B正在创建5.创建bean B的过程中需要实例化B对象并根据实例化的对象生成ObjectFactory放入第三缓存6.创建bean B的过程中需要填充属性a这个时候会调用getBean(a)方法但是与第一步不同的是第三缓存中可获得早期的bean A对象给属性a赋值7.创建bean B成功后给bean A 的属性b赋值bean A也可以创建成功org.springframework.beans.factory.support.AbstractBeanFactoryTTgetBean(Stringname){ObjectbeangetSingleton(beanName);if(beannull){//alreadyCreated.add(beanName) alreadyCreated存储的已经被创建过一次的beanNamemarkBeanAsCreated(beanName);//获取bean定义finalRootBeanDefinitionmbdgetMergedLocalBeanDefinition(beanName);if(mbd.isSingleton()){//创建bean入参 String beanNameObjectFactory factorysharedInstancegetSingleton(beanName,()-{returncreateBean(beanName,mbd,args);}}}protectedvoidmarkBeanAsCreated(StringbeanName){if(!this.alreadyCreated.contains(beanName)){synchronized(this.mergedBeanDefinitions){if(!this.alreadyCreated.contains(beanName)){// Let the bean definition get re-merged now that were actually creating// the bean... just in case some of its metadata changed in the meantime.clearMergedBeanDefinition(beanName);this.alreadyCreated.add(beanName);}}}}getSingleton(String name)org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(beanName)代码经过简化publicObjectgetSingleton(StringbeanName){returngetSingleton(beanName,true);}protectedObjectgetSingleton(StringbeanName,booleanallowEarlyReference){//从spring一级缓存中获取beanObjectsingletonObjectthis.singletonObjects.get(beanName);//以及缓存为空从第二级缓存中获取beanif(singletonObjectnullisSingletonCurrentlyInCreation(beanName)){synchronized(this.singletonObjects){singletonObjectthis.earlySingletonObjects.get(beanName);//二级缓存为null如果allowEarlyReference为true从三级缓存拿出singletonFactory并执行Object方法其返回结果放入二级缓存并从三级缓存去掉if(singletonObjectnullallowEarlyReference){ObjectFactory?singletonFactorythis.singletonFactories.get(beanName);if(singletonFactory!null){singletonObjectsingletonFactory.getObject();this.earlySingletonObjects.put(beanName,singletonObject);this.singletonFactories.remove(beanName);}}}}returnsingletonObject;}getSingleton(String name, ObjectFactory? singletonFactory)org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(beanName,ObjectFactory代码经过简化publicObjectgetSingleton(StringbeanName,ObjectFactory?singletonFactory){synchronized(this.singletonObjects){ObjectsingletonObjectthis.singletonObjects.get(beanName);if(singletonObjectnull){//singletonsCurrentlyInCreation.add(beanName)beforeSingletonCreation(beanName);booleannewSingletonfalse;try{//结合上面的代码。实际上调用的是createBean(beanName, mbd, args)singletonObjectsingletonFactory.getObject();newSingletontrue;}finally{//singletonsCurrentlyInCreation.remove(beanName)afterSingletonCreation(beanName);}if(newSingleton){//singletonObjects.put(beanName, singletonObject);addSingleton(beanName,singletonObject);}}returnsingletonObject;}}protectedvoidaddSingleton(StringbeanName,ObjectsingletonObject){synchronized(this.singletonObjects){this.singletonObjects.put(beanName,singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}接下来调用create方法org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBeanprotectedObjectdoCreateBean(StringbeanName,RootBeanDefinitionmbd,NullableObject[]args)throwsBeanCreationException{BeanWrapperinstanceWrappernull;if(instanceWrappernull){instanceWrappercreateBeanInstance(beanName,mbd,args);finalObjectbeaninstanceWrapper.getWrappedInstance();Class?beanTypeinstanceWrapper.getWrappedClass();applyMergedBeanDefinitionPostProcessors(mbd,beanType,beanName);//isSingletonCurrentlyInCreationsingletonsCurrentlyInCreation.contains(beanName)booleanearlySingletonExposure(mbd.isSingleton()this.allowCircularReferencesisSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){//放入第三级缓存addSingletonFactory(beanName,()-getEarlyBeanReference(beanName,mbd,bean));}ObjectexposedObjectbean;//填充bean的属性populateBean(beanName,mbd,instanceWrapper);//初始化beanexposedObjectinitializeBean(beanName,exposedObject,mbd);if(earlySingletonExposure){//从第二级缓存中获取当前beanObjectearlySingletonReferencegetSingleton(beanName,false);if(earlySingletonReference!null){if(exposedObjectbean){exposedObjectearlySingletonReference;}elseif(!this.allowRawInjectionDespiteWrappinghasDependentBean(beanName)){//this.dependentBeanMap.get(beanName)在bean是初始化后进行属性填充之后会注册依赖和被依赖的关系往dependentBeanMap新增依赖关系//以Demo 创建Bean A 为例beanName为a需要注入的属性值为bean b所以获取到的dependentBeans为bString[]dependentBeansgetDependentBeans(beanName);SetStringactualDependentBeansnewLinkedHashSet(dependentBeans.length);for(StringdependentBean:dependentBeans){//并且bean b 至少触发创建过// bean b已经开始创建那么bean b注入的早期的bean对象而不是经过整个生命周期的bean对象if(!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)){actualDependentBeans.add(dependentBean);}}if(!actualDependentBeans.isEmpty()){thrownewBeanCurrentlyInCreationException(beanName,Bean with name beanName has been injected into other beans [StringUtils.collectionToCommaDelimitedString(actualDependentBeans)] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using getBeanNamesOfType with the allowEagerInit flag turned off, for example.);}}}}}}}protectedbooleanremoveSingletonIfCreatedForTypeCheckOnly(StringbeanName){if(!this.alreadyCreated.contains(beanName)){removeSingleton(beanName);returntrue;}else{returnfalse;}三.spring循环依赖问题的再度解决在初始化过程中BeanPostProcessor.postProcessAfterInitialization方法和BeanPostProcessor.postProcessBeforeInitialization中对bean可以进行处理返回新的bean代替原来的bean,上述我们只处理了AOP有关的基于BeanPostProcessor.postProcessAfterInitialization并不代表所有的BeanPostProcessor.postProcessAfterInitialization的都没有循环依赖问题这里我们先上demo根据上述讲解的源码进行分析ServicepublicclassA{AutowiredprivateBb;}ServicepublicclassB{AutowiredpublicAa;}ComponentpublicclassBeanPostProcessorDimplementsBeanPostProcessor{OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName){if(beaninstanceofA){AanewA();BeanUtils.copyProperties(bean,A);returna;}returnbean;}}先创建bean a先创建bean b以当前demo来说先创建bean a会抛出BeanCurrentlyInCreationException而先创建bean b却不会所以可能会出现开发环境不同bean 的创建顺序不同导致有的同事可以正常启动项目而有的会抛出BeanCurrentlyInCreationException终止项目运行。解决方法加上lazy注解具体可查阅lazy注解有关资料这里就不讲述了

相关文章:

spring源码bean生命周期篇 五 如何解决循环依赖

一.spring循环依赖 1. 什么是循环依赖? bean的生命周期前面的章节我们有讲解过大量的源码,我们粗略的分为这几步 spring扫描class获取BeanDefintionspring根据BeanDefintion实例化bean创建bean之前需要实例化对象,实例化后填充原始对象中的属…...

容器镜像加速实战:3种方案彻底解决国内拉取难题

容器镜像加速实战:3种方案彻底解决国内拉取难题 【免费下载链接】public-image-mirror 很多镜像都在国外。比如 gcr 。国内下载很慢,需要加速。致力于提供连接全世界的稳定可靠安全的容器镜像服务。 项目地址: https://gitcode.com/GitHub_Trending/pu…...

filer.js vs 传统文件API:为什么这个类UNIX封装库能提升3倍开发效率?

filer.js vs 传统文件API:为什么这个类UNIX封装库能提升3倍开发效率? 【免费下载链接】filer.js A wrapper library for the HTML5 Filesystem API what reuses UNIX commands (cp, mv, ls) for its API. 项目地址: https://gitcode.com/gh_mirrors/fi…...

TV Bro电视浏览器:终极Android电视网页浏览解决方案,让大屏上网变得简单高效

TV Bro电视浏览器:终极Android电视网页浏览解决方案,让大屏上网变得简单高效 【免费下载链接】tv-bro Simple web browser for android optimized to use with TV remote 项目地址: https://gitcode.com/gh_mirrors/tv/tv-bro 您是否曾尝试在智能…...

免费开源AMD Ryzen调试工具终极指南:从零掌握SMUDebugTool完整使用教程

免费开源AMD Ryzen调试工具终极指南:从零掌握SMUDebugTool完整使用教程 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目…...

TQVaultAE:分布式游戏资产管理系统的架构设计与技术实现

TQVaultAE:分布式游戏资产管理系统的架构设计与技术实现 【免费下载链接】TQVaultAE Extra bank space for Titan Quest Anniversary Edition 项目地址: https://gitcode.com/gh_mirrors/tq/TQVaultAE TQVaultAE作为一个开源的游戏资产管理工具,通…...

C166架构中idaata变量存储类别变更的解析与优化

1. 问题现象与背景解析最近在Keil C166开发环境中遇到了一个有趣的编译警告,代码看起来非常简单:void main(void) {int i;int j;int idata asdf; // 触发警告的变量声明i 100;j 1000;asdf i j; }编译时会出现如下警告:*** WARNING 189 I…...

3分钟掌握md2pdf:离线Markdown转PDF的终极指南

3分钟掌握md2pdf:离线Markdown转PDF的终极指南 【免费下载链接】md2pdf Offline markdown to pdf, choose -> edit -> transform 🥂 项目地址: https://gitcode.com/gh_mirrors/md/md2pdf 你是否经常需要将Markdown文档转换为PDF格式&#…...

Schwinger模型与轴子动力学:量子模拟中的强CP问题研究

1. Schwinger模型与强CP问题概述Schwinger模型作为11维的量子电动力学(QED),长期以来被视为研究规范场论非微扰效应的理想试验场。这个看似简单的理论却蕴含着丰富的物理内涵:轴向反常、非平庸真空结构以及拓扑θ项等特性,使其成为理解高维规…...

WhatsNew vs 其他更新提示库:为什么它是iOS开发者的首选

WhatsNew vs 其他更新提示库:为什么它是iOS开发者的首选 【免费下载链接】WhatsNew Showcase new features after an app update similar to Pages, Numbers and Keynote. 项目地址: https://gitcode.com/gh_mirrors/wh/WhatsNew 在iOS应用开发中&#xff0c…...

【紧急预警】你还在裸用ChatGPT写生产代码?这4类高危漏洞已致37家团队线上事故

更多请点击: https://kaifayun.com 第一章:ChatGPT编程辅助的底层风险认知与责任边界界定 当开发者将ChatGPT嵌入编码工作流时,其输出常被误认为具备工程级可靠性。然而,模型生成的代码本质上是统计拟合结果,不具备形…...

NotebookLM实验结果可信吗?(P值阈值设定与多重检验校正全拆解)

更多请点击: https://codechina.net 第一章:NotebookLM实验结果可信吗?(P值阈值设定与多重检验校正全拆解) NotebookLM 作为基于文档的AI实验助手,其内置的“实验模式”常用于自动比对不同提示策略或模型配…...

暗黑破坏神2终极角色编辑器:打造完美角色的完整指南

暗黑破坏神2终极角色编辑器:打造完美角色的完整指南 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 想要在暗黑破坏神2中体验完美的角色构建吗?厌倦了重复刷装备的枯燥过程…...

【NotebookLM视频转文字实战指南】:20年AI工程师亲测的5大避坑技巧与98.7%准确率实现路径

更多请点击: https://intelliparadigm.com 第一章:NotebookLM视频转文字的核心原理与能力边界 NotebookLM 的视频转文字功能并非直接处理原始视频流,而是依赖 Google Cloud Speech-to-Text API 的增强版语音识别管道,并结合 YouT…...

Keil MDK中Flash算法RAM配置的DWORD对齐问题解析

1. 问题现象与背景解析当使用Keil MDK开发环境配合J-LINK或ULINK系列调试器时,在Flash Download配置选项卡中设置Flash算法RAM大小时,可能会遇到"Invalid Number Error: Number must be DWORD Aligned"的错误提示。这个错误通常发生在以下场景…...

Finch微服务部署:基于Finagle的生产环境最佳实践

Finch微服务部署:基于Finagle的生产环境最佳实践 【免费下载链接】finch Scala combinator library for building Finagle HTTP services 项目地址: https://gitcode.com/gh_mirrors/fin/finch Finch是一个基于Scala的组合器库,专为构建Finagle H…...

深度解析ZXing.Net:.NET生态中的企业级条码识别与生成解决方案

深度解析ZXing.Net:.NET生态中的企业级条码识别与生成解决方案 【免费下载链接】ZXing.Net .Net port of the original java-based barcode reader and generator library zxing 项目地址: https://gitcode.com/gh_mirrors/zx/ZXing.Net ZXing.Net作为Java版…...

超参数调优效率提升300%:Advisor与传统调参工具深度对比

超参数调优效率提升300%:Advisor与传统调参工具深度对比 【免费下载链接】advisor Open-source implementation of Google Vizier for hyper parameters tuning 项目地址: https://gitcode.com/gh_mirrors/ad/advisor 在机器学习模型开发中,超参数…...

如何快速完成AI智能图像分层:layerdivider完整使用指南

如何快速完成AI智能图像分层:layerdivider完整使用指南 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 你是否曾经面对复杂的插画设计&#x…...

FlashAttention 为什么对序列长度这么“敏感”?

FlashAttention 为什么对序列长度这么“敏感”? 很多朋友在昇腾 NPU 上测 FlashAttention 性能时,都会遇到一个让人挠头的现象:为什么 seq_len512 时,FlashAttention 比标准 Attention 还慢?非要等到 seq_len2048 才开…...

如何快速安装HS2-HF Patch:HoneySelect2终极汉化与MOD整合完整指南

如何快速安装HS2-HF Patch:HoneySelect2终极汉化与MOD整合完整指南 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch HS2-HF Patch是HoneySelect2玩家…...

AI-HF_Patch终极指南:如何为AI-Shoujo游戏安装完整增强补丁

AI-HF_Patch终极指南:如何为AI-Shoujo游戏安装完整增强补丁 【免费下载链接】AI-HF_Patch Automatically translate, uncensor and update AI-Shoujo! 项目地址: https://gitcode.com/gh_mirrors/ai/AI-HF_Patch 你是否在寻找一款能够彻底提升AI-Shoujo游戏体…...

在Taotoken模型广场中根据任务与预算选择合适的AI模型

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在Taotoken模型广场中根据任务与预算选择合适的AI模型 当你准备将大模型能力集成到自己的应用或工作流中时,面对市场上…...

【VMware虚拟机】Linux下ubuntu连接网络详细讲解!

原理讲解 window上网需要网络适配器,通过家用路由器下发WLAN,自分配ip地址,连接即用 linux同理:在VMware虚拟机上需要”虚拟路由器“。对应为虚拟网络编辑器 1.打开虚拟网络编辑器 2.点击NAT,NAT模式和DHCP必须选上…...

LLM 本地部署框架 vLLM 和 LMDeploy

1. 安装vLLM的环境 1.1 安装要求 1. vLLM 包含预编译的 C 和 CUDA (12.8) 二进制文件。 2. 要求: 操作系统: LinuxPython: 3.9 -- 3.12 # (实测:推荐安装3.10以上版本)GPU: 计算能力 7.0 或更高 (例如, V100, T4, RTX20xx, A100, L4, H100 等…...

液冷及前沿散热技术的理论分析:从宏观系统到芯片级散热的范式跃迁

🎓作者简介:科技自媒体优质创作者 🌐个人主页:莱歌数字-CSDN博客 211、985硕士,从业16年 从事结构设计、热设计、售前、产品设计、项目管理等工作,涉足消费电子、新能源、医疗设备、制药信息化、核工业等…...

读写场景下的锁选择策略

数据库加锁场景及锁类型选择指南 数据库加锁的核心目标是解决并发事务下的数据一致性问题,防止出现脏读、不可重复读、幻读等异常。锁的选择与应用场景紧密相关,主要取决于操作类型、数据访问模式、事务隔离级别以及数据库引擎的特性。以下通过具体场景…...

3步掌握Windows字体优化:Better ClearType Tuner完整使用指南

3步掌握Windows字体优化:Better ClearType Tuner完整使用指南 【免费下载链接】BetterClearTypeTuner A better way to configure ClearType font smoothing on Windows 10. 项目地址: https://gitcode.com/gh_mirrors/be/BetterClearTypeTuner 你是否曾经在…...

歌词滚动姬:5分钟掌握专业级歌词制作的艺术

歌词滚动姬:5分钟掌握专业级歌词制作的艺术 【免费下载链接】lrc-maker 歌词滚动姬|可能是你所能见到的最好用的歌词制作工具 项目地址: https://gitcode.com/gh_mirrors/lr/lrc-maker 歌词滚动姬(LRC Maker)是一款完全免费…...

3步解锁CPU隐藏性能:CPUDoc智能调度实战指南

3步解锁CPU隐藏性能:CPUDoc智能调度实战指南 【免费下载链接】CPUDoc 项目地址: https://gitcode.com/gh_mirrors/cp/CPUDoc 对于追求极致性能的PC用户来说,CPU调度优化一直是个技术难题。传统方法要么过于复杂,要么效果有限。CPUDoc…...