Spring Boot 系统初始化器详解

news2024/11/16 15:46:36

Spring Boot 3.x系列文章

  1. Spring Boot 2.7.8 中文参考指南(一)
  2. Spring Boot 2.7.8 中文参考指南(二)-Web
  3. Spring Boot 源码阅读初始化环境搭建
  4. Spring Boot 框架整体启动流程详解
  5. Spring Boot 系统初始化器详解

自定义系统初始化器

Spring Boot 有多种加载自定义初始化器的方法:
1、创建一个实现ApplicationContextInitializer接口的类,在spring.factories中添加,如MyInitializer
2、创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers添加,如MyInitializer2
3、创建一个实现ApplicationContextInitializer接口的类,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3
4、创建一个实现EnvironmentPostProcessor接口的类,在spring.factories中添加,如MyEnvironmentPostProcessor

代码如下所示:

MyInitializer.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(2)
public class MyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key", "value");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer run");
    }
}

MyInitializer2.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(1)
public class MyInitializer2 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key2", "value2");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer2 run");
    }
}

MyInitializer3.java


import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(10)
public class MyInitializer3 implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment configurableEnvironment = applicationContext.getEnvironment();
        Map<String, Object> map = new HashMap<>();
        map.put("key3", "value3");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        configurableEnvironment.getPropertySources().addLast(mapPropertySource);
        log.info("My Initializer3 run");
    }
}

MyEnvironmentPostProcessor.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Order(5)
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Map<String, Object> map = new HashMap<>();
        map.put("key", "value");
        MapPropertySource mapPropertySource = new MapPropertySource("mySource", map);
        environment.getPropertySources().addLast(mapPropertySource);
        //为什么不打印日志
//        log.info("My EnvironmentPostProcessor run");
        System.out.println("My EnvironmentPostProcessor run");
    }
}

启动后截图:
在这里插入图片描述

疑问❓

  • 在MyEnvironmentPostProcessor的示例中,用log.info("My EnvironmentPostProcessor run"); 不会打印日志。
  • MyInitializer3的输出怎么会在MyInitializer2之前。

加载原理

实例1加载原理

在之前的文章中《Spring Boot 框架整体启动流程详解》有介绍到Spring Boot 应用程序初始化的时候会从META-INF/spring.factories加载ApplicationContextInitializer类实例

在这里插入图片描述
SpringFactoriesLoader 是Spring 框架中的类,用于从多个Jar文件的META-INF/spring.factories中加载并实例化给定的类型,spring.factories文件必须采用Properties格式,其中key是接口或抽象类的完全限定名称,value是以逗号分隔的实现类名列表。例如:
example.MyService=example.MyServicesImpl1,example.MyService Impl2
其中example.MyService是接口的名称,MyServiceImpl1和MyServiceImpl2是两个实现。

获取实例分成了两部分,首先从多个Jar文件的META-INF/spring.factories中加载key和value,返回一个SpringFactoriesLoader实例,然后调用SpringFactoriesLoader的load方法初始化指定key(key为接口或者抽象类的全限定名)对应的所有value(接口实现类),返回实例列表。

spring.factories的加载

在这里插入图片描述
FACTORIES_RESOURCE_LOCATION指定了加载的路径

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
// 判断资源路径是否为空,若为空则抛出异常
	Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
// 获取资源对应的类加载器,若传入的类加载器为空,则使用SpringFactoriesLoader类的类加载器
	ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
			SpringFactoriesLoader.class.getClassLoader());
