Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析

接上篇博客Spring Boot 2.1.1(八)启动流程分析之ApplicationStartingEvent事件的发布监听流程

目录

流程分析

1、DefaultApplicationArguments构造方法

2、参数解析

3、测试验证


流程分析

发布ApplicationStartingEvent后就是封装args参数。

public ConfigurableApplicationContext run(String... args) {
	
    ....
        
        //本篇博客从本行开始记录
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
        
            //本篇内容记录到这,后续更新
        ....
    }
	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 2.1.1(九)启动流程分析之args参数的封装解析

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

public SimpleCommandLinePropertySource(String... args) {
	super(new SimpleCommandLineArgsParser().parse(args));
}

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。

Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析

Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析

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

Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析

3、测试验证

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

Spring Boot 2.1.1(九)启动流程分析之args参数的封装解析

运行。 

@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 2.1.1(九)启动流程分析之args参数的封装解析

成功!