性能文章>SpringBoot的CommandLineRunner和ApplicationRunner>

SpringBoot的CommandLineRunner和ApplicationRunner原创

https://a.perfma.net/img/2382850
1年前
249333

1 ApplicationRunner和CommandLineRunner

  • 如果我们想在SpringBoot启动完成之后立即做一些什么业务处理,就可以实现SpringBoot提供的这个俩个接口去完成。注意是启动完成,也就意味着你可以在这个接口里面使用SpringBoot提供的所有功能了。

1.1 ApplicationRunner接口源码

ApplicationRunner这个接口非常简单就一个方法,源码如下:

ApplicationRunner接口源码

  • ApplicationArguments 应用参数处理类,这个类可以处理以双横线开头的参数:–foo=bar

ApplicationArguments类

ApplicationArguments类

1.2 CommandLineRunner接口源码

CommandLineRunner这个接口非常简单就一个方法,源码如下:

CommandLineRunner接口源码

2 先把代码跑起来看看

  • pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.study.demo</groupId>
    <artifactId>spring-boot-runner-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>springbootrunner</name>

    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <!-- lookup parent from repository -->
        <relativePath/>
    </parent>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • ApplicationRunnerDemo类实现了ApplicationRunner接口
package com.study.demo.runner;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * CommandLineRunner 和 ApplicationRunner这俩个类的执行时机都是一样的,都是在SpringBoot启动的倒数第二步开始执行。
 * 倒数第二步基本上等于SpringBoot已经启动完毕了。可以去看SpringApplication.run(Application.class, args);run方法的源码。
 *
 * CommandLineRunner 和 ApplicationRunner这俩个类谁先执行?
 * 同等优先级的情况下ApplicationRunner类先执行。
 * 不同等优先级的情况下:看@Order(1)的值,@Order(1)的值越小,越先执行。
 */
 //@Order(Ordered.HIGHEST_PRECEDENCE)
@Order(2)
@Component
public class ApplicationRunnerDemo implements ApplicationRunner {

    /**
     * java -jar spring-boot-runner-demo-1.0-SNAPSHOT.jar --test=show p1 p2 p3
     * @param args incoming application arguments
     * @throws Exception 业务异常
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {

        System.out.println("ApplicationRunner的作用就是,SpringBoot启动成功之后,我就立马做点事情,主要功能就是项目启动成功之后," +
                "ApplicationRunner默认比CommandLineRunner优先执行");

        System.out.println("ApplicationArguments类的getSourceArgs()方法获取到的是最原始的参数: " +
                "你传的是什么参数,这里拿到的就是什么参数:->" + Arrays.toString(args.getSourceArgs()));

        System.out.println("ApplicationArguments类的getOptionNames()方法获取到的是参数的key值->: " + args.getOptionNames());
        System.out.println("如果传递的参是这样的:--foo=bar --debug,那么getOptionNames()获取到的参数是这样的[\"foo\", \"debug\"]----> " + args.getOptionNames());
        System.out.println("ApplicationArguments类的getNonOptionArgs方法获取到的参数都是没用--开头传递的参数->: " + args.getNonOptionArgs());

        String key = "test";
        if (args.containsOption(key)) {
            System.out.println("如果存在--test这个参数,把test参数的值取出来给大家伙看一下:" + args.getOptionValues(key));
        }

        String noKey = "noKey";
        if (!args.containsOption(noKey)) {
            System.out.println("兄弟们不存在--noKey这个参数" );
        }

        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        System.out.println();
    }
}

  • CommandLineRunnerDemo类实现了CommandLineRunner接口
package com.study.demo.runner;

import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * CommandLineRunner 和 ApplicationRunner这俩个类的执行时机都是一样的,都是在SpringBoot启动的倒数第二步开始执行。
 * 倒数第二步基本上等于SpringBoot已经启动完毕了。可以去看SpringApplication.run(Application.class, args);run方法的源码。
 *
 * CommandLineRunner 和 ApplicationRunner这俩个类谁先执行?
 * 同等优先级的情况下ApplicationRunner类先执行。
 * 不同等优先级的情况下:看@Order(1)的值,@Order(1)的值越小,越先执行。
 */
//@Order(Ordered.HIGHEST_PRECEDENCE)
@Order(1)
@Component
public class CommandLineRunnerDemo implements CommandLineRunner {

    /**
     * java -jar .\spring-boot-runner-demo-1.0-SNAPSHOT.jar --test=show p1 p2 p3
     * @param args incoming main method arguments
     * @throws Exception 业务异常
     */
    @Override
    public void run(String... args) throws Exception {

        System.out.println("CommandLineRunner的作用就是,SpringBoot启动成功之后,我就立马做点事情," +
                "主要功能就是项目启动成功之后,ApplicationRunner默认比CommandLineRunner优先执行");

        System.out.println("CommandLineRunner拿到的是最原始的参数,你传的是什么参数,这里拿到的就是什么参数:" + Arrays.toString(args));

        System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
        System.out.println();
    }
}

  • SpringBoot启动类
package com.study.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    /**
     * 使用 java -jar 命令启动jar包,并给main方法传递参数,参数之间用空格隔开
     * java -jar .\spring-boot-runner-demo-1.0-SNAPSHOT.jar --test=show p1 p2 p3
     * @param args 参数数组
     */
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2.1 将这个SpringBoot项目打成jar项目运行

  • 使用mvn clean package将项目打成jar包

将项目打成jar包

  • 使用java -jar spring-boot-runner-demo-1.0-SNAPSHOT.jar --test=show p1 p2 p3 这个命令运行jar包,注意参数之间用空格隔开。

java -jar 启动项目

启动成功

3 讲解代码

3.1 先看这个类ApplicationRunnerDemo

  • ApplicationRunnerDemo这个类,我们实现了ApplicationRunner接口,并且在类上面使用了@Order(2)注解。

ApplicationRunnerDemo这个类

3.2 再看这个类CommandLineRunnerDemo

  • CommandLineRunnerDemo这个类,我们实现了CommandLineRunner接口,并且在类上面使用了@Order(1)注解。

CommandLineRunnerDemo这个类

3.3 参数是怎么传递过来的?这俩个接口又会在什么时候执行呢?

  • 当我们执行java -jar spring-boot-runner-demo-1.0-SNAPSHOT.jar --test=show p1 p2 p3 这个命令运行jar包这条命令的时候,实际上执行的是SpringBoot启动类的main方法,后面以空格隔开的参数,会传给main方法的参数args,这个是Java的基础功能,不是SpringBoot提供的功能。

参数传递

方法调用

参数传递

SpringBoot的启动的run方法

  • 最关键的地方来了,ApplicationArguments类出现了。注意上图中这行代码,将我们命令行中的参数,通过构造方法传给DefaultApplicationArguments这个类了。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  • 我们来看DefaultApplicationArguments拿到我们的参数之后做了什么处理吧?

DefaultApplicationArguments类源码

Source类

org.springframework.core.env.SimpleCommandLinePropertySource类

解析参数

获取参数

在接口里面获取参数

到这里参数传递,参数解析,获取参数都已经讲完了。

  • 然后,又将applicationArguments这个对象,传给callRunners(context, applicationArguments)方法了;

callRunners方法

调用我们实现的Runner实现类

获取用户传的最原始的参数

获取用户传的最原始的参数

sort()方法排序

ApplicationRunner和CommandLineRunnerDemo的执行时机,也讲完了。

点赞收藏
四千岁

请关注俺的微信公众号:四千岁

请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
3
3