SpringBoot启动流程分析之命令行参数args的封装解析(三)

目录


文章目录

  • SpringBoot启动流程分析之命令行参数args的封装解析(三)
  • 1、DefaultApplicationArguments构造方法
  • 2、参数解析
  • 3、测试验证



流程分析发布ApplicationStartingEvent后就是封装args参数。

org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {
		...
		try {
           //  开始分析...
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		...
	}
1、DefaultApplicationArguments构造方法

可以看到args参数在构造方法中在作为参数放到了DefaultApplicationArguments的静态内部类Source的构造方法中进一步封装,至于下面的全局变量args放的就是原始的args参数。可以通过getSourceArgs方法得到。

public class DefaultApplicationArguments implements ApplicationArguments {

	private final Source source;

	private final String[] args;

	public DefaultApplicationArguments(String[] args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
	}
    
private static class Source extends SimpleCommandLinePropertySource {

    Source(String[] args) {
        super(args);
    }

    @Override
    public List<String> getNonOptionArgs() {
        return super.getNonOptionArgs();
    }

    @Override
    public List<String> getOptionValues(String name) {
        return super.getOptionValues(name);
    }

}
    
// 其它方法忽略...
2、参数解析

类图

spring boot 单元测试里面如何加启动参数 springboot启动参数指定_构造方法

在Source的父类SimpleCommandLinePropertySource的构造方法中new了一个SimpleCommandLineArgsParser对象,然后调用parse()方法解析参数,如下。

public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {

	/**
	 * Create a new {@code SimpleCommandLinePropertySource} having the default name
	 * and backed by the given {@code String[]} of command line arguments.
	 * @see CommandLinePropertySource#COMMAND_LINE_PROPERTY_SOURCE_NAME
	 * @see CommandLinePropertySource#CommandLinePropertySource(Object)
	 */
	public SimpleCommandLinePropertySource(String... args) {
		super(new SimpleCommandLineArgsParser().parse(args));
	}
    
 }
class SimpleCommandLineArgsParser {

	/**
	 * Parse the given {@code String} array based on the rules described {@linkplain
	 * SimpleCommandLineArgsParser above}, returning a fully-populated
	 * {@link CommandLineArgs} object.
	 * @param args command line arguments, typically from a {@code main()} method
	 */
	public CommandLineArgs parse(String... args) {
        //创建CommandLineArgs对象
		CommandLineArgs commandLineArgs = new CommandLineArgs();
		for (String arg : args) {
			if (arg.startsWith("--")) {
                //如果是就截取,截取长度是参数长度,如果命令是--spring,那么就从s开始截取,包括s
				String optionText = arg.substring(2, arg.length());
				String optionName;
				String optionValue = null;
                //如果匹配到了=号,截取=号左边做optionName,右边做optionValue
				if (optionText.contains("=")) {
					optionName = optionText.substring(0, optionText.indexOf('='));
					optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
				}
				else {    
                    //如果没有=号,optionName 直接就是截取的optionText
					optionName = optionText;
				}
				if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
					throw new IllegalArgumentException("Invalid argument syntax: " + arg);
				}
                //这里将解析的参数添加到上面创建的CommandLineArgs对象中,该对象中有一个Map<String, List<String>>来存放
				commandLineArgs.addOptionArg(optionName, optionValue);
			}
			else {
				commandLineArgs.addNonOptionArg(arg);
			}
		}
		return commandLineArgs;
	}

}

回过头继续跟踪到父类CommandLinePropertySource构造方法,这里又传了一个字符串"commandLineArgs"到父类构造,为默认name。

通过以上的类图关系,最后是赋值给了PropertySource的name和source。该类用来存放key/value属性对的抽象基类。value可以是任意类型。到这DefaultApplicationArguments对象的创建流程就走完了。

spring boot 单元测试里面如何加启动参数 springboot启动参数指定_构造方法_02


spring boot 单元测试里面如何加启动参数 springboot启动参数指定_spring_03


spring boot 单元测试里面如何加启动参数 springboot启动参数指定_spring cloud_04

3、测试验证

可以验证一下。右键->run as->run configurations,添加一个命令行参数。

spring boot 单元测试里面如何加启动参数 springboot启动参数指定_spring_05

@SpringBootApplication
public class Application {
	public static void main(String[] args) {
//		SpringApplication.run(Application.class, args);
		
		SpringApplication application = new SpringApplication(Application.class);
		ConfigurableApplicationContext context = application.run(args);
		ApplicationArguments arguments = context.getBean(ApplicationArguments.class);
		System.out.println(context.getEnvironment().getPropertySources().get("commandLineArgs").getProperty("spring.config.location"));
		System.out.println("name:"+arguments.getOptionNames()+" , value:"+arguments.getOptionValues("spring.config.location"));
		context.close();
	}
}

spring boot 单元测试里面如何加启动参数 springboot启动参数指定_spring cloud_06

补充一下:命令行参数在cmd中添加如下,先在项目根目录执行mvn clean install ,然后到target目录或者在jar包前面加上target目录都行,执行图片中的命令运行, 图片是从我后面文章复制过来的,主要是说明命令行参数在cmd中怎么加

spring boot 单元测试里面如何加启动参数 springboot启动参数指定_spring_07