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的相关源码解析完成。
原文链接:《Spring Boot启动命令参数详解及源码分析》
Spring技术视频
CSDN学院:《Spring Boot 视频教程全家桶》
程序新视界:精彩和成长都不容错过

Spring Boot启动命令参数详解及源码分析的更多相关文章
- Android应用AsyncTask处理机制详解及源码分析
1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...
- Java SPI机制实战详解及源码分析
背景介绍 提起SPI机制,可能很多人不太熟悉,它是由JDK直接提供的,全称为:Service Provider Interface.而在平时的使用过程中也很少遇到,但如果你阅读一些框架的源码时,会发现 ...
- 【转载】Android应用AsyncTask处理机制详解及源码分析
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...
- 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)
[1]前言 本篇幅是对 线程池底层原理详解与源码分析 的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解. [2]ScheduledThreadPoolExecut ...
- SpringMVC异常处理机制详解[附带源码分析]
目录 前言 重要接口和类介绍 HandlerExceptionResolver接口 AbstractHandlerExceptionResolver抽象类 AbstractHandlerMethodE ...
- [转]SpringMVC拦截器详解[附带源码分析]
目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:ht ...
- SpringMVC拦截器详解[附带源码分析]
目录 前言 重要接口及类介绍 源码分析 拦截器的配置 编写自定义的拦截器 总结 总结 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:h ...
- SpringMVC类型转换、数据绑定详解[附带源码分析]
目录 前言 属性编辑器介绍 重要接口和类介绍 部分类和接口测试 源码分析 编写自定义的属性编辑器 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那 ...
- SpringMVC拦截器详解[附带源码分析](转)
本文转自http://www.cnblogs.com/fangjian0423/p/springMVC-interceptor.html 感谢作者 目录 前言 重要接口及类介绍 源码分析 拦截器的配置 ...
随机推荐
- 【java】javac命令在win10不可用,提示javac不是内部或外部命令,也不是可运行的程序【解决方法】
JDK安装成功,并且配置了环境变量,java命令正常可以使用,但是javac命令提示 不是内部或外部命令,也不是可运行的程序 解决方法: 产生这个问题的原因,是因为环境变量的配置中,Path中配置使用 ...
- Mysql中分组函数
查询每个工种的最高薪资
- 初始认知学习 .net core 逐步加深
1.一般用空项目练手 2.一般你已经有数据库的情况下使用如下的方式 开始生成类的操作 这里我使用的是Database First模式,使用工具Scaffold-DbContext(数据库上下文脚手架) ...
- Windows Server - 用tomcat部署finereport
原博地址:https://blog.csdn.net/qq_39019865/article/details/80969728
- 0 != null 为什么报指针?
大家好,这是我第一次写博客,来分享我平时工作中遇到的问题及平时学习的技术,如果有写的不好或者不对的地方还望大家能够指出和包涵. 那么接下来就开始说下我工作中遇到的这个问题,我写了一个test,如下: ...
- JS基石之-----数组转换为树结构函数
我们常常在做后台管理系统的时候 需要对数据进行组装成一个树装结构,这里特地将此方法进行封装: let data = [ { id: , text: }, { id: , text: }, { id: ...
- vue+element表单校验功能
要实现这个功能其实并不难,element组件直接用就可以, 但是我在使用过程中碰到了几个坑,就记录下来,分享给大家,避免落坑,话不多说,直接上过程...... 表单校验功能: 实现这个功能,总共分 ...
- ECharts学习指南
1.了解ECharts ECharts简单说就是互联网开发程序过程中,后台数据库用以实现数据到图形的映射的一个插件. 具体来说一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在PC和 ...
- linux echo命令颜色显示
echo命令颜色显示: echo: -n: 不换行. -e:让转移符生效. \t(tab) \n (换行) 实例: $ echo -e "\033[34mabcd\03 ...
- 由MQTT topic的正则表达式匹配引发的特殊字符"/"匹配思考
正则表达式中的'/'替换 近期项目对接OneNET的MQTT物联网套件,需要完成命令下发流程. 流程要求: (1)设备在接收平台下发的命令(topic为$sys/{pid}/{device-name} ...