【微服务】spring 控制bean加载顺序使用详解
目录
一、前言
二、使用@order注解控制顺序
2.1 @order 注解使用示例
2.2 order注解顺序失效问题
2.2.1 @order失效问题解决办法
2.3 实现Ordered接口
三、使用@dependon注解控制顺序
四、AutoConfiguration注解控制bean加载顺序
4.1 @AutoConfigureBefore 操作演示
4.2 @AutoConfigureOrder 操作演示
4.3 源码解读与分析
五、自定义ApplicationContextInitializer
5.1 ApplicationContextInitializer介绍
5.2 ApplicationContextInitializer使用
5.3 ApplicationContextInitializer控制加载顺序
六、使用场景
6.1 解决bean的依赖关系
6.2 设置某些配置类的优先级最高
6.3 依赖传递
七、写在文末
一、前言
在使用spring框架开发过程中,可能会遇到下面的情况:
- 某个bean被另一个bean依赖,也就是bean-b的创建必须依赖bean-a;
- 某个bean被很多其他bean依赖,比如bean-a初始化完成后,其他bean需要依靠bean-a初始化自己的业务;
- ...
类似这样的场景还有很多,总结来说,这就涉及到bean的加载顺序问题,如何解决呢?下面列举出几种常用的解决方案。
二、使用@order注解控制顺序
@order注解是spring-core包下的一个注解,@Order的作用是定义Spring IOC容器中Bean的执行顺序的优先级(这里的顺序也可以理解为存放到容器中的先后顺序)。开发过程当中有时候经常会出现配置依赖关系,例如注入A对象使用了@ConditionalOnBean(B.class),意思是要求容器当中必须存在B.class的实例的时候,才会进行注入A。这时候我们就必须保证B对象在注入A对象前进行注入。
2.1 @order 注解使用示例
有如下两个类,Demo1和Demo2,分别在类上添加@Order注解
@Component
@Order(1)
public class Demo1 {@Beanpublic UserService serviceA(){System.out.println("serviceA 执行");return new UserService();}
}
两个类各自创建一个UserService的bean
@Component
@Order(2)
public class Demo2 {@Beanpublic UserService serviceB(){System.out.println("serviceB 执行");return new UserService();}
}
运行下面的代码,观察测试效果
public class OrderTest {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanScanConfig.class);String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for(String beanName : beanDefinitionNames){System.out.println(beanName);}}
}
可以看到,order数字更小的demo1这个类的bean比demo2的bean要先创建出来;
2.2 order注解顺序失效问题
上面通过在类上添加@order,可以控制类创建的bean的顺序,事实真的如此吗?如果此时,我们将demo1的order调大,demo2的order数字调小,你将看到的效果仍然是serviceA先输出,这就是order注解顺序失效问题。关于这个问题,spring官方文档也有说明,翻译出来意思如下:
您可以在目标类级别和@Bean方法上声明@Order注释,可能针对的是单个bean定义(如果多个定义使用同一个bean类)。@Order值可能会影响注入点的优先级,但请注意,它们不会影响单例启动顺序,这是由依赖关系和@DependsOn声明确定的正交关注。
2.2.1 @order失效问题解决办法
解决方式1
将需要控制bean的创建顺序的两个类放到不同的包路径下
再次运行测试代码,当demo2的order数字更小的情况下,就能看到预期的demo2创建的bean优先输出的效果了
解决方式2
重新修改类的命名,比如将demo2的类修改为ADemo,让ADemo这个类在同一个包路径下置于Demo2之前
这样修改后,再次运行测试代码观察效果,此时ADemo就优先Demo1了
2.3 实现Ordered接口
使用spring提供的ordered接口,只需要自定义的类实现这个接口,并重写里面的getOrder方法,在getOrder方法的返回值中,数值越小,优先级越高,如下自定义两个被sppring管理的类,实现Ordered接口
@Component
public class Listener1 implements Ordered {@Beanpublic UserService userService1(){System.out.println("userService1 bean");return new UserService();}@Overridepublic int getOrder() {return 100;}
}
@Component
public class Listener2 implements Ordered {@Beanpublic UserService userService2(){System.out.println("userService2 bean");return new UserService();}@Overridepublic int getOrder() {return 10;}
}
与ordered接口类似的还有PriorityOrdered,用法大同小异,有兴趣的同学可以进一步挖掘。
三、使用@dependon注解控制顺序
当某个bean-a的创建或加载需要明确依赖另一个bean-b的时候可以考虑使用dependon注解,换言之,bean-b要优先于bean-a创建;
如下有两个类,OrderDemo1和OrderDemo2
@Component
@DependsOn("orderDemo2")
public class OrderDemo1 {public OrderDemo1(){System.out.println("OrderDemo1 ...");}}
其中OrderDemo1的创建依赖OrderDemo2
@Component
public class OrderDemo2 {public OrderDemo2(){System.out.println("OrderDemo2 ...");}}
运行程序,可以看到OrderDemo2优先于OrderDemo1创建
由于这种方法是通过bean的名字(字符串)来控制顺序的,如果改了bean的类名,很可能就会忘记来改所有用到它的注解,那就问题大了,所以这种方式一般不推荐使用,但是也不失为控制bean创建顺序的方式。
四、AutoConfiguration注解控制bean加载顺序
Spring Boot会根据当前容器内的情况来动态的判断自动配置类的配置顺序,它给我们提供了@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder 三大注解:
@AutoConfigureBefore
用在自动配置类上面,表示该自动配置类需要在另外指定的自动配置类配置完之后配置加载
@AutoConfigureAfter
用在自动配置类上面,表示该自动配置类需要在另外指定的自动配置类配置完之前配置加载
@AutoConfigureOrder
确定配置加载的优先级顺序,表示绝对顺序(数字越小,优先顺序越高)
4.1 @AutoConfigureBefore 操作演示
定义两个配置类DbConfig1与DbConfig2,代码如下
@Configuration
public class DbConfig1 {public DbConfig1(){System.out.println("DbConfig1 构建方法...");}}
其中,DbConfig2类上使用了注解AutoConfigureBefore,期望DbConfig2比DbConfig1先加载
@Configuration
@AutoConfigureBefore(DbConfig1.class)
public class DbConfig2 {public DbConfig2(){System.out.println("DbConfig2 构建方法...");}}
到这里还没有完事,还需在resources目录下,将DbConfig1与DbConfig2配置到spring.factories文件中进行自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.congge.config.DbConfig2,\
com.congge.config.DbConfig1
运行上面的代码,观察控制台输出,可以看到DbConfig2比DbConfig1先加载
我们还可以将注解换成AutoConfigureAfter,看看是什么效果
@Configuration
@AutoConfigureAfter(DbConfig1.class)
public class DbConfig2 {public DbConfig2(){System.out.println("DbConfig2 构建方法...");}}
再次运行代码,可以看到这次就是DbConfig1先加载
4.2 @AutoConfigureOrder 操作演示
也可以使用AutoConfigureOrder 注解来控制不同配置类的加载顺序,数字越小,优先顺序越高,添加两个配置类,分别使用该注解进行标注
@Configuration
@AutoConfigureOrder(2)
public class OrderConfig1 {public OrderConfig1(){System.out.println("OrderConfig1 加载...");}
}
OrderConfig2中的数值更大,理论上OrderConfig1优先加载
@Configuration
@AutoConfigureOrder(7)
public class OrderConfig2 {public OrderConfig2(){System.out.println("OrderConfig2 加载...");}
}
同样,使用AutoConfigureOrder控制配置类的加载顺序也需要将其配置到spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.congge.config.DbConfig2,\
com.congge.config.DbConfig1,\
com.congge.config.OrderConfig1,\
com.congge.config.OrderConfig2
运行上面的代码,看到如下效果,说明OrderConfig1优于OrderConfig2加载
4.3 源码解读与分析
最关键的代码在AutoConfigurationImportSelector这个类中,spring在bean装载过程中,将自动配置类从spring.factories加载出来之后会根据条件排序,在selectImports()方法中最后一行代码进行排序,如下
跟进sortAutoConfigurations这个方法,最终的排序逻辑来到下面这个getInPriorityOrder方法中;
在这个方法中,关于排序是分成3种方式完成的
- 先按字母排序;
- 再按照@AutoConfigureOrder进行排序;
- 最后按照 @AutoConfigureBefore和@AutoConfigureAfter排序;
而从配置的顺序不难发现,最终决定权还是在@AutoConfigureAfter、@AutoConfigureBefore这两个注解。
五、自定义ApplicationContextInitializer
对spring的bean的生命周期和加载过程熟悉的同学,想必了解到,容器启动过程中,通过解析xml文件中的配置生成bean或者通过扫描路径并解析得到bean,这些bean最终会统一保存在一个容器中被spring管理。既然如此,开发人员就可以人工的干预这个bean的解析过程,通过spring提供的相关扩展点,改变bean在容器中的位置就可以达到控制bean的顺序了,下面看具体的操作流程。
5.1 ApplicationContextInitializer介绍
先看spring官网的介绍:
翻译过来大概的意思如下
- 用于在spring容器刷新之前初始化Spring ConfigurableApplicationContext的回调接口。(剪短说就是在容器刷新之前调用该类的 initialize 方法。并将 ConfigurableApplicationContext 类的实例传递给该方法);
- 通常用于需要对应用程序上下文进行编程初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件等;
- 可排序的(实现Ordered接口,或者添加@Order注解);
5.2 ApplicationContextInitializer使用
新建一个类 MyAppInitializer并实现 ApplicationContextInitializer 接口
public class MyAppInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("-----MyAppInitializer initialize-----");}
}
然后在启动类中添加进去
@SpringBootApplication
public class BootApp {public static void main(String[] args) {SpringApplication application = new SpringApplication(BootApp.class);application.addInitializers(new MyAppInitializer());application.run(args);}
}
运行上面的main程序,在启动时看到控制台输出下面的信息
从上面的介绍了解到,实现ApplicationContextInitializer该接口,将会在容器刷新之前进行加载,对应到spring源码中,即refreshContext(context)方法,了解spring的bean的加载流程的同学应该知道,refreshContext里面就是调用AbstractApplicationContext的refresh的方法,其主要功能就是注册spring容器里面的bean,以及对bean的处理还有广播等功能,简而言之,就是在这个方法中,完成系统中bean的创建和存储的一系列过程。
基于此,按照上面的效果来看,实现ApplicationContextInitializer该接口之后,其类的加载时机要更加靠前,于是就可以借助这个特性,在bean还没有真正创建出来之前,人为的干预bean的创建顺序,将指定的类注册到spring上下文中。
5.3 ApplicationContextInitializer控制加载顺序
自定义一个类实现ApplicationContextInitializer接口
public class MyAppInitializer implements ApplicationContextInitializer {@Overridepublic void initialize(ConfigurableApplicationContext applicationContext) {System.out.println("-----MyAppInitializer initialize-----");applicationContext.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());}
}
自定义一个类实现接口BeanDefinitionRegistryPostProcessor,重写postProcessBeanDefinitionRegistry方法,手动注册需要控制加载到容器中顺序的类,注意,按照上面的流程操作完成之后,对于你要控制的配置类,就不要使用相关的注解标注了,比如下面要控制的是OrderService这个类的顺序在最前面。
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();beanDefinition.setBeanClass(OrderService.class);beanDefinitionRegistry.registerBeanDefinition("orderService",beanDefinition);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {}
}
将MyAppInitializer配置到spring.factories文件中
org.springframework.context.ApplicationContextInitializer=\
com.congge.config.MyAppInitializer
运行main程序,观察控制台输出效果如下,说明OrderService不仅被容器管理,而且顺序在最前面,这就达到了控制bean的顺序的目的。
六、使用场景
到这里,再来看文章开头提出的问题,为什么需要控制spring中bean的加载顺序呢?关于这个问题,这里提出下面的几种常用的场景。
6.1 解决bean的依赖关系
比如在初始化时,配置类B需要用到配置类A中的某些属性,配置类C需要用到B中的某些属性,在这种级联依赖的情形下,为了避免启动过程中依赖的问题导致启动失败,就可以通过控制配置bean的顺序来解决。
6.2 设置某些配置类的优先级最高
比如说,我们有一个商品管理的配置类,需要在项目启动的时候,完成从数据库的数据写入到redis缓存中,并且定时刷新商品数据,并且该类还提供了一个对外访问的static方法,这种场景下,由于对外提供了static方法,其他类可以直接调用它的方法,如果不是最先加载的话,当请求获取商品数据时,商品还没有加载完成,那就就会出现问题。因此就需要保证这个配置bean最先被加载。
6.3 依赖传递
当程序中需要对接口的参数进行不同梯度的校验和拦截时,一个常见的做法就是利用AOP,减少对主业务流程的干扰,在这种情况下,如果在A类的AOP逻辑处理完成之后继续传递到下一级B的AOP中进行处理,这就需要控制不同的AOP类的执行顺序,这时就需要控制不同AOP的执行顺序。
七、写在文末
在某些特殊的业务场景下,合理控制bean的加载顺序可以帮助我们解决很多复杂的业务需求,同时也可以作为spring提供的一种功能扩展点进行使用,在spring体系中具有重要的作用,本篇到此结束,感谢观看。
相关文章:

