spring5/springboot2源碼學習 -- spring中對命令行參數的處理

概念

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章