概念
就算不用spring,我们肯定也知道命令行参数。其实就算你不用java,你也应该知道命令行参数😂
在启动一个通过spring boot打的fatjar形式的Java程序时,我们可能用如下的命令去启动:
java -Xmx6g -XX:SurvivorRatio=4 -Djava.net.preferIPv4Stack=true --spring.profiles.active=prod -jar study-spring.jar > xx.log
常见用法
package com.pk.study.spring.env;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import org.springframework.boot.DefaultApplicationArguments;
import org.springframework.core.env.SimpleCommandLinePropertySource;
/**
* @author pengkai
* @date 2019/12/9
*/
public class CommandLineArgsTest {
private static final Logger logger = LogManager.getLogger();
@Test
public void test() {
//模拟启动时传入的命令行参数
//一个key可以指定多个值,不同值用,分隔
//一个key也可以出现多次,他的值最终会自己合并
String[] args = {"--a", "--b=bb","--c=a,b,c","--c=d", "-Dc=true"};
//使用方式一:通过ApplicationArguments,这是spring boot中才有的
DefaultApplicationArguments arguments = new DefaultApplicationArguments(args);
logger.info(arguments.getOptionValues("c"));
logger.info(arguments.getNonOptionArgs());
//使用方式二:通过PropertySource
SimpleCommandLinePropertySource ps = new SimpleCommandLinePropertySource(args);
logger.info(ps.getProperty("b"));
logger.info(ps.getProperty("c"));
}
}
ApplicationArguments是spring boot中的接口,作用就是保存启动时的命令行参数
SimpleCommandLinePropertySource是一个PropertySource的实现类,是spring用于Environment,表示来源于命令行参数的配置项。关于Environment和PropertySource的分析请见:Environment分析
实现解析
无论是spring boot中基于ApplicationArguments使用,还是Environment中基于SimpleCommandLinePropertySource使用。最终的入口都是:
public SimpleCommandLinePropertySource(String... args) {
super(new SimpleCommandLineArgsParser().parse(args));
}
所以主要的功能就是SimpleCommandLineArgsParser.parse()函数:
public CommandLineArgs parse(String... args) {
CommandLineArgs commandLineArgs = new CommandLineArgs();
for (String arg : args) {
if (arg.startsWith("--")) {
String optionText = arg.substring(2, arg.length());
String optionName;
String optionValue = null;
if (optionText.contains("=")) {
optionName = optionText.substring(0, optionText.indexOf('='));
optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
}
else {
optionName = optionText;
}
if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
}
commandLineArgs.addOptionArg(optionName, optionValue);
}
else {
commandLineArgs.addNonOptionArg(arg);
}
}
return commandLineArgs;
}
可以,spring 根据命令行参数是否是–开头,将参数分为两类:optionArg、nonOptionArg
在继续看CommandLineArgs类:
class CommandLineArgs {
//value是List<String>
private final Map<String, List<String>> optionArgs = new HashMap<>();
//并没有弄成key value的形式
private final List<String> nonOptionArgs = new ArrayList<>();
//对于optionArg,这里是保存成了Map<String, List<String>>的形式
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 void addNonOptionArg(String value) {
this.nonOptionArgs.add(value);
}
//忽略不重要的其他方法
}
可以看到,对于optionArg,spring会将其解析成键值对的形式,并且值是List,可能你会奇怪,为啥value是个List?因为,spring是允许一个key指定多个值的,比如:
–spring.profiles.active=a,b,c
##结语
这个部分涉及到了spring和spring boot的以下类:
- ApplicationArguments和DefaultApplicationArguments
- SimpleCommandLineArgsParser和CommandLineArgs
共计4个类。
(水平有限,最近在看spring源码,分享学习过程,希望对各位有点微小的帮助。
如有错误,请指正~)
来源:CSDN
作者:pumpkin_pk
链接:https://blog.csdn.net/yuxiuzhiai/article/details/103465149