【微服务】spring 控制bean加载顺序使用详解
目录 一、前言 二、使用order注解控制顺序 2.1 order 注解使用示例 2.2 order注解顺序失效问题 2.2.1 order失效问题解决办法 2.3 实现Ordered接口 三、使用dependon注解控制顺序 四、AutoConfiguration注解控制bean加载顺序 4.1 AutoConfigureBefore 操作演示 4.2 A…...

python-切换镜像源和使用PyCharm进行第三方开源包安装
文章目录 前言python-切换镜像源和使用PyCharm进行第三方开源包安装1. 切换镜像源2. 使用PyCharm进行第三方开源包安装 前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。 而且听说点赞的人每…...

tp6 + swagger 配置文档接口
ThinkPHP 6.0 运行环境要求PHP7.2,兼容PHP8.1 安装 composer create-project topthink/think tp 6.0.*如果需要更新框架使用 composer update topthink/framework文档 完全开发手册 swagger 文档 注解文档 安装包 composer require zircote/swagger-php 引用…...

试图一文彻底讲清 “精准测试”
在软件测试中,我们常常碰到两个基本问题(困难): 很难保障无漏测:我们做了大量测试,但不清楚测得怎样,对软件上线后会不会出问题,没有信心; 选择待执行的测试用例&#…...