// 从缓存中获取SpringFactoriesLoader,若不存在,则创建一个并缓存 Map<String, SpringFactoriesLoader>,key为ClassLoader,资源对应的类加载器
	Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(
			resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
// 返回resourceLocation对应的SpringFactoriesLoader对象,若不存在,则创建一个并缓存,key为resourceLocation,资源路径
	return loaders.computeIfAbsent(resourceLocation, key ->
			new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}

computeIfAbsent 返回的是key关联的value值

最后一步value创建了一个SpringFactoriesLoader实例,loadFactoriesResource 使用给定的资源类加载器从"META-INF/spring.factories"中加载

protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
//实现列表,key=接口或抽象类全限定名 value=实现类全限定名
	Map<String, List<String>> result = new LinkedHashMap<>();
	try {
	//获取指定路径下所有的资源URL
		Enumeration<URL> urls = classLoader.getResources(resourceLocation);
		while (urls.hasMoreElements()) {
			UrlResource resource = new UrlResource(urls.nextElement());
			//从URL资源中读取配置
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			properties.forEach((name, value) -> {
			//实现类逗号分割,转换为数组
				String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);
				//接口的实现类列表
				List<String> implementations = result.computeIfAbsent(((String) name).trim(),
						key -> new ArrayList<>(factoryImplementationNames.length));

//去掉实现类两边空格,并插入实现类列表
				Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);
			});
		}
		
//去重
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
	}
	//返回不可修改的map
	return Collections.unmodifiableMap(result);
}

加载部分有很多的key,value 要分清楚。

spring.factories接口实现类的实例化

实例化通过调用SpringFactoriesLoader的load方法

	public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {
		return load(factoryType, argumentResolver, null);
	}

factoryType指定要实例化的类型,这里为 org.springframework.context.ApplicationContextInitializer
argumentResolver 实例化需要的参数,这里为null

public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
		@Nullable FailureHandler failureHandler) {

	Assert.notNull(factoryType, "'factoryType' must not be null");
	//从factories 中获取指定接口类型的所有实现
	//factories就是加载步骤中返回的result
	List<String> implementationNames = loadFactoryNames(factoryType);
	logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
	List<T> result = new ArrayList<>(implementationNames.size());
	//定义失败处理器
	FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;
	//循环,实例化
	for (String implementationName : implementationNames) {
	//通过构造函数实例化
		T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
		if (factory != null) {
			result.add(factory);
		}
	}
	//根据order 排序
	AnnotationAwareOrderComparator.sort(result);
	return result;
}

最终返回排序后的ApplicationContextInitializer 实例,赋值SpringApplication 的 initializers 变量。

执行

执行会在SpringApplication类的prepareContext(准备上下文)中进行调用,如图所示:
在这里插入图片描述

	//返回一个只读的有序,LinkedHashSet 类型
	public Set<ApplicationContextInitializer<?>> getInitializers() {
		return asUnmodifiableOrderedSet(this.initializers);
	}
	protected void applyInitializers(ConfigurableApplicationContext context) {
	//获取所有的ApplicationContextInitializer实例
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			// 判断ApplicationContextInitializer实例泛型是否与context对象类型一致
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			// 调用ApplicationContextInitializer实例的initialize方法进行初始化操作
			initializer.initialize(context);
		}
	}

实例2加载原理

创建一个实现ApplicationContextInitializer接口的类,在SpringApplication 中使用addInitializers添加,如MyInitializer2

我们使用addInitializers 将ApplicationContextInitializer接口的实现加入到SpringApplication中。

	public void addInitializers(ApplicationContextInitializer<?>... initializers) {
		this.initializers.addAll(Arrays.asList(initializers));
	}

initializers 就是 SpringApplication中的initializers变量,执行点同实例1,在准备上下文的时候执行,由于执行前会进行一次排序,所以他们两的顺序是正确的。

实例3加载原理

创建一个实现ApplicationContextInitializer接口的类,在application.ymlapplication.properties中使用context.initializer.classes添加,如MyInitializer3

该处通过配置文件添加ApplicationContextInitializer实现类,并且通过DelegatingApplicationContextInitializer 初始化器进行加载和执行。

