SpringBoot源码解析(四):解析应用参数args

news2024/11/19 6:32:18

SpringBoot源码系列文章

SpringBoot源码解析(一):SpringApplication构造方法

SpringBoot源码解析(二):引导上下文DefaultBootstrapContext

SpringBoot源码解析(三):启动开始阶段

SpringBoot源码解析(四):解析应用参数args


目录

  • 前言
  • 一、入口
  • 二、默认应用程序参数DefaultApplicationArguments
    • 1、功能概述
    • 2、使用示例
    • 3、接口ApplicationArguments
    • 4、实现类DefaultApplicationArguments
  • 四、Source
    • 1、属性源PropertySource
    • 2、枚举属性源EnumerablePropertySource
    • 3、命令行属性源CommandLinePropertySource
    • 4、简单命令行属性源SimpleCommandLinePropertySource
  • 五、解析参数原理
    • 1、解析方法
    • 2、解析参数的存储和访问
    • 3、实际应用
  • 总结

前言

  前文深入解析了SpringBoot启动的开始阶段,包括获取和启动应用启动监听器、事件与广播机制,以及如何通过匹配监听器实现启动过程各阶段的自定义逻辑。接下来,我们将探讨SpringBoot启动类main函数中的参数args的作用及其解析过程

SpringBoot版本2.7.18SpringApplication的run方法的执行逻辑如下,本文将详细介绍第3小节:解析应用参数

// SpringApplication类方法
public ConfigurableApplicationContext run(String... args) {
    // 记录应用启动的开始时间
    long startTime = System.nanoTime();

    // 1.创建引导上下文,用于管理应用启动时的依赖和资源
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;

    // 配置无头模式属性,以支持在无图形环境下运行
    // 将系统属性 java.awt.headless 设置为 true
    configureHeadlessProperty();

    // 2.获取Spring应用启动监听器,用于在应用启动的各个阶段执行自定义逻辑
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动开始方法(发布开始事件、通知应用监听器ApplicationListener)
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        // 3.解析应用参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 4.准备应用环境,包括读取配置文件和设置环境变量
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        // 配置是否忽略 BeanInfo,以加快启动速度
        configureIgnoreBeanInfo(environment);

        // 5.打印启动Banner
        Banner printedBanner = printBanner(environment);

        // 6.创建应用程序上下文
        context = createApplicationContext();
        
        // 设置应用启动的上下文,用于监控和管理启动过程
        context.setApplicationStartup(this.applicationStartup);

        // 7.准备应用上下文,包括加载配置、添加 Bean 等
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // 8.刷新上下文,完成 Bean 的加载和依赖注入
        refreshContext(context);

        // 9.刷新后的一些操作,如事件发布等
        afterRefresh(context, applicationArguments);

        // 计算启动应用程序的时间,并记录日志
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 10.通知监听器应用启动完成
        listeners.started(context, timeTakenToStartup);

        // 11.调用应用程序中的 `CommandLineRunner` 或 `ApplicationRunner`,以便执行自定义的启动逻辑
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 12.处理启动过程中发生的异常,并通知监听器
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 13.计算应用启动完成至准备就绪的时间,并通知监听器
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        // 处理准备就绪过程中发生的异常
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }

    // 返回已启动并准备就绪的应用上下文
    return context;
}

一、入口

  • 将main方法的参数args封装成一个对象DefaultApplicationArguments,以便方便地解析和访问启动参数
// 3.解析应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

二、默认应用程序参数DefaultApplicationArguments

1、功能概述

  DefaultApplicationArguments是SpringBoot中的一个类,用于处理启动时传入的参数。它实现了ApplicationArguments接口,并提供了一些便捷的方法来访问传入的命令行参数选项参数

// 3.解析应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  • 解析命令行参数:将 main 方法中的 args 参数解析成选项参数和非选项参数,方便应用在启动时读取外部传入的配置
  • 访问选项参数:支持以--key=value格式的选项参数,通过方法getOptionNames()getOptionValues(String name)获取特定的选项及其值
  • 访问非选项参数:对于不以--开头的参数,可以通过getNonOptionArgs()获取它们的列表

2、使用示例

  • 假设我们在命令行中运行应用,传递了一些参数