Visual Studio 删除行尾空格
1.CtrlH 打开替换窗口(注意选择合适的查找范围) VS2010: VS2017、VS2022: 2.复制下面正则表达式到上面的选择窗口(注意前面有一个空格): VS2010: $ VS2017、VS2022: $ 3.下面的替换窗口不写入 VS2010: VS2017、VS2022: 4.点选“正则表达式…...

LeetCode_BFS_中等_1926.迷宫中离入口最近的出口
目录 1.题目2.思路3.代码实现(Java) 1.题目 给你一个 m x n 的迷宫矩阵 maze (下标从 0 开始),矩阵中有空格子(用 ‘.’ 表示)和墙(用 ‘’ 表示)。同时给你迷宫的入口 …...

开源Windows12网页版HTML源码
开源Windows12网页版HTML源码,无需安装就能用的Win12网页版来了Windows12概念版(PoweredbyPowerPoint)后深受启发,于是通过使用HTML、CSS、js等技术做了这样一个模拟板的Windows12系统,并已发布至github进行开源。 这…...
vscode中使用指定路径下的cmake
在 Visual Studio Code 中指定自定义的 CMake 路径,你可以通过以下步骤来实现: 打开你的 CMake 项目所在的文件夹,在 Visual Studio Code 中。 在项目文件夹中,创建一个名为 .vscode 的文件夹,如果它还不存在。 在 .…...

