После использования 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»