在这里插入图片描述
DelegatingApplicationContextInitializer 被定义在了spring-boot.jar 的 META-INF/spring.factories中,并且由于他的order是0,所以会在我们自定义MyInitializer和MyInitializer2 前执行,它是另外一种独立的初始化器,专门用于将配置文件中的ApplicationContextInitializer实现类加载到Spring容器中。

在这里插入图片描述
执行在DelegatingApplicationContextInitializer类的applyInitializers方法中

private void applyInitializers(ConfigurableApplicationContext context,
		List<ApplicationContextInitializer<?>> initializers) {
		//排序
	initializers.sort(new AnnotationAwareOrderComparator());
	for (ApplicationContextInitializer initializer : initializers) {
	//调用initialize方法
		initializer.initialize(context);
	}
}

实例4加载原理

创建一个实现EnvironmentPostProcessor接口的类,在spring.factories中添加,如MyEnvironmentPostProcessor
实例4是在所有的测试中最先打印日志的,是因为它是在prepareEnvironment(准备环境)中执行,而前面3个实例都是在prepareContext(准备上下文)中执行。
该实例中EventPublishingRunListener会调用prepareEnvironment方法,EventPublishingRunListener被定义在Spring Boot Jar包的META-INF/spring.factories中,用于发布各种SpringApplicationEvent事件。

EventPublishingRunListener类中

public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
		ConfigurableEnvironment environment) {
		//广播环境准备完成事件
	multicastInitialEvent(
			new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}

private void multicastInitialEvent(ApplicationEvent event) {
	//刷新SimpleApplicationEventMulticaster中的事件列表
	refreshApplicationListeners();
	//广播事件
	this.initialMulticaster.multicastEvent(event);
}

private void refreshApplicationListeners() {
	this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
}

SimpleApplicationEventMulticaster类中

public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, null);
}

@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
	// 获取执行事件的线程池
	Executor executor = getTaskExecutor();
	//获取指定事件类型的事件集合
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
	//如果定义了执行线程池,则用线程池调用
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
		//同步调用监听器
			invokeListener(listener, event);
		}
	}
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//获取失败处理器
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
		try {
			doInvokeListener(listener, event);
		}
		catch (Throwable err) {
			errorHandler.handleError(err);
		}
	}
	else {
		doInvokeListener(listener, event);
	}
}

@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
	try {
	//此处执行事件监听器的onApplicationEvent方法
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
		String msg = ex.getMessage();
		if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
				(event instanceof PayloadApplicationEvent payloadEvent &&
						matchesClassCastMessage(msg, payloadEvent.getPayload().getClass()))) {
			// Possibly a lambda-defined listener which we could not resolve the generic event type for
			// -> let's suppress the exception.
			Log loggerToUse = this.lazyLogger;
			if (loggerToUse == null) {
				loggerToUse = LogFactory.getLog(getClass());
				this.lazyLogger = loggerToUse;
			}
			if (loggerToUse.isTraceEnabled()) {
				loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
			}
		}
		else {
			throw ex;
		}
	}
}

listener.onApplicationEvent(event);处,在本例中为EnvironmentPostProcessorApplicationListener

EnvironmentPostProcessorApplicationListener类中:

public void onApplicationEvent(ApplicationEvent event) {
//根据各个事件类型分别去处理
	if (event instanceof ApplicationEnvironmentPreparedEvent environmentPreparedEvent) {
		onApplicationEnvironmentPreparedEvent(environmentPreparedEvent);
	}
	if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent();
	}
	if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent();
	}
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	SpringApplication application = event.getSpringApplication();
	// 获取所有的 EnvironmentPostProcessor,然后执行其 postProcessEnvironment 方法
	for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
			event.getBootstrapContext())) {
		postProcessor.postProcessEnvironment(environment, application);
	}
}

// 获取所有的 EnvironmentPostProcessor
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
		ConfigurableBootstrapContext bootstrapContext) {
	ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
	//postProcessorsFactory 是一个函数表达式
	EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
	return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}

EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);中的postProcessorsFactory是在EnvironmentPostProcessorApplicationListener实例化的时候初始化,根据前面的文章我们知道EnvironmentPostProcessorApplicationListener是一个监听器,会在SpringBoot初始化的时候初始化。

public EnvironmentPostProcessorApplicationListener() {
	this(EnvironmentPostProcessorsFactory::fromSpringFactories);
}

private EnvironmentPostProcessorApplicationListener(
		Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory) {
	this.postProcessorsFactory = postProcessorsFactory;
	this.deferredLogs = new DeferredLogs();
}

static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
	return new SpringFactoriesEnvironmentPostProcessorsFactory(
			SpringFactoriesLoader.forDefaultResourceLocation(classLoader));
}

EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) 会在EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader); apply的时候调用,如果没有加载META-INF/spring.factories会再这里再次加载。

EnvironmentPostProcessorsFactory 的主要作用是实例化EnvironmentPostProcessorSpringFactoriesEnvironmentPostProcessorsFactory是其子类。

SpringFactoriesEnvironmentPostProcessorsFactory类中:

public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
		ConfigurableBootstrapContext bootstrapContext) {
	ArgumentResolver argumentResolver = ArgumentResolver.of(DeferredLogFactory.class, logFactory);
	//向argumentResolver对象中添加ConfigurableBootstrapContext.class和bootstrapContext,获取更新后的argumentResolver对象
	argumentResolver = argumentResolver.and(ConfigurableBootstrapContext.class, bootstrapContext);
	// // 向argumentResolver对象中添加BootstrapRegistry.class和bootstrapContext,获取更新后的argumentResolver对象
	argumentResolver = argumentResolver.and(BootstrapContext.class, bootstrapContext);
	 通过this.loader.load方法加载EnvironmentPostProcessor类型的对象,参数为argumentResolver
	argumentResolver = argumentResolver.and(BootstrapRegistry.class, bootstrapContext);
	//加载EnvironmentPostProcessor类型的对象
	return this.loader.load(EnvironmentPostProcessor.class, argumentResolver);
}

最后循环调用postProcessor.postProcessEnvironment(environment, application);完成执行。

总结

同样的,用一张图来总结本文整个流程:
在这里插入图片描述


作者其他文章:
Prometheus 系列文章

  1. Prometheus 的介绍和安装
  2. 直观感受PromQL及其数据类型
  3. PromQL之选择器和运算符
  4. PromQL之函数
  5. Prometheus 告警机制介绍及命令解读
  6. Prometheus 告警模块配置深度解析
  7. Prometheus 配置身份认证
  8. Prometheus 动态拉取监控服务
  9. Prometheus 监控云Mysql和自建Mysql

Grafana 系列文章,版本:OOS v9.3.1

  1. Grafana 的介绍和安装
  2. Grafana监控大屏配置参数介绍(一)
  3. Grafana监控大屏配置参数介绍(二)
  4. Grafana监控大屏可视化图表
  5. Grafana 查询数据和转换数据
  6. Grafana 告警模块介绍
  7. Grafana 告警接入飞书通知

Spring Boot Admin 系列

  1. Spring Boot Admin 参考指南
  2. SpringBoot Admin服务离线、不显示健康信息的问题
  3. Spring Boot Admin2 @EnableAdminServer的加载
  4. Spring Boot Admin2 AdminServerAutoConfiguration详解
  5. Spring Boot Admin2 实例状态监控详解
  6. Spring Boot Admin2 自定义JVM监控通知
  7. Spring Boot Admin2 自定义异常监控
  8. Spring Boot Admin 监控指标接入Grafana可视化

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

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

相关文章

账号长久不用、归属不明......企业要如何管理这些失控的账号?

据报道&#xff0c;谷歌将于今年底开始删除2年未使用个谷歌个人账号。理由是&#xff0c;谷歌发现&#xff0c;如果一个账户在一定时间内未被使用&#xff0c;那么该账户被入侵的概率更大。 这些长时间未使用的账号使用的密码一般都是比较老的或重用的密码&#xff0c;关联密码…