复杂度分析
文章目录 如何分析、统计算法的执行效率和资源消耗?为什么需要复杂度分析?测试结果非常依赖测试环境测试结果受数据规模的影响很大 大O复杂度表示法时间复杂度分析只关注循环次数最多的一段代码加法法则:总复杂度等于量级最大的那段代码的复杂…...
Linux安装jrockit-jdk1.6.0_29-R28.2.0-4.1.0-linux-x64
下载软件:jrockit-jdk1.6.0_29-R28.2.0-4.1.0-linux-x64.bin 执行安装 ./jrockit-jdk1.6.0_29-R28.2.0-4.1.0-linux-x64.bin 安装提示,一路next,注意第二步修改安装的路径,请修改成: <------------------------ O…...

7.2 怎样定义函数
7.2.1 为什么要定义函数 主要内容: 为什么要定义函数 C语言要求所有在程序中用到的函数必须“先定义,后使用”。这是因为在调用一个函数之前,编译系统需要知道这个函数的名字、返回值类型、功能以及参数的个数与类型。如果没有事先定义&…...
Chrome扩展V2到V3的变化
Chrome扩展manifest V3变化、升级迁移指南_chrome_ZK645945-华为云开发者联盟 (csdn.net) 1.background //V2 "background": "background.js"//V3 "background": {"service_worker": "background.js"} 2.executeScript …...
lock、tryLock、lockInterruptibly有什么区别?
lock、tryLock 和 lockInterruptibly 都是用于线程同步的方法,但它们有不同的行为和用途: lock() 方法:lock() 方法是 Java 中 Lock 接口定义的一部分,它用于获取锁并阻塞当前线程,直到锁可用为止。如果锁当前被其他线程占用,lock() 方法会导致当前线程阻塞,直到锁被释放…...

