SPRING BOOT启动命令参数及源码详析

 更新时间:2019年12月23日 08:36:38   作者:程序新视界  
这篇文章主要给大家介绍了关于SPRING BOOT启动命令参数及源码分析的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SPRING BOOT具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目。同时,也可以通过在执行jar -jar时传递参数来进行配置。本文带大家系统的了解一下Spring Boot命令行参数相关的功能及相关源码分析。

命令行参数使用

启动Spring Boot项目时,我们可以通过如下方式传递参数:

java -jar xxx.jar --server.port=8081

默认情况下Spring Boot使用8080端口,通过上述参数将其修改为8081端口,而且通过命令行传递的参数具有更高的优先级,会覆盖同名的其他配置参数。

启动Spring Boot项目时传递参数,有三种参数形式:

  • 选项参数
  • 非选项参数
  • 系统参数

选项参数,上面的示例便是选项参数的使用方法,通过“–-server.port”来设置应用程序的端口。基本格式为“–name=value”(“–”为连续两个减号)。其配置作用等价于在application.properties中配置的server.port=8081。

非选项参数的使用示例如下:

java -jar xxx.jar abc def 

上述示例中,“abc”和“def”便是非选项参数。

系统参数,该参数会被设置到系统变量中,使用示例如下:

java -jar -Dserver.port=8081 xxx.jar

参数值的获取

选项参数和非选项参数均可以通过ApplicationArguments接口获取,具体获取方法直接在使用参数的类中注入该接口即可。

@RestController
public class ArgumentsController {
  @Resource
  private ApplicationArguments arguments;
}

通过ApplicationArguments接口提供的方法即可获得对应的参数。关于该接口后面会详细讲解。

另外,选项参数,也可以直接通过@Value在类中获取,如下:

@RestController
public class ParamController {
  @Value("${server.port}")
  private String serverPort;
}

系统参数可以通过java.lang.System提供的方法获取:

String systemServerPort = System.getProperty("server.port");

参数值的区别

关于参数值区别,重点看选项参数和系统参数。通过上面的示例我们已经发现使用选项参数时,参数在命令中是位于xxx.jar之后传递的,而系统参数是紧随java -jar之后。

如果不按照该顺序进行执行,比如使用如下方式使用选项参数:

java -jar --server.port=8081 xxx.jar

则会抛出如下异常:

Unrecognized option: --server.port=8081
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

如果将系统参数放在jar包后面,问题会更严重。会出现可以正常启动,但参数无法生效。这也是为什么有时候明明传递了参数但是却未生效,那很可能是因为把参数的位置写错了。

这个错误是最坑的,所以一定谨记:通过-D传递系统参数时,务必放置在待执行的jar包之前。

另外一个重要的不同是:通过@Value形式可以获得系统参数和选项参数,但通过System.getProperty方法只能获得系统参数。

ApplicationArguments解析

上面提到了可以通过注入ApplicationArguments接口获得相关参数,下面看一下具体的使用示例:

@RestController
public class ArgumentsController {
  @Resource
  private ApplicationArguments arguments;
  @GetMapping("/args")
  public String getArgs() {
    System.out.println("# 非选项参数数量: " + arguments.getNonOptionArgs().size());
    System.out.println("# 选项参数数量: " + arguments.getOptionNames().size());
    System.out.println("# 非选项具体参数:");
    arguments.getNonOptionArgs().forEach(System.out::println);
    System.out.println("# 选项参数具体参数:");
    arguments.getOptionNames().forEach(optionName -> {
      System.out.println("--" + optionName + "=" + arguments.getOptionValues(optionName));
    });
    return "success";
  }
}

通过注入ApplicationArguments接口,然后在方法中调用该接口的方法即可获得对应的参数信息。

ApplicationArguments接口中封装了启动时原始参数的数组、选项参数的列表、非选项参数的列表以及选项参数获得和检验。相关源码如下:

public interface ApplicationArguments {
  /**
   * 原始参数数组(未经过处理的参数)
   */
  String[] getSourceArgs();
  /**
   * 选项参数名称
   */
  Set<String> getOptionNames();
  /**
   * 根据名称校验是否包含选项参数
   */
  boolean containsOption(String name);
  /**
   * 根据名称获得选项参数
   */
  List<String> getOptionValues(String name);
  /**
   * 获取非选项参数列表
   */
  List<String> getNonOptionArgs();
}

命令行参数的解析

上面直接使用了ApplicationArguments的注入和方法,那么它的对象是何时被创建,何时被注入Spring容器的?

在执行SpringApplication的run方法的过程中会获得传入的参数,并封装为ApplicationArguments对象。相关源代码如下:

public ConfigurableApplicationContext run(String... args) {
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // ...
    prepareContext(context, environment, listeners, // ...
  } catch (Throwable ex) {
    // ...
  }
  return context;
}

在上述代码中,通过创建一个它的实现类DefaultApplicationArguments来完成命令行参数的解析。