java -jar myapp.jar --server.port=8080 arg1 arg2
  • 在代码中,我们可以使用DefaultApplicationArguments来解析这些参数
public static void main(String[] args) {
    DefaultApplicationArguments appArgs = new DefaultApplicationArguments(args);

    // 获取所有选项参数名称
    System.out.println("选项参数:" + appArgs.getOptionNames());  // 输出: ["server.port"]
    // 获取指定选项的值(所有以 `--` 开头的选项参数名称)
    System.out.println("server.port 值:" + appArgs.getOptionValues("server.port"));  // 输出: ["8080"]

    // 获取非选项参数(所有不以 `--` 开头的参数,通常用于传递无标记的参数值)
    System.out.println("非选项参数:" + appArgs.getNonOptionArgs());  // 输出: ["arg1", "arg2"]
}

3、接口ApplicationArguments

  • ApplicationArguments是DefaultApplicationArguments类的父接口
// 提供对用于运行应用的参数的访问。
public interface ApplicationArguments {
	// 返回传递给应用程序的原始未处理参数
	String[] getSourceArgs();
	// 返回所有选项参数的名称
	Set<String> getOptionNames();
	// 返回解析的选项参数集合中是否包含具有给定名称的选项
	boolean containsOption(String name);
	// 返回与给定名称的选项参数关联的值集合
	List<String> getOptionValues(String name);
	// 返回解析的非选项参数集合
	List<String> getNonOptionArgs();
}
  • getOptionNames():返回所有选项参数的名称
    • 例如:参数是"--foo=bar --debug",则返回["foo", "debug"]
  • getOptionValues(String name):返回与给定名称的选项参数关联的值集合
    • 如果选项存在但没有值(例如:"--foo"),返回一个空集合
    • 如果选项存在且有单一值(例如:"--foo=bar"),返回一个包含一个元素的集合["bar"]
    • 如果选项存在且有多个值(例如:"--foo=bar --foo=baz"),返回包含每个值的集合["bar", "baz"]
    • 如果选项不存在,返回null
  • getNonOptionArgs():返回解析的非选项参数集合

4、实现类DefaultApplicationArguments

  • 代码很简单,对外暴露使用DefaultApplicationArguments,内部实现都在Source
// ApplicationArguments的默认实现类,用于解析应用程序启动时传入的参数。
public class DefaultApplicationArguments implements ApplicationArguments {
	private final Source source; // 用于解析和存储参数的内部辅助类
	private final String[] args; // 启动时传入的原始参数
	// 构造函数,使用传入的参数数组初始化对象
	public DefaultApplicationArguments(String... args) {
		Assert.notNull(args, "Args must not be null"); // 确保传入参数不为 null
		this.source = new Source(args); // 使用内部类 Source 解析参数
		this.args = args; // 保存原始参数
	}
	// 获取原始未处理的参数数组
	@Override
	public String[] getSourceArgs() {
		return this.args;
	}

	// 获取所有选项参数的名称集合
	@Override
	public Set<String> getOptionNames() {
		String[] names = this.source.getPropertyNames();
		// 该集合不能被修改(即添加、删除元素等操作会抛出 UnsupportedOperationException 异常)
		return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(names)));
	}

	// 检查是否包含指定名称的选项参数
	@Override
	public boolean containsOption(String name) {
		return this.source.containsProperty(name);
	}

	// 获取指定名称的选项参数的值集合
	@Override
	public List<String> getOptionValues(String name) {
		List<String> values = this.source.getOptionValues(name);
		return (values != null) ? Collections.unmodifiableList(values) : null;
	}

	// 获取所有非选项参数(不以 "--" 开头的参数)
	@Override
	public List<String> getNonOptionArgs() {
		return this.source.getNonOptionArgs();
	}

	// 内部类,用于处理和解析命令行参数
	// 继承自 SimpleCommandLinePropertySource,可以获取选项参数和非选项参数
	private static class Source extends SimpleCommandLinePropertySource {
		// 使用参数数组初始化 Source
		Source(String[] args) {
			super(args);
		}
		// 获取所有非选项参数。
		@Override
		public List<String> getNonOptionArgs() {
			return super.getNonOptionArgs();
		}
		// 获取指定名称的选项参数的值列表
		@Override
		public List<String> getOptionValues(String name) {
			return super.getOptionValues(name);
		}
	}
}

