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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值