mysql面试题5:索引、主键、唯一索引、联合索引的区别?什么情况下设置了索引但无法使用?并且举例说明
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说索引、主键、唯一索引、联合索引的区别? 索引、主键、唯一索引和联合索引是数据库中常用的索引类型,它们有以下区别: 索引:索引是一种数…...

数据集笔记:纽约花旗共享单车od数据
花旗共享单车公布的其共享单车轨迹数据,包括2013年-2021年曼哈顿、布鲁克林、皇后区和泽西城大约14500辆自行车和950个站点的共享单车轨迹数据 数据地址:Citi Bike System Data | Citi Bike NYC | Citi Bike NYC 性别(0未知;1男&…...
为什么 0.1+0.2 不等于 0.3
为什么 0.10.2 不等于 0.3 在 JavaScript 中,0.1 0.2 的结果不等于 0.3,这是因为在 JavaScript 中采用的是双精度浮点数格式(64 位),而在这种格式下无法精确表示某些小数,因此在进行计算时会出现精度误差。…...
huggingface_hub v0.17 现已发布
InferenceClient 现在支持所有任务!💥,感谢社区的巨大努力,新添加的任务包括: 对象检测文本分类Token 分类翻译问题回答表格问题回答填充掩码表格分类表格回归文档问题回答视觉问题回答零样本分类 这些方法还支持使用 …...

机器学习——一元线性回归构造直线,并给出损失函数
目 录 Question 问题分析 1.概念补充 2.流程分析 3.注意 具体实现 最终成果 代码 思考: Question 在二维平面有n个点,如何画一条直线,使得所有点到该直线距离之和最短 如果能找到,请给出其损失函数 问题分析 1.概念…...

OpenHarmony自定义组件介绍
一、创建自定义组件 在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑…...

云原生之使用Docker部署PDF多功能工具Stirling-PDF
云原生之使用Docker部署PDF多功能工具Stirling-PDF 一、Stirling-PDF介绍1.1 Stirling-PDF简介1.2 Stirling-PDF功能 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Stirli…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...