Linux之tar归档命令

目录 Linux之tar归档命令 定义 语法格式 参数及作用 常用选项 创建&#xff08;非压缩的&#xff09;包文件 ​编辑 创建带压缩的包文件 列出包文件中的文件列表 提取包文件到指定目录 tar打包时排除 --exclude -X或--exclude-from Linux之tar归档命令 定义 用于打…

全面的软件测试

1 全过程的软件测试图解 传统的软件测试&#xff0c;开发人员完成任务之后&#xff0c;最后交付给测试人员&#xff0c;这种模式下&#xff0c;测试人员不能及早发现需求阶段的缺陷&#xff0c;同时测试工作的开展也滞后了&#xff0c;产品质量得不到有效的过程控制和分析&…

echarts折线图使用记录

1项目中引入echarts文档api介绍&#xff0c;链接地址如下 https://echarts.apache.org/handbook/zh/basics/import 2 官网基础样例介绍,链接地址如下 https://echarts.apache.org/handbook/zh/get-started/ 3 基本折线图介绍内容及链接 3.1 最简单的折线图 3.2 笛卡尔坐标系中…

【Python】Step Into Python Class

【Python】Step Into Python Class Before All Python作为一门面向过程兼容面向对象的语言&#xff0c;在面向对象中&#xff0c;使用class关键字来申明一个类。 But&#xff0c;是不是应该深入考虑一下这个class的底层实现过程呢&#xff1f;&#xff08;不考虑CPython&…

msvcp110.dll丢失怎么修复(一键修复办法)

msvcp110.dll是C编程中非常重要的库文件之一。它实现了运行时库的大部分功能&#xff0c;并提供了许多标准库和其他功能的具体实现&#xff0c;如多线程编程和IO操作等。提高程序的运行效率和稳定性。下面是详细解决msvcp110.dll丢失问题的方法跟msvcp110.dll文件的介绍。 msvc…

npm install报错 -> npm ERR! Unexpected token ‘.‘ 报错解决办法

原因&#xff1a; 我遇到这个问题的场景是用nvm1.1.7的版本安装了16.x以上的node, 然后再下载依赖的时候就报错了 总结一下就是nvm版本太低了&#xff0c;他的里面没有集成高版本node导致的 解决&#xff1a; 我们把nvm版本换到最新的就可以了 1. 卸载掉当前所有的node nvm …

ABIDE Preprocessed 结构态MRI数据集介绍及下载

ABIDE数据集介绍及下载 ABIDE Prerocessed项目是在ABIDE I 项目的基础上发展而来&#xff0c;主要是对ABIDE I中采集到的原始数据进行了一定的预处理和初步的特征提取。针对于fMRI和sMRI数据有着不同的处理方式&#xff0c;本次主要对其中提供的sMRI预处理结果进行介绍&#xf…

Python程序设计基础:标识符、变量与赋值、输入输出

文章目录 一、标识符二、变量与赋值三、输入输出 一、标识符 Python对每个标识符的命名存在要求&#xff1a; 1、每个标识符必须以字母或下划线“_”开头&#xff0c;后跟字母、数字或下划线的任意序列。根据这个规则&#xff0c;以下都是Python中的合法名称&#xff1a;a&…

excel如何实现识别文本在对应单元格填上数据?

要实现 Excel 识别文本在对应单元格填上数据&#xff0c;有以下两种方法&#xff1a; 方法一&#xff1a;使用 VLOOKUP 函数 1. 在 Excel 工作表中&#xff0c;输入一个表格&#xff0c;列名为对应的文本&#xff0c;行名为不同条目。 2. 准备输入数据&#xff0c;在一个新的…

python使用requests+excel进行接口自动化测试

