ApplicationRunner 接口和 CommandLineRunner 类似,在 Spring 容器启动完成时,继承 ApplicationRunner 接口的类的 run 方法会被自动执行(前提是这个类被Spring 管理)。相比于 CommandLineRunner, ApplicationRunner 提供了对命令行参数的解析功能。
示例1:入门
demo01 项目结构:
demo01
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner.java
│ │ └── Demo01App.java
│ └── resources
└── test
├── java
└── resources
build.gradle 内容:
buildscript {
ext {
springBootVersion = '2.1.3.RELEASE'
}
repositories {
maven { url 'http://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
maven { url 'http://mirrors.cloud.tencent.com/nexus/repository/maven-public/' }
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Demo01App 类内容:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Demo01App {
public static void main(String[] args) {
SpringApplication.run(Demo01App.class, args);
}
}
CustomApplicationRunner 类内容:
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
执行 Demo01App ,输出:
This is CustomApplicationRunner
示例2:获取命令行参数
demo02 项目结构:
demo02
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner.java
│ │ └── Demo02App.java
│ └── resources
└── test
├── java
└── resources
Demo02App 类代码:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Demo02App {
public static void main(String[] args) {
SpringApplication.run(Demo02App.class, args);
}
}
CustomApplicationRunner 类代码:
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
// 获取所有命令行参数
System.out.println("获取所有命令行参数");
for (String arg: args.getSourceArgs()) {
System.out.println(arg);
}
System.out.println("------");
// 获取所有选项
System.out.println("获取所有选项");
for (String option: args.getOptionNames()) {
System.out.println(option);
}
System.out.println("------");
// 获取解析后的参数,以 --msg 为例子
System.out.println("获取解析后的参数,以 --msg 为例子");
if (args.containsOption("msg")) {
System.out.println("--msg选项的值有:");
for (String s:args.getOptionValues("msg")) {
System.out.println(s);
}
}
System.out.println("------");
// 获取无选项指定的参数
System.out.println("获取无选项指定的参数");
for (String val: args.getNonOptionArgs()) {
System.out.println(val);
}
}
}
containsOption 函数可以用来判断是否存在某个选项,可以当做开关用,也可以当做kv键值对的键来用。
进入 demo02 目录,打包,看下执行效果:
$ gradle build
$ java -jar ./build/libs/demo02-0.0.1-SNAPSHOT.jar --msg
This is CustomApplicationRunner
获取所有命令行参数
--msg
------
获取所有选项
msg
------
获取解析后的参数,以 --msg 为例子
--msg选项的值有:
------
获取无选项指定的参数
$ java -jar ./build/libs/demo02-0.0.1-SNAPSHOT.jar --msg=hi test.txt
This is CustomApplicationRunner01
获取所有命令行参数
--msg=hi
test.txt
------
获取所有选项
msg
------
获取解析后的参数,以 --msg 为例子
--msg选项的值有:
hi
------
获取无选项指定的参数
test.txt
$ java -jar ./build/libs/demo02-0.0.1-SNAPSHOT.jar --msg=hi --msg=hi2 test.txt
This is CustomApplicationRunner01
获取所有命令行参数
--msg=hi
--msg=hi2
test.txt
------
获取所有选项
msg
------
获取解析后的参数,以 --msg 为例子
--msg选项的值有:
hi
hi2
------
获取无选项指定的参数
test.txt
示例3:多个 ApplicationRunner 实现类
demo03 项目结构:
demo03
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner01.java
│ │ ├── CustomApplicationRunner02.java
│ │ └── Demo03App.java
│ └── resources
└── test
├── java
└── resources
Demo03App 类代码:
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Demo03App {
public static void main(String[] args) {
SpringApplication.run(Demo03App.class, args);
}
}
CustomApplicationRunner01 类代码:
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner01 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
CustomApplicationRunner02 类代码:
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner02 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
执行 Demo03App ,输出:
This is CustomApplicationRunner01
This is CustomApplicationRunner02
一个问题:为什么是先执行 CustomApplicationRunner01 ,再执行 CustomApplicationRunner01 呢?是按照spring加载bean的顺序,还是给句类名排序?spring加载bean 的顺序是怎么样的?
示例4:使用 @Order 注解自定义多个 ApplicationRunner 实现类的run方法执行顺序
demo04 项目结构:
demo04
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner01.java
│ │ ├── CustomApplicationRunner02.java
│ │ └── Demo04App.java
│ └── resources
└── test
├── java
└── resources
代码和 demo03 类似,但是加上了 Order 注解。
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(1) // 值越小,越先执行
public class CustomApplicationRunner01 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(0) // 值越小,越先执行
public class CustomApplicationRunner02 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
执行 Demo04App ,输出:
This is CustomApplicationRunner02
This is CustomApplicationRunner01
示例5:使用 Ordered 接口自定义多个 ApplicationRunner 实现类的run方法执行顺序
demo05 项目结构:
demo05
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner01.java
│ │ ├── CustomApplicationRunner02.java
│ │ └── Demo05App.java
│ └── resources
└── test
├── java
└── resources
代码和示例4类似。但是不用 Order 注解,而是实现 Ordered 接口的方法。
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner01 implements ApplicationRunner, Ordered {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
@Override
public int getOrder() {
return 1;
}
}
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
@Component
public class CustomApplicationRunner02 implements ApplicationRunner, Ordered {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
@Override
public int getOrder() {
return 0;
}
}
执行 Demo05App ,输出:
This is CustomApplicationRunner02
This is CustomApplicationRunner01
示例6:SpringBootApplication 注解的类实现 ApplicationRunner 接口
demo06 项目结构如下:
demo06
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ └── Demo06App.java
│ └── resources
└── test
├── java
└── resources
Demo06App 类内容:
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class Demo06App implements ApplicationRunner { // 同时实现 ApplicationRunner 接口
public static void main(String[] args) {
SpringApplication.run(Demo06App.class, args);
System.out.println("finish");
}
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
执行该类,输出:
This is Demo06App$$EnhancerBySpringCGLIB$$c1cc2ff9
finish
示例7:ApplicationRunner 与 CommandLineRunner 混用
demo07 项目结构:
demo07
├── build.gradle
└── src
├── main
│ ├── java
│ │ └── demo
│ │ ├── CustomApplicationRunner01.java
│ │ ├── CustomApplicationRunner02.java
│ │ ├── CustomCommandLineRunner01.java
│ │ ├── CustomCommandLineRunner02.java
│ │ └── Demo07App.java
│ └── resources
└── test
├── java
└── resources
主要是想看下,Order注解能否同时对 ApplicationRunner 接口实现类和 CommandLineRunner 接口实现类,进行排序。
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(3)
public class CustomApplicationRunner01 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
package demo;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(1)
public class CustomApplicationRunner02 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
package demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(2)
public class CustomCommandLineRunner01 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
package demo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Order(0)
public class CustomCommandLineRunner02 implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("This is " + getClass().getSimpleName());
}
}
运行 Demo07App ,输出:
This is CustomCommandLineRunner02
This is CustomApplicationRunner02
This is CustomCommandLineRunner01
This is CustomApplicationRunner01
Order 注解能够同时对对ApplicationRunner 接口实现类和 CommandLineRunner 接口实现类的执行顺序排序。
如果把4个类的@Order
注解都去掉,执行结果:
This is CustomApplicationRunner01
This is CustomApplicationRunner02
This is CustomCommandLineRunner01
This is CustomCommandLineRunner02