Подробное объяснение параметров команды запуска Spring Boot и анализ исходного кода

Java

После использования Spring Boot мы все знаем, что можем быстро запустить проект Spring Boot через java -jar. В то же время его также можно настроить, передав параметры при выполнении jar -jar. Эта статья поможет вам систематически понять функции, связанные с параметрами командной строки Spring Boot, и соответствующим анализом исходного кода.

использование параметра командной строки

При запуске проекта Spring Boot мы можем передать параметры следующим образом:

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

По умолчанию Spring Boot использует порт 8080, который изменяется на порт 8081 с помощью указанных выше параметров, а параметры, передаваемые через командную строку, имеют более высокий приоритет и переопределяют другие параметры конфигурации с тем же именем.

Параметры передаются при запуске проекта Spring Boot Существует три формы параметров:

  • параметр опции
  • необязательные аргументы
  • Системные параметры

Параметры опции, приведенный выше пример показывает, как использовать параметры опции, установить порт приложения через «--server.port». Основной формат — «--name=value» («--» — два последовательных знака минус). Его конфигурация эквивалентна server.port=8081, настроенному в application.properties.

Пример использования необязательных параметров выглядит следующим образом:

java -jar xxx.jar abc def 

В приведенном выше примере «abc» и «def» не являются необязательными аргументами.

Системный параметр, этому параметру будет присвоена системная переменная, пример использования следующий:

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

получить значение параметра

Через интерфейс ApplicationArguments можно получить как параметры параметров, так и параметры, не являющиеся параметрами. Конкретный метод получения можно напрямую внедрить в интерфейс в классе, использующем параметр.

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

Соответствующие параметры можно получить с помощью методов, предоставляемых интерфейсом ApplicationArguments. Подробно интерфейс будет объяснен позже.

Кроме того, параметры опций также можно получить непосредственно в классе через @Value следующим образом:

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

Системные параметры можно получить с помощью методов, предоставляемых java.lang.System:

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

Различия в значениях параметров

Что касается разницы между значениями параметров, сосредоточьтесь на параметрах опций и параметрах системы. В приведенном выше примере мы обнаружили, что при использовании параметра option параметр передается в команде после xxx.jar, а системный параметр — сразу после java -jar.

Например, если вы не следуете этому порядку, используйте параметр option следующим образом:

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

Будет выброшено следующее исключение:

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

Если системные параметры размещаются после пакета jar, проблема будет более серьезной. Будет казаться, что его можно запустить нормально, но параметры не могут вступить в силу. По этой же причине иногда параметр передается, но не действует, вероятно, из-за неправильного положения параметра.

Эта ошибка самая досадная, поэтому обязательно помните: при передаче системных параметров через -D обязательно размещайте их перед исполняемым jar-пакетом.

Еще одно важное отличие состоит в том, что системные параметры и параметры опций можно получить через форму @Value, но только системные параметры можно получить через метод System.getProperty.

Разрешение аргументов приложения

Как упоминалось выше, соответствующие параметры можно получить, внедрив интерфейс ApplicationArguments.Давайте рассмотрим конкретный пример использования:

@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";
    }
}

Путем внедрения интерфейса ApplicationArguments и последующего вызова метода интерфейса в методе можно получить соответствующую информацию о параметрах.

Интерфейс ApplicationArguments инкапсулирует массив исходных параметров при запуске, список параметров параметров, список параметров, не являющихся параметрами, а также получение и проверку параметров параметров. Соответствующий исходный код выглядит следующим образом:

public interface ApplicationArguments {

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

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

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

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

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

Разбор аргументов командной строки

Внедрение и метод ApplicationArguments используются непосредственно выше, поэтому когда его объект создается и внедряется в контейнер Spring?

В процессе выполнения метода run SpringApplication входящие параметры получаются и инкапсулируются как объекты ApplicationArguments. Соответствующий исходный код выглядит следующим образом:

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

В приведенном выше коде анализ аргументов командной строки выполняется путем создания класса DefaultApplicationArguments, который его реализует.

Часть кода DefaultApplicationArguments выглядит следующим образом:

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

Через метод построения присвойте args переменной-члену args, в которой реализация метода getSourceArgs в интерфейсе ApplicationArguments возвращает значение args в этом классе.

Для установки переменной-члена Source (внутренний класс) конструктор его родительского класса SimpleCommandLinePropertySource вызывается при создании объекта Source:

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

В этом методе создается реальный анализатор SimpleCommandLineArgsParser и вызывается его метод parse для анализа параметров.

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

Приведенные выше правила синтаксического анализа относительно просты: различать и анализировать различные типы параметров в соответствии с «--» и «=».

Объект класса реализации ApplicationArguments создается с помощью вышеуказанного метода, но он еще не был внедрен в контейнер Spring.Внедрение в контейнер Spring все еще выполняется с помощью метода prepareContext, вызываемого в методе SpringApplication#run выше. Соответствующий код выглядит следующим образом:

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

На данный момент соответствующий анализ исходного кода ApplicationArguments в Spring Boot завершен.

Оригинальная ссылка: "Подробное объяснение параметров команды запуска Spring Boot и анализ исходного кода

Весеннее техническое видео

Академия CSDN:«Семейное ведро Spring Boot Video Tutorial»


Программа Новые Горизонты: Захватывающие и растущие нельзя пропустить

程序新视界-微信公众号