概念
就算不用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源碼,分享學習過程,希望對各位有點微小的幫助。
如有錯誤,請指正~)