Command arguments detailed analysis of the source code and start the Spring Boot

Used Spring Boot, we all know that you can quickly start Spring Boot project through java -jar. Meanwhile, the configuration may be performed by passing parameters when performing jar -jar. This article walks you look at the system functions and the related source code analysis Spring Boot command-line arguments.

Command line parameters

When you start Spring Boot project, we can pass by way of the following parameters:

java -jar xxx.jar --server.port=8081

Spring Boot using default port 8080, by which the above parameters modified to port 8081, passing command line parameters and have a higher priority, other configuration parameters overrides the same name.

Spring Boot pass parameters when starting the project, there are three parameters in the form:

  • Options parameters
  • Non-option argument
  • System parameters

Options parameters, the above example is the use of option parameters to set the port applications through "--server.port". The basic format "--name = value" ( "-" for the two consecutive minus). Disposed in a configuration equivalent to the effect of application.properties server.port = 8081.

Example of use non-option parameters are as follows:

java -jar xxx.jar abc def 

The above example, "abc" and "def" is a non-option argument.

System parameter, which is provided to the system variables, using the example below:

java -jar -Dserver.port=8081 xxx.jar

Gets the value of the parameter

Options options parametric and non-parametric interfaces can be obtained by ApplicationArguments, this interface can be directly implanted in the specific method of acquiring the parameter class.

@RestController
public class ArgumentsController {
    @Resource
    private ApplicationArguments arguments;
}

Can be obtained by the process parameters corresponding interface provided ApplicationArguments. The interface on the back will explain in detail.

Further, the option parameter may be acquired by directly @Value in the class, as follows:

@RestController
public class ParamController {
    @Value("${server.port}")
    private String serverPort;
}

The system parameters can be obtained through the method provided java.lang.System:

String systemServerPort = System.getProperty("server.port");

The difference between the value of the parameter

About the difference between the value of the parameter, look at the focus options and system parameters. By the above example we have found that when using the option parameter, parameter in the command is passed behind a xxx.jar, and system parameters are immediately following java -jar.

If not performed in this order, such as the use of the option parameter in the following manner:

java -jar --server.port=8081 xxx.jar

It will throw the following exception:

Unrecognized option: --server.port=8081
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

If the system parameters on the jar back pack, the problem will get worse. There will be a normal start, but the parameters can not take effect. This is why sometimes obviously passes parameters but has not entered into force, it is probably because the positional parameters wrong.

This error is the most pit, so be sure to keep in mind: -D parameter passing through the system, be sure to place the jar before the package to be executed.

Another important difference is: You can get the system parameters and options parameters @Value form, but can only be obtained by System.getProperty system parameter method.

ApplicationArguments resolve

The above mentioned parameters can be obtained by injecting ApplicationArguments interface following look at the use of specific examples:

@RestController
public class ArgumentsController {

    @Resource
    private ApplicationArguments arguments;

    @GetMapping("/args")
    public String getArgs() {

        System.out.println("# 非选项参数数量: " + arguments.getNonOptionArgs().size());
        System.out.println("# 选项参数数量: " + arguments.getOptionNames().size());
        System.out.println("# 非选项具体参数:");
        arguments.getNonOptionArgs().forEach(System.out::println);

        System.out.println("# 选项参数具体参数:");
        arguments.getOptionNames().forEach(optionName -> {
            System.out.println("--" + optionName + "=" + arguments.getOptionValues(optionName));
        });

        return "success";
    }
}

By injecting ApplicationArguments interface then it calls the interface method may be obtained in the process corresponding to the parameter information.

ApplicationArguments interface encapsulates an array list, the original parameters of the options parameter, parameter lists, and options and non-option parameters obtained inspection at startup. The relevant source code as follows:

public interface ApplicationArguments {

    /**
     * 原始参数数组(未经过处理的参数)
     */
    String[] getSourceArgs();

    /**
     * 选项参数名称
     */
    Set<String> getOptionNames();

    /**
     * 根据名称校验是否包含选项参数
     */
    boolean containsOption(String name);

    /**
     * 根据名称获得选项参数
     */
    List<String> getOptionValues(String name);

    /**
     * 获取非选项参数列表
     */
    List<String> getNonOptionArgs();
}

Parse command line arguments

Direct injection and the use of the above methods ApplicationArguments, then it is when an object is created, when it was injected into the Spring container?

During execution of the run method SpringApplication get passed parameters, and encapsulated as ApplicationArguments object. Relevant source code is as follows:

public ConfigurableApplicationContext run(String... args) {
        
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // ...
        prepareContext(context, environment, listeners, // ...
    } catch (Throwable ex) {
        // ...
    }
    return context;
}

In the above code, the parameters to complete the command line is parsed by creating a class of its implementation DefaultApplicationArguments.

DefaultApplicationArguments part of the code as follows:

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;
    }
    
    // ...

    @Override
    public List<String> getOptionValues(String name) {
        List<String> values = this.source.getOptionValues(name);
        return (values != null) ? Collections.unmodifiableList(values) : null;
    }

    private static class Source extends SimpleCommandLinePropertySource {
        Source(String[] args) {
            super(args);
        }
        // ...
    }
}

By constructing method, args will be assigned to the member variable args, realized getSourceArgs method in which the interface is returned args ApplicationArguments in value in the class.

For (internal) setting member variable Source, when you create a Source object calls the constructor of its parent class SimpleCommandLinePropertySource:

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

Creating a real parser SimpleCommandLineArgsParser in the process and call its parse method to parse parameters.

class SimpleCommandLineArgsParser {

    public CommandLineArgs parse(String... args) {
        CommandLineArgs commandLineArgs = new CommandLineArgs();
        for (String arg : args) {
            // --开头的选参数解析
            if (arg.startsWith("--")) {
                // 获得key=value或key值
                String optionText = arg.substring(2, arg.length());
                String optionName;
                String optionValue = null;
                // 如果是key=value格式则进行解析
                if (optionText.contains("=")) {
                    optionName = optionText.substring(0, optionText.indexOf('='));
                    optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length());
                } else {
                    // 如果是仅有key(--foo)则获取其值
                    optionName = optionText;
                }
                // 如果optionName为空或者optionValue不为空但optionName为空则抛出异常
                if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {
                    throw new IllegalArgumentException("Invalid argument syntax: " + arg);
                }
                // 封装入CommandLineArgs
                commandLineArgs.addOptionArg(optionName, optionValue);
            } else {
                commandLineArgs.addNonOptionArg(arg);
            }
        }
        return commandLineArgs;
    }
}

Parsing rules above is relatively simple, is based - and parsed to distinguish between different types and parameters "=" "."

Creates an object implementation class ApplicationArguments by the above method, but the moment has not yet injected into the Spring container, into the Spring container is still prepareContext via the process SpringApplication # run method called in to complete. Related code is as follows:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // ...
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 通过beanFactory将ApplicationArguments的对象注入Spring容器
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    // ...
}

So far the relevant source on Spring Boot in ApplicationArguments the completion of the analysis.

Original link: " the Spring and the Boot startup command arguments detailed source code analysis "

Spring Technology Video

CSDN Institute: "the Spring the Boot video tutorials family bucket"


New Horizons program : exciting and growth are not to be missed

New Horizons program - micro-channel public number

Guess you like

Origin www.cnblogs.com/secbro/p/12080818.html