Spring Boot源码分析六:解析启动args参数

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

run

java 复制代码
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   // 这是Spring的上下文,在这里定义,在下面进行的初始化
   ConfigurableApplicationContext context = null;
   // 配置一个系统属性
   configureHeadlessProperty();
   // 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
   // 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

DefaultApplicationArguments

这个类的构造方法中做了一些事情,看一下构造方法。

java 复制代码
public DefaultApplicationArguments(String... args) {
// 参数判断 自己的业务中可以用这个代替一些if else
   Assert.notNull(args, "Args must not be null");
   // 创建一个资源对象。这个资源对象中解析了环境信息和参数信息
   this.source = new Source(args);
   // 将我们的args赋值给了全局变量
   this.args = args;
}

重点看一下构造这个source的时候做了什么事情。

new Source(args)

这个类的继承关系。

解析args参数。封装成CommandLineArgs。

java 复制代码
public SimpleCommandLinePropertySource(String... args) {
   super(new SimpleCommandLineArgsParser().parse(args));
}

解析args参数封装成CommandLineArgs。通过这个方法我们知道我们的args参数应该怎么传了,是以双横线开始的,key和value之间用等号连接。

java 复制代码
public CommandLineArgs parse(String... args) {
   CommandLineArgs commandLineArgs = new CommandLineArgs();
   for (String arg : args) {
      if (arg.startsWith("--")) {
         String optionText = arg.substring(2);
         String optionName;
         String optionValue = null;
         int indexOfEqualsSign = optionText.indexOf('=');
         if (indexOfEqualsSign > -1) {
            optionName = optionText.substring(0, indexOfEqualsSign);
            optionValue = optionText.substring(indexOfEqualsSign + 1);
         }
         else {
            optionName = optionText;
         }
         if (optionName.isEmpty()) {
            throw new IllegalArgumentException("Invalid argument syntax: " + arg);
         }
         commandLineArgs.addOptionArg(optionName, optionValue);
      }
      else {
         commandLineArgs.addNonOptionArg(arg);
      }
   }
   return commandLineArgs;
}

addOptionArg

每解析一个就添加到CommandLineArgs中。

保存在了这个对象的optionArgs属性中。根据这个属性和这个addOptionArg方法的实现来看,如果同一个key配置了两次会同一个key保存一个集合。

java 复制代码
private final Map<String, List<String>> optionArgs = new HashMap<>();
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);
   }
}

OK 今天先到这里吧。

See you next time :)

相关推荐
Dovis(誓平步青云)20 分钟前
多个链接不等于多份证据,新闻核验看板怎样合并来源
java·服务器·前端·javascript·人工智能·pdf·电脑
旺仔Sec39 分钟前
2026年江西省职业院校技能大赛(中职组)应用软件系统开发赛项竞赛样题
java·应用软件系统开发
Dovis(誓平步青云)1 小时前
几个方案来回选不定?做一个随时切换的候选推荐页
android·java·服务器·开发语言·javascript·数据库·智能化
一条破秋裤1 小时前
Linux 线程同步:读写锁
java·linux·jvm
蜗牛互联网1 小时前
从Barclays扩大Claude部署看受监管软件工程的责任重分配
java·大数据·人工智能·后端·软件工程
天远API2 小时前
零信任架构实战:基于天远股权穿透构建自动化供应链授信穿透网关
java·人工智能·架构·自动化
最强小杰2 小时前
claude-opus-5.5 API 接入教程:anthropic-version 头、tools 定义、流式事件格式三个常见问题全部梳理,收藏备用
java·服务器·网络·ai
Cc.Y2 小时前
Java零基础入门:方法(函数)深度掌握
java·开发语言
奋发向前wcx2 小时前
CSP-J复赛模拟赛1 王晨旭补题 2026.10.2
java·开发语言
卓怡学长2 小时前
w192基于springboot“考研情报站”微信小程序设计与实现
java·spring boot·spring·微信小程序·intellij-idea