在当今的互联网时代中&#xff0c;接口自动化测试越来越成为软件测试的重要组成部分。Python是一种简单易学&#xff0c;高效且可扩展的语言&#xff0c;自然而然地成为了开发人员的首选开发语言。而requests和xlwt这两个常用的Python标准库&#xff0c;能够帮助我们轻松地开发…

LInux之find查找

目录 LInux之find查找 定义 详解 格式 参数及作用 详解 1.按照文件名搜索 2.按照文件大小搜索 3.按照修改时间搜索 4.按照权限搜索 5.按照所有者和所属组搜索 6.按照文件类型搜索 7.逻辑运算符 8.其他选项 -exec参数 获取到该目录中所有以host开头的文件列表 如在…

【测试入门】测试用例经典设计方法 —— 因果图法

01、因果图设计测试用例的步骤 1、分析需求 阅读需求文档&#xff0c;如果User Case很复杂&#xff0c;尽量将它分解成若干个简单的部分。这样做的好处是&#xff0c;不必在一次处理过程中考虑所有的原因。没有固定的流程说明究竟分解到何种程度才算简单&#xff0c;需要测试…

3D打印机分类汇总

1 根据市场定位分类 当今市面上应用比较多的3D打印机是SLS、SLA、DLP、FDM四种3D打印机&#xff0c;按照用途可分为两类&#xff1a;一类是高精度工业打印机&#xff0c;比如SLA、DLP、SLS&#xff1b;一类是以FDM、SLA&#xff08;用于工业打印机更多&#xff09;为主的桌面级…

NRF52832空中升级DFU

1.工具环境搭建 gcc-arm-none-eabi编译环境&#xff1a;GCC编译环境 Downloads | GNU Arm Embedded Toolchain Downloads – Arm Developer mingw 平台&#xff08;win版的Linux命令行&#xff09; Download MinGW - Minimalist GNU for Windows micro-ecc-master源码 GitHu…

永不磨灭的设计模式(23种设计模式全集)

永不磨灭的设计模式 概述七大基本原则23种设计模式1、单例模式2、工厂(方法)模式3、抽象工厂模式4、原型模式5、建造者模式6、适配器模式7、桥接模式8、组合模式9、装饰器模式10、外观模式11、享元模式12、代理模式13、责任链模式14、命令模式15、迭代器模式16、中介者模式17、…

tinkerCAD案例:3.基本按钮

基本按钮 在本课中&#xff0c;您将学习制作具有圆柱形状的基本按钮。 说明 将圆柱体拖动到工作平面。 将其缩小到 2 毫米的高度。 提示&#xff1a; 您可以使用圆柱形状顶部的白点缩小圆柱体。 将其缩小到直径 16 毫米。 这将是按钮的主要形状。 现在我们可以创建允许将纽…

CSS动画:多动画同步播放或非同步播放

前言 本篇在讲什么 在CSS样式表现动画的基础上的拓展 本篇适合什么 适合初学H5的小白 适合初学CSS的小白 适合入门的前端程序 本篇需要什么 对Html和css语法有简单认知 Node.js(博主v18.13.0)的开发环境 Npm(博主v8.19.3)的开发环境 依赖VS code编辑器 本篇的特色…

impala内存超限

目录 一、背景 二、报错内容 三、解决办法 1.调参 2.简单粗暴 一、背景 impala shell执行SQL语句时报错 二、报错内容 Memory limit exceeded: Could not allocate memory while trying to increase reservation. 三、解决办法 1.调参 mem_limit参数&#xff1a;&…

ciscn 2023 初赛 pwn shell we go

ciscn 2023 初赛 pwn shell we go 这题go pwn&#xff0c;符号恢复就恢复很长时间了&#xff0c;网上的插件好多都没用 根着流程&#xff0c;可以看到这里有一个验证&#xff0c;以空格来分割&#xff0c;第一个参数会验证是否为nAcDsMicN 如果第一个参数验证通过&#xff0c…