DefaultApplicationArguments部分代码如下:

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");
    this.source = new Source(args);
    this.args = args;
  }
  // ...
  @Override
  public List<String> getOptionValues(String name) {
    List<String> values = this.source.getOptionValues(name);
    return (values != null) ? Collections.unmodifiableList(values) : null;
  }
  private static class Source extends SimpleCommandLinePropertySource {
    Source(String[] args) {
      super(args);
    }
    // ...
  }
}

通过构造方法,将args赋值给成员变量args,其中接口ApplicationArguments中getSourceArgs方法的实现在该类中便是返回args值。

针对成员变量Source(内部类)的设置,在创建Source对象时调用了其父类SimpleCommandLinePropertySource的构造方法:

public SimpleCommandLinePropertySource(String... args) {
  super(new SimpleCommandLineArgsParser().parse(args));
}

在该方法中创建了真正的解析器SimpleCommandLineArgsParser并调用其parse方法对参数进行解析。

class SimpleCommandLineArgsParser {
  public CommandLineArgs parse(String... args) {
    CommandLineArgs commandLineArgs = new CommandLineArgs();
    for (String arg : args) {
      // --开头的选参数解析
      if (arg.startsWith("--")) {
        // 获得key=value或key值
        String optionText = arg.substring(2, arg.length());
        String optionName;
        String optionValue = null;
        // 如果是key=value格式则进行解析
        if (optionText.contains("=")) {
          optionName = optionText.substring(0, optionText.indexOf('='));
          optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
        } else {
          // 如果是仅有key(--foo)则获取其值
          optionName = optionText;
        }
        // 如果optionName为空或者optionValue不为空但optionName为空则抛出异常
        if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
          throw new IllegalArgumentException("Invalid argument syntax: " + arg);
        }
        // 封装入CommandLineArgs
        commandLineArgs.addOptionArg(optionName, optionValue);
      } else {
        commandLineArgs.addNonOptionArg(arg);
      }
    }
    return commandLineArgs;
  }
}

上述解析规则比较简单,就是根据“–”和“=”来区分和解析不同的参数类型。

通过上面的方法创建了ApplicationArguments的实现类的对象,但此刻还并未注入Spring容器,注入Spring容器是依旧是通过上述SpringApplication#run方法中调用的prepareContext方法来完成的。相关代码如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  // ...
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  // 通过beanFactory将ApplicationArguments的对象注入Spring容器
  beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  // ...
}

至此,关于Spring Boot中ApplicationArguments的相关源码解析完成。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • Java安全后端返回文件流方式

    Java安全后端返回文件流方式

    这篇文章主要介绍了Java安全后端返回文件流方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Springboot Mybatis-Plus数据库单元测试实战(三种方式)

    Springboot Mybatis-Plus数据库单元测试实战(三种方式)

    这篇文章主要介绍了Springboot Mybatis-Plus数据库单元测试实战(三种方式),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • Spring Boot获取resources目录下的文件三种方式详解

    Spring Boot获取resources目录下的文件三种方式详解

    在Spring Boot项目中,经常需要获取resources目录下的文件,这些文件可以包括配置文件、模板文件、静态资源等,这篇文章主要介绍了Spring Boot获取resources目录下的文件的三种方式,需要的朋友可以参考下
    2023-06-06
  • 学习Java之如何正确地向上转型与向下转型

    学习Java之如何正确地向上转型与向下转型

    面向对象的第三个特征是多态,实现多态有三个必要条件:继承、方法重写和向上转型,在学习多态之前,我们还要先学习Java的类型转换,本篇文章就来带大家认识什么是类型转换,看看类型转换都有哪几种情况,以及如何避免类型转换时出现异常
    2023-05-05
  • Java 读取类路径下的资源文件实现代码

    Java 读取类路径下的资源文件实现代码

    这篇文章主要介绍了Java 读取类路径下的资源文件实现代码的相关资料,主要实现工具类代码ResourceLoadUtil.java的实例需要的朋友可以参考下
    2017-07-07
  • Java中Stringbuild,Date和Calendar类的用法详解

    Java中Stringbuild,Date和Calendar类的用法详解

    这篇文章主要为大家详细介绍了Java中Stringbuild、Date和Calendar类的用法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-04-04
  • Mybatis批量更新报错问题

    Mybatis批量更新报错问题

    这篇文章主要介绍了Mybatis批量更新报错的问题及解决办法,包括mybatis批量更新的两种方式,需要的的朋友参考下
    2017-01-01
  • SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解决方案

    SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解决方案

    这篇文章主要介绍了SpringBoot2.0整合SpringCloud Finchley @hystrixcommand注解找不到解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • springboot log4j2.xml如何读取application.yml中属性值

    springboot log4j2.xml如何读取application.yml中属性值

    这篇文章主要介绍了springboot log4j2.xml如何读取application.yml中属性值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java实现快速并查集

    Java实现快速并查集

    这篇文章主要为大家详细介绍了Java实现快速并查集,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07

最新评论