spring5/springboot2源码学习 -- spring中对命令行参数的处理

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-15 06:59:33

概念

就算不用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源码,分享学习过程,希望对各位有点微小的帮助。
如有错误,请指正~)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!