四、Source

  Source是DefaultApplicationArguments解析参数内部的真正实现类,类图如下,逐一分析。

在这里插入图片描述

1、属性源PropertySource

  PropertySource是Spring框架中的一个核心抽象类,用于表示属性(键值对)的来源。通过将各种配置来源(如系统属性环境变量配置文件等)封装为PropertySource对象,Spring可以提供统一的接口来读取和管理这些配置数据。

  • 属性源名称:每个PropertySource实例都具有唯一的名称,用于区分不同的属性源
  • 属性源对象PropertySource<T>是一个泛型类,其中T代表具体的属性源类型
  • getProperty(String name):用于在属性源对象中检索具体的属性,name表示具体属性的键,返回具体属性的值
public abstract class PropertySource<T> {
	protected final Log logger = LogFactory.getLog(getClass());
	protected final String name;  // 属性源的名称
	protected final T source;  // 属性源的数据源对象

	// 使用给定的名称和源对象创建属性源
	public PropertySource(String name, T source) {
		Assert.hasText(name, "Property source name must contain at least one character");
		Assert.notNull(source, "Property source must not be null");
		this.name = name;
		this.source = source;
	}

	// 使用给定的名称和一个新的Object对象作为底层源创建属性源
	public PropertySource(String name) {
		this(name, (T) new Object());
	}
	
	// 返回属性源名称
	public String getName() {
		return this.name;
	}
	// 返回属性源的底层源对象。
	public T getSource() {
		return this.source;
	}

	// 判断属性源是否包含给定名称的属性(子类可以实现更高效的算法)
	// containsProperty和getProperty参数的name与上面定义的属性源名称的name不是一回事
	public boolean containsProperty(String name) {
		return (getProperty(name) != null);
	}
	// 返回与给定名称关联的属性值,如果找不到则返回null,子类实现
	@Nullable
	public abstract Object getProperty(String name);

	...
}

2、枚举属性源EnumerablePropertySource

  EnumerablePropertySource继承自PropertySource,主要用于定义getPropertyNames()方法,可以获取属性源对象中所有属性键的名称

public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
	// 使用给定的名称和源对象创建属性源(调用父类PropertySource的构造方法)
	public EnumerablePropertySource(String name, T source) {
		super(name, source);
	}
	// 也是调用父类构造
	protected EnumerablePropertySource(String name) {
		super(name);
	}
	
	// 判断属性源是否包含具有给定名称的属性(重新了PropertySource的此方法)
	@Override
	public boolean containsProperty(String name) {
		return ObjectUtils.containsElement(getPropertyNames(), name);
	}
	// 返回所有属性的名称
	public abstract String[] getPropertyNames();
}

3、命令行属性源CommandLinePropertySource

  CommandLinePropertySource是Spring框架中用于处理命令行参数PropertySource实现。它可以将应用程序启动时传入的命令行参数解析成键值对,便于在应用配置中使用。

  • 命令行属性源名称默认为commandLineArgs
  • getOptionValues(String name):通过命令行属性源(即选项参数键值对)的键获取对应的值
  • getNonOptionArgs():通过命令行属性源(即键默认为nonOptionArgs的非选项参数键值对)获取对于的值
  • 例:java -jar your-app.jar --server.port=8081 --spring.profiles.active=prod arg1 arg2
    • 选项参数会有多个键值对,key1为server.port,key2为spring.profiles.active
    • 非选项参数永远只有一个键值对,所有key都是nonOptionArgs
