springboot源码分析5-springboot之命令行参数以及原理

简介: 摘要:本文我们重点分析一下Springboot框架中的命令行参数的使用以及框架内部处理的命令行参数的原理。众所周知,springboot项目可以有两种方式启动,第一种使用jar包;第二种使用war包。

摘要:本文我们重点分析一下Springboot框架中的命令行参数的使用以及框架内部处理的命令行参数的原理。

众所周知,springboot项目可以有两种方式启动,第一种使用jar包;第二种使用war包。在使用jar方式的时候,我们可以在启动jar包的时候设置一些命令参数。

1.1 命令行参数使用

首先我们看一下如何使用在项目启动的时候设置命令行参数以及值。我这里使用的开发工具是Spring Tool Suite 版本是: 3.9.0.RELEASE。我们先建立一个工程文件,目录结构如下图所示:

                             

Application内容如下:

1 @EnableConfigurationProperties

2 public class Application {

3  public static void main(String[] args) {

4  ConfigurableApplicationContext configurableApplicationContext =    SpringApplication.run(Application.class, args);

5  }

6 }

 

怎么启动上述的类呢?操作步骤如下:

第一步:点击 Debug AS-->>Debug Configurations

 

 

第二步:

 

 

首先,我们需要点击①箭头处的新增按钮,然后就会建立一个Spring Boot App、其次我们输入命令行的参数,然后点击App即可完成设置并启动项目。上图中我们设置了三个变量,如下所示:

--foo=bar  -foo=bar1  --foo=bar2

   上述这三个变量我们该如何获取呢?这也是校验参数是否设置成功的一个途径吧相信大家都想知道下一步的操作,下面的实例代码还是在上文的两个步骤为前提下进行。实例代码如下:

1 @EnableConfigurationProperties

2 public class Application {

3  public static void main(String[] args) {

4  ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(Application.class, args);

5  String property = configurableApplicationContext.getEnvironment().getProperty("foo");

6  System.out.println(property);

7  }

8 }

运行上述的代码,控制台的输出信息如下:

bar,bar2

   我们通过ConfigurableApplicationContext实例获取环境ConfigurableEnvironment实例对象,然后通过ConfigurableEnvironment获取属性foo。ConfigurableEnvironment大家暂时先有个印象,我们通过这个实例对象获取到项目中所有的配置属性信息。这个我们后续也会详细的进行讲解。看到上面的输出,发现输出的是bar,bar2

,然而bar1并没有输出?为什么呢?大家看下这三个命令行参数有何迥异,很显然bar,bar2的参数属性都是--开头的,而bar1是-开头的。那我们就很好奇springboot是如何获取这些参数值以及解析的呢?

   上述中的启动类是Application,该类中的main方法去启动Springboot项目,既然是main方法,所以我们上文提到的三个命令行参数最终会被设置到args参数中的。这一点大家一定要注意了。SpringApplication.run方法会将args参数继续传递到SpringApplication类中让框架处理的。接下来,我们看一下SpringApplication.run方法中关于命令行参数的相关处理逻辑吧。

1.2 命令行参数原理

 我们开始跟进SpringApplication类中的run(String... args)方法,相关的代码如下所示:

1 public ConfigurableApplicationContext run(String... args) {

2  ...//省略

3  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

4  ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

5  ...//省略

6  }

1.2.1 DefaultApplicationArguments类

     首先,我们看一下DefaultApplicationArguments类,该类的核心代码如下:

1 public class DefaultApplicationArguments implements ApplicationArguments {

2  private final Source source;

3  private final String[] args;

4  public DefaultApplicationArguments(String[] args) {

5      Assert.notNull(args, "Args must not be null");

6      this.source = new Source(args);

7      this.args = args;

8  }

DefaultApplicationArguments类的构造函数中,首先实例化Source类,并将args参数以及值进行传递。然后在自身类中使用args属性进行参数值的报错,DefaultApplicationArguments类实现了ApplicationArguments接口。ApplicationArguments 接口中定义了各种命令行参数的操作,比如参数值的获取、参数名称的获取等方法。

上面的代码感觉没有什么神奇的地方,貌似只是对命令行参数的各种封装而已。其实Source类大有玄机。该类的代码如下所示:

1 private static class Source extends SimpleCommandLinePropertySource {

2  Source(String[] args) {

3  super(args);

4  }

5 ...//省略

6  }

     Source类继承SimpleCommandLinePropertySource类,并在当前类的构造函数调用SimpleCommandLinePropertySource 类的构造函数,SimpleCommandLinePropertySource 类的构造函数如下所示:

1 public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> {

2  public SimpleCommandLinePropertySource(String... args) {

3  super(new SimpleCommandLineArgsParser().parse(args));

4  }

    虽然SimpleCommandLinePropertySource类的构造函数继续调用CommandLinePropertySource类的构造函数进行处理,但是我们只需要将new SimpleCommandLineArgsParser().parse(args)这行代码搞明白,关于命令行参数以及值的处理我们就可以搞明白了。接下来我们快速看一下parse方法的实现逻辑,实例代码如下:

1 public CommandLineArgs parse(String... args) {

2  CommandLineArgs commandLineArgs = new CommandLineArgs();

3  for (String arg : args) {

4    if (arg.startsWith("--")) {

5            String optionText = arg.substring(2, arg.length());

6            String optionName;

7            String optionValue = null;

8           if (optionText.contains("=")) {

9                   optionName = optionText.substring(0, optionText.indexOf("="));

10                   optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length());

11            }

12          else {

13         optionName = optionText;

14       }

15  if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) {

16              throw new IllegalArgumentException("Invalid argument syntax: " + arg);

17  }

18        commandLineArgs.addOptionArg(optionName, optionValue);

19  }

20  else {

21           commandLineArgs.addNonOptionArg(arg);

22  }

23  }

24        return commandLineArgs;

25  }

