什么是命令行参数
从springboot官网可以知道,通过下面三种方式把一些值当作属性放到spring的上下文中,然后可以再代码中直接读取这些属性的值:
- 通过环境变量方式environment variable:
$ SPRING_APPLICATION_JSON=’{“acme”:{“name”:“test”}}’ java -jar myapp.jar - 通过系统属性system property:
$ java -Dspring.application.json=’{“acme”:{“name”:“test”}}’ -jar myapp.jar - 通过命令行参数command line argument:
$ java -jar myapp.jar --spring.application.json=’{“acme”:{“name”:“test”}}’
我们今天要讲的就是第三种情况,命令行参数。
首先来说一下什么是命令行参数?
从代码上来讲,命令行参数就是springboot启动类里面那个main方法中的那个args参数,程序启动时传给那个args变量的东东就是命令行参数。
怎么传
那么通过什么方式可以传给他呢?这里介绍两个方式
方式一:通过命令行,如下:
java -jar myproject-0.0.1-SNAPSHOT.jar --debug --Mygroup=a,b --filePath=c:/data/1.txt -myargs=2 testNoArg debug=true
方式二:开发是通过idea直接配置,如下:
通过上面两种方式后,–debug --Mygroup=a,b --filePath=c:/data/1.txt -myargs=2 testNoArg debug=true 这部分就会当作参数传给我们springboot启动类的main方法中的args参数了(切记:每项后面加空格)。
有什么用
那么启动程序时设置这个参数有什么用呢?我们结合run方法的代码来说明。
1.通过这些启动时设置参数,可以在启动时向Property中添加属性值。一般我们都是通过application.properties来设置Property的值,然后在程序中可以通过@Value注解来使用。但是如果有些值想在启动时设置,然后通过@Value来获取,那么就可以通过启动参数来设置。所有的这些property都放在springboot官网中说的environment(代码里也是)。springboot启动时关于所有的property还有environment相关的设置,都在SpringApplication这个类的run方法中,源码部分如下:
public ConfigurableApplicationContext run(String... args) {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);【1】
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments)【2】;
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);【3】
afterRefresh(context, applicationArguments);
callRunners(context, applicationArguments);
return context;
}
【1】中便会把传入的参数进行封装,封装的过程可参考下面的【参数封装过程】。然后得到一个applicationArguments ,这里面就是我们的参数,然后【2】处便把参数的内容放到environment中了,然后environment会放到context中【3】。然后才可以通过@Value获取到了。包括application.properties这些文件中内容都是放到environment中。截了一个向environment中放参数的图,如下
如果不想这些参数放到environment中,使用下面的代码禁用:
SpringApplication.setAddCommandLineProperties(false)
2.通过将参数封装到ApplicationArguments 后,当我们在代码中可以直接通过注入的方式获取到我们传入给main方法的参数。例子如下:
@Component
public class MyBean {
@Autowired
public MyBean(ApplicationArguments args) {
// --Mygroup=a,b --filePath=c:/data/1.txt -myargs=2 testNoArg TestNoarg2=3
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs(); //option和nonOption的意思文章下面的【参数封装过程】有介绍
Set<String> optionNames = args.getOptionNames();
for (String optionName : optionNames) {
System.out.println("这是传过来的参数[{}]"+optionName);
}
String[] sourceArgs = args.getSourceArgs();
for (String sourceArg : sourceArgs) {
System.out.println("这是传过来sourceArgs[{}]"+ sourceArg);
}
}
}
参数封装过程
参数封装的代码层次关系大体如下:
SpringApplication.run(ApplicationRunner.class, args)
|-- ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
|-- this.source = new Source(args);
|-- super(args);【1】
|-- super(new SimpleCommandLineArgsParser().parse(args));【2】
CommandLineArgs是一个接口,默认实现类DefaultApplicationArguments。
【1】处调用的是Source的父类SimpleCommandLinePropertySource的构造方法,如下:
【1】处调用父类的构造方法如下:
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
这个构造方法的的参数是一个可变的String类型,所以可以直接把main方法中的参数所有内容都接受到。然后解析这个这些参数内容。解析代码如下:
以参数 --debug --Mygroup=a,b --filePath=c:/data/1.txt -myargs=2 testNoArg TestNoarg2=3 --debug=true 为例子:
class SimpleCommandLineArgsParser {
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
// 如果-- 开头参数如--filePath=c:/data/1.txt
if (arg.startsWith("--")) {
String optionText = arg.substring(2);// 去掉--
String optionName;
String optionValue = null;
//获取参数=后面的内容,如--filePath=c:/data/1.txt最后得到的就是c:/data/1.txt
int indexOfEqualsSign = optionText.indexOf('=');
if (indexOfEqualsSign > -1) {// 如果有=号如--filePath=c:/data/1.txt
optionName = optionText.substring(0, indexOfEqualsSign);//获取=左边的值(filePath)
optionValue = optionText.substring(indexOfEqualsSign + 1);//获取=右边的值(c:/data/1.txt)
}
else {// 没有=号就直接赋值,如例子中的--debug,就直接将debug赋值
optionName = optionText;
}
commandLineArgs.addOptionArg(optionName, optionValue);// 【3】
}
else {
commandLineArgs.addNonOptionArg(arg);// 【4】 不是--开头的参数,直接添加
}
}
return commandLineArgs;
}
}
从上面这段代码【3】【4】可以看出,传入的参数可以分为两类:option和NonOption【这两个概念很重要】。
以两个 – 开头的参数(比如- - debug)为option,其他的都为NonOption