public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> {
	// CommandLinePropertySource实例的默认名称
	public static final String COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs";
	// 表示非选项参数的属性键的默认名称
	public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs";
	private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME;
	
	// 创建一个新的命令行属性源,使用默认名称
	public CommandLinePropertySource(T source) {
		super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source);
	}

	// 创建一个新的命令行属性源,具有给定名称
	public CommandLinePropertySource(String name, T source) {
		super(name, source);
	}

	// 可以通过set方法设置非选项参数的键的名称
	public void setNonOptionArgsPropertyName(String nonOptionArgsPropertyName) {
		this.nonOptionArgsPropertyName = nonOptionArgsPropertyName;
	}

	// 首先检查指定的名称是否是特殊的“非选项参数”属性,
	// 如果是,则委托给抽象方法#getNonOptionArgs()
	// 否则,委托并返回抽象方法#containsOption(String)
	@Override
	public final boolean containsProperty(String name) {
		if (this.nonOptionArgsPropertyName.equals(name)) {
			return !getNonOptionArgs().isEmpty();
		}
		return this.containsOption(name);
	}

	// 首先检查指定的名称是否是特殊的“非选项参数”属性,
	// 如果是,则委托给抽象方法#getNonOptionArgs(),返回用逗号隔开字符串
	// 否则,委托并返回抽象方法#getOptionValues(name),返回用逗号隔开字符串
	@Override
	@Nullable
	public final String getProperty(String name) {
		if (this.nonOptionArgsPropertyName.equals(name)) {
			Collection<String> nonOptionArguments = getNonOptionArgs();
			if (nonOptionArguments.isEmpty()) {
				return null;
			}
			else {
				return StringUtils.collectionToCommaDelimitedString(nonOptionArguments);
			}
		}
		Collection<String> optionValues = getOptionValues(name);
		if (optionValues == null) {
			return null;
		}
		else {
			return StringUtils.collectionToCommaDelimitedString(optionValues);
		}
	}

	// 返回从命令行解析的选项参数集合中是否包含具有给定名称的选项
	protected abstract boolean containsOption(String name);

	// 返回与给定名称的选项参数关联的值集合
	@Nullable
	protected abstract List<String> getOptionValues(String name);

	// 返回从命令行解析的非选项参数集合,永不为null
	protected abstract List<String> getNonOptionArgs();
}

4、简单命令行属性源SimpleCommandLinePropertySource

  SimpleCommandLinePropertySource是Spring框架中的一个类,继承自CommandLinePropertySource,用于解析和处理命令行参数。它设计为简单易用,通过接收一个字符串数组(即命令行参数 args),将参数分为"选项参数""非选项参数"两类。

  • 命令行属性源对象类型为CommandLineArgs,通过new SimpleCommandLineArgsParser().parse(args)获取
public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {
	// 构造函数:创建一个使用默认名称commandLineArgs的SimpleCommandLinePropertySource实例的命令行属性源
	public SimpleCommandLinePropertySource(String... args) {
		super(new SimpleCommandLineArgsParser().parse(args));
	}
	// 创建指定名称命令行属性源
	public SimpleCommandLinePropertySource(String name, String[] args) {
		super(name, new SimpleCommandLineArgsParser().parse(args));
	}
	
	// 获取所有选项参数的名称
	@Override
	public String[] getPropertyNames() {
		return StringUtils.toStringArray(this.source.getOptionNames());
	}

	// 检查是否包含指定名称的选项
	@Override
	protected boolean containsOption(String name) {
		return this.source.containsOption(name);
	}

	// 获取指定选项名称的值列表
	@Override
	@Nullable
	protected List<String> getOptionValues(String name) {
		return this.source.getOptionValues(name);
	}

	// 获取所有非选项参数的列表
	@Override
	protected List<String> getNonOptionArgs() {
		return this.source.getNonOptionArgs();
	}
}

五、解析参数原理

  在上一节中,我们了解了应用程序参数args被解析后的结构存储方式。接下来,我们回到文章开头,详细解析参数是如何被逐步解析出来的。

// 3.解析应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  • 根据new DefaultApplicationArguments(args)寻找解析arg的位置

在这里插入图片描述

  • 解析完arg调用父类CommandLinePropertySource的构造方法

在这里插入图片描述

1、解析方法

  SimpleCommandLineArgsParser通过遍历传入的命令行参数数组,根据参数的格式,将参数解析并分为选项参数和非选项参数。

  • 选项参数解析规则
    • 选项参数必须以--前缀开头,例如 --name=John 或 --debug
    • 如果包含等号 =,= 左边的部分是选项名称,右边的部分是选项值
    • 如果没有等号,则视为不带值的选项
    • 如果获取不到选项名称(例如传入 --=value或–),抛出异常,表示参数格式无效
  • 非选项参数解析规则
    • 所有不以--开头的参数被视为非选项参数