我们将上述代码的逻辑梳理如下:

① 实例化CommandLineArgs类。这个类封装了命令行解析之后的参数以及值信息、还有没有被识别的命令行参数以及值的信息。

② 循环遍历所有的参数以及值args,比如我们传递的是--foo=bar  -foo=bar1  --foo=bar2

③ 如果参数以--开头,则开始如下的操作;否则将其作为不能识别的参数进行处理,上文我们定义的-foo就是不能被识别的参数。

截取--字符串并使用optionText变量进行存储,比如参数--foo=bar截取之后,则optionText值为foo=bar。

optionName为foo,optionValue为bar,通过上述代码逻辑可以看出,如果=前边或者后边出现了空格就惨了。因为这个地方的代码并没有对空格以及特殊字符进行区分。

1.2.1.1. 识别参数添加

commandLineArgs.addOptionArg(optionName, optionValue);进行springboot可识别的命令行参数的添加工作,其内部实现逻辑如下所示:

1 private final Map<String, List<String>> optionArgs = new HashMap<>();

2 public void addOptionArg(String optionName, @Nullable String optionValue) {

3  if (!this.optionArgs.containsKey(optionName)) {

4  this.optionArgs.put(optionName, new ArrayList<>());

5  }

6  if (optionValue != null) {

7  this.optionArgs.get(optionName).add(optionValue);

8  }

9  }

上述代码中,首先校验optionName参数值是否存在于optionArgs集合中。如果不存在,则直接实例化ArrayList并将其添加到optionArgs集合中。

注意:optionArgs的key为参数的名称,value是一个List集合,存储的是该参数的所有值。通过这里的处理我们可以看出,我们上文输出的foo参数的值是bar,bar2就很容易理解了。

1.2.1.2. 未知参数添加

commandLineArgs.addNonOptionArg(arg)方法进行未知参数的添加逻辑,比如上文中的-foo=bar1参数就在这个方法进行处理的额,因为该参数不是--开头的。commandLineArgs.addNonOptionArg(arg)方法如下所示:

1 private final List<String> nonOptionArgs = new ArrayList<>();

2 public void addNonOptionArg(String value) {

3  this.nonOptionArgs.add(value);

4  }

未知参数最终被添加到了nonOptionArgs集合。

讲解到这里基本上命令行参数的设置以及解析原理都搞明白了。


欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Java相关原创技术干货。 
扫一扫下方二维码或者长按识别二维码,即可关注。
 

   


 

相关文章
|
1月前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
36 0
|
3天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
55 14
|
1月前
|
Java Spring
SpringBoot自动装配的原理
在Spring Boot项目中,启动引导类通常使用`@SpringBootApplication`注解。该注解集成了`@SpringBootConfiguration`、`@ComponentScan`和`@EnableAutoConfiguration`三个注解,分别用于标记配置类、开启组件扫描和启用自动配置。
59 17
|
1月前
|
Java 容器
springboot自动配置原理
启动类@SpringbootApplication注解下,有三个关键注解 (1)@springbootConfiguration:表示启动类是一个自动配置类 (2)@CompontScan:扫描启动类所在包外的组件到容器中 (3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效
|
2月前
|
Java Spring 容器
springboot @RequiredArgsConstructor @Lazy解决循环依赖的原理
【10月更文挑战第15天】在Spring Boot应用中,循环依赖是一个常见问题,当两个或多个Bean相互依赖时,会导致Spring容器陷入死循环。本文通过比较@RequiredArgsConstructor和@Lazy注解,探讨它们解决循环依赖的原理和优缺点。@RequiredArgsConstructor通过构造函数注入依赖,使代码更简洁;@Lazy则通过延迟Bean的初始化,打破创建顺序依赖。两者各有优势,需根据具体场景选择合适的方法。
121 4
|
2月前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
3月前
|
JSON Java 数据格式
springboot 参数统一处理
springboot 参数统一处理
|
2月前
|
监控 Java Maven
springboot学习二:springboot 初创建 web 项目、修改banner、热部署插件、切换运行环境、springboot参数配置,打包项目并测试成功
这篇文章介绍了如何快速创建Spring Boot项目,包括项目的初始化、结构、打包部署、修改启动Banner、热部署、环境切换和参数配置等基础操作。
164 0
|
3月前
|
Java Spring
spring boot 启动项目参数的设定
spring boot 启动项目参数的设定