class SimpleCommandLineArgsParser {
	public CommandLineArgs parse(String... args) {
		// 创建 CommandLineArgs 实例,用于存储解析结果
		CommandLineArgs commandLineArgs = new CommandLineArgs(); 
		for (String arg : args) { // 遍历每个命令行参数
			if (arg.startsWith("--")) { // 如果参数以 "--" 开头,则视为选项参数
				String optionText = arg.substring(2); // 去掉 "--" 前缀
				String optionName; // 选项名称
				String optionValue = null; // 选项值,默认为 null
				int indexOfEqualsSign = optionText.indexOf('='); // 查找等号的位置
				if (indexOfEqualsSign > -1) { // 如果找到了等号
					optionName = optionText.substring(0, indexOfEqualsSign); // 等号前的部分为选项名称
					optionValue = optionText.substring(indexOfEqualsSign + 1); // 等号后的部分为选项值
				}
				else {
					optionName = optionText; // 如果没有等号,整个文本为选项名称,值为 null
				}
				// 如果选项名称为空,抛出异常,例如,只输入了 "--=" 或 "--"
				if (optionName.isEmpty()) { 
					throw new IllegalArgumentException("Invalid argument syntax: " + arg);
				}
				// 将解析出的选项名称和值添加到 CommandLineArgs 对象中
				commandLineArgs.addOptionArg(optionName, optionValue); 
			}
			else {
				// 如果参数不是选项参数,直接作为非选项参数添加到 CommandLineArgs 对象中
				commandLineArgs.addNonOptionArg(arg); 
			}
		}
		return commandLineArgs; // 返回解析结果
	}
}
  • 属性源对象类型CommandLineArgs
// 命令行参数的简单表示形式,分为“带选项参数”和“无选项参数”。
class CommandLineArgs {

	// 存储带选项的参数,每个选项可以有一个或多个值
	private final Map<String, List<String>> optionArgs = new HashMap<>();

	// 存储无选项的参数
	private final List<String> nonOptionArgs = new ArrayList<>();

	// 为指定的选项名称添加一个选项参数,并将给定的值添加到与此选项关联的值列表中(可能有零个或多个)
	public void addOptionArg(String optionName, @Nullable String optionValue) {
		if (!this.optionArgs.containsKey(optionName)) {
			this.optionArgs.put(optionName, new ArrayList<>());
		}
		if (optionValue != null) {
			this.optionArgs.get(optionName).add(optionValue);
		}
	}

	// 返回命令行中所有带选项的参数名称集合
	public Set<String> getOptionNames() {
		return Collections.unmodifiableSet(this.optionArgs.keySet());
	}
	// 判断命令行中是否包含指定名称的选项。
	public boolean containsOption(String optionName) {
		return this.optionArgs.containsKey(optionName);
	}
	// 返回与给定选项关联的值列表。
	// 表示null表示该选项不存在;空列表表示该选项没有关联值。
	@Nullable
	public List<String> getOptionValues(String optionName) {
		return this.optionArgs.get(optionName);
	}
	// 将给定的值添加到无选项参数列表中
	public void addNonOptionArg(String value) {
		this.nonOptionArgs.add(value);
	}
	// 返回命令行中指定的无选项参数列表
	public List<String> getNonOptionArgs() {
		return Collections.unmodifiableList(this.nonOptionArgs);
	}
}

2、解析参数的存储和访问

  解析方法很简单,所有内容都在SimpleCommandLineArgsParser的parse方法中完成。相比之下,存储访问方式更为复杂。


  存储位置位于属性源对象PropertySource中。从代码可知,args表示命令行参数,因此属性源名称为命令行属性源默认名称commandLineArgs属性源对象为解析args后的键值对

在这里插入图片描述


  访问查询方式的底层实现就是操作CommandLineArgs中的optionArgs(选项参数)nonOptionArgs(非选项参数)两个集合,但此过程经过多次跳转,最终依次通过 DefaultApplicationArguments -> DefaultApplicationArguments#Source -> SimpleCommandLinePropertySource -> CommandLineArgs获取,其中CommandLineArgs就是是命令行属性源对象。这种设计主要是为了提供更灵活、安全的访问方式,避免直接暴露内部数据结构带来的潜在风险。

在这里插入图片描述

在这里插入图片描述

3、实际应用

  之前在SpringBoot基础(二):配置文件详解文章中有介绍过配置文件设置临时属性,这次回过头再来看,就很清晰明了了。

在这里插入图片描述
在这里插入图片描述

总结

  • 在SpringBoot启动时,启动类main函数中的args参数被解析为两类
    • 选项参数(如 --server.port=8080)
    • 非选项参数(如 arg1、arg2)
  • 对外暴露应用参数对象ApplicationArguments提供查询方法
    • getOptionValues(String name)方法可以获取选项参数
    • getNonOptionArgs() 方法则用于获取非选项参数
    • 这些参数在启动过程的后续阶段可供使用

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

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

相关文章

【Linux】指令 + 重定向操作

Linux基本指令 一.Linux基本指令1.mv&#xff08;重要&#xff09;2.cat3.more和less&#xff08;重要&#xff09;4.head和tail5.date6.cal7.find&#xff08;重要&#xff09; 二.Linux相关知识点1. Linux系统中&#xff1a;一切皆文件2. 重定向操作1. 输出重定向2. 追加重定…

【精通 Readline 库】:优化 Shell 外壳程序的艺术

&#x1f4c3;博客主页&#xff1a; 小镇敲码人 &#x1f49a;代码仓库&#xff0c;欢迎访问 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3fd;留言 &#x1f60d;收藏 &#x1f30f; 任尔江湖满血骨&#xff0c;我自踏雪寻梅香。 万千浮云遮碧…

ESP-IDF VScode 项目构建/增加组件 新手友好!!!

项目构建 1.新建文件夹&#xff0c;同时在该文件夹内新建.c和.h文件 如图所示&#xff0c;在components中新建ADC_User.c、ADC_User.h、CMakeLists.txt文件。当然这里你也可以不在components文件夹内新建文件&#xff0c;下面会说没有在components文件夹内新建文件构建项目的方…

玩转N1盒子:速刷OpenWRT软路由系统并实现公网访问管理

文章目录 前言1. 制作刷机固件U盘1.1 制作刷机U盘需要准备以下软件&#xff1a;1.2 制作步骤 2. N1盒子降级与U盘启动2.1 N1盒子降级2.2 N1盒子U盘启动设置2.3 使用U盘刷入OpenWRT2.4 OpenWRT后台IP地址修改2.5 设置旁路由&无线上网 3. 安装cpolar内网穿透3.1 下载公钥3.2 …

机器学习4

九、线性回归 1、概念 假设存在多个点&#xff0c;需要使用一条线来保障尽量拟合这些点&#xff0c;寻找这条线就叫回归。 机器学习中一种有监督学习的算法,回归问题主要关注的是因变量(需要预测的值)和一个或多个数值型的自变量(预测变量)之间的关系。 2、损失函数 存…

【Java EE初阶---多线程(初阶)】初识计算机

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java &#xff0c;Java数据结构 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录…

网络安全之国际主流网络安全架构模型

目前&#xff0c;国际主流的网络安全架构模型主要有&#xff1a; ● 信息技术咨询公司Gartner的ASA&#xff08;Adaptive Security Architecture自适应安全架构&#xff09; ● 美国政府资助的非营利研究机构MITRE的ATT&CK&#xff08;Adversarial Tactics Techniques &…

生成式人工智能(AIGC)在软件开发设计模式课程教学中的应用

一、引言 软件设计模式作为软件工程领域的核心组成部分&#xff0c;对于提升软件系统的质量和可维护性至关重要。然而&#xff0c;传统的软件设计模式课程教学方法面临着诸多挑战&#xff0c;例如教师准备教学案例的过程繁琐&#xff0c;学生理解和应用具体案例难度较大&#…

丹摩征文活动|摩智算平台深度解析:Faster R-CNN模型的训练与测试实战

目录 文章前言Faster R-CNN的简介Faster RCNN的训练与测试提前准备1.1 mobaxterm&#xff08;远程连接服务器&#xff09;1.2 本文的源码下载 目标检测模型 Faster-Rcnn2.1云服务器平台 数据上传内置JupyterLab的使用本地连接使用DAMODEL实例获取实例的SSH访问信息通过SSH连接通…

【实用教程】如何利用 JxBrowser 在 Kotlin 中实现屏幕共享

JxBrowser是一个跨平台的 JVM 库&#xff0c;它允许您将基于 Chromium 的 Browser 控件集成到 Compose、Swing、JavaFX、SWT 应用程序中&#xff0c;并使用 Chromium 的数百种功能。为了在 Kotlin 中实现屏幕共享&#xff0c;我们利用了 Chromium 的 WebRTC 支持以及 JxBrowser…

无人机动力系统节能技术的未来发展趋势——CKESC电调小课堂12.1

无人机动力系统节能技术的未来发展趋势包括以下几个方面&#xff1a; 1. 能源类型多元化与高效化 新型电池技术的发展&#xff1a;锂离子电池的性能将不断提升&#xff0c;能量密度增加、充放电速度加快、循环寿命延长。同时&#xff0c;固态电池技术有望取得突破并应用于无人…

【汇编语言】数据处理的两个基本问题(二) —— 解密汇编语言:数据长度与寻址方式的综合应用

文章目录 前言1. 指令要处理的数据有多长&#xff1f;1.1 通过寄存器指明数据的尺寸1.1.1 字操作1.1.2 字节操作 1.2 用操作符X ptr指明内存单元的长度1.2.1 访问字单元1.2.2 访问字节单元1.2.3 为什么要用操作符X ptr指明 1.3 其他方法 2. 寻址方式的综合应用2.1 问题背景&…

【算法】【优选算法】前缀和(下)

目录 一、560.和为K的⼦数组1.1 前缀和1.2 暴力枚举 二、974.和可被K整除的⼦数组2.1 前缀和2.2 暴力枚举 三、525.连续数组3.1 前缀和3.2 暴力枚举 四、1314.矩阵区域和4.1 前缀和4.2 暴力枚举 一、560.和为K的⼦数组 题目链接&#xff1a;560.和为K的⼦数组 题目描述&#x…

【进阶系列】正则表达式 #匹配

正则表达式 正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的检查一个字符串是否与某种模式匹配。re模块使 Python 语言拥有全部的正则表达式功能。 一个正则表达式的匹配工具&#xff1a;regex101: build, test, and debug regex s "C:\\a\\b\\c" pri…

【技术解析】Dolphinscheduler实现MapReduce任务的高效管理

MapReduce是一种编程模型&#xff0c;用于处理和生成大数据集&#xff0c;主要用于大规模数据集&#xff08;TB级数据规模&#xff09;的并行运算。本文详细介绍了Dolphinscheduler在MapReduce任务中的应用&#xff0c;包括GenericOptionsParser与args的区别、hadoop jar命令参…

Debezium-MySqlConnectorTask

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 MySqlConnectorTask&#xff0c;用于读取MySQL的二进制日志并生成对应的数据变更事件 整体架构流程 技术名词解释 数据库模式&#xff08;Database Schema&#xff09; 数据库模式是指数据库中数据的组织结构和定义&…

剧本杀门店预约小程序,解锁沉浸式推理体验

一、开发背景 剧本杀作为一种热门娱乐游戏&#xff0c;深受大众的欢迎&#xff0c;但随着市场的快速发展&#xff0c;竞争也在不断加大&#xff0c;对于剧本杀线下商家来说面临着发展创新。 剧本杀线下门店数量目前正在逐渐增加&#xff0c;竞争激烈&#xff0c;而门店的获客…

今天你学C++了吗——C++启航之入门知识

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…

【element-tiptap】Tiptap编辑器核心概念----结构篇

core-concepts 前言&#xff1a;这篇文章来介绍一下 Tiptap 编辑器的一些核心概念 &#xff08;一&#xff09;结构 1、 Schemas 定义文档组成方式。一个文档就是标题、段落以及其他的节点组成的一棵树。 每一个 ProseMirror 的文档都有一个与之相关联的 schema&#xff0c;…

LC69---219存在重复元素(滑动窗口)---Java版

1.题目描述 2.思路 3.代码实现 public class Solution { public boolean containsNearbyDuplicate(int[] nums, int k) {Map<Integer,Integer> m1new HashMap<>();// 1:0, 2:1,3:2,1:3 key存数组的值&#xff0c;value存索引&#xff0c;为getnum[i]做准备&am…