本文共 7173 字,大约阅读时间需要 23 分钟。
目录
一、前言
如果我们想在应用启动时,搞事情,并且在整个应用生命周期只搞一次,有什么办法呢?比如在应用启动的时候,提前从数据库加载一些数据
,提前加载加密证书
等。
其实 SpringBoot 有2大法宝可以做到,CommandLineRunner 和 ApplicationRunner 接口。这2个接口里面都只有一个 run 方法,只要实现了这2个接口的类,并且重写了 run 方法,并将它们的实例交由 Spring 容器管理,Spring 容器在启动后,会自动调用执行 run 方法。并且在整个应用生命周期内只会执行一次。
二、CommandLineRunner 接口说明
package org.springframework.boot;@FunctionalInterfacepublic interface CommandLineRunner { void run(String... args) throws Exception;}
我们定义一个类实现 CommandLineRunner 接口,测试下它的调用时机。我们直接用启动类来实现接口,当然你也可以另起一个类来实现 CommandLineRunner,并注入到 Spring 容器中。
@SpringBootApplicationpublic class Application implements CommandLineRunner { public static void main(String[] args) { System.out.println("--- Application main begin to start..."); SpringApplication.run(Application.class, args); System.out.println("--- Application main has start..."); } @Override public void run(String... args) throws Exception { System.out.println("--- CommandLineRunner in Application..."); }}
启动日志如下,证明确实会在应用启动时,即 Spring 容器加载完后,执行我们定义的 run 方法,执行完成后项目启动完成。
--- Application main begin to start... . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.8.RELEASE)// 省略一些日志2021-02-20 14:10:02.473 INFO 25240 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''2021-02-20 14:10:02.486 INFO 25240 --- [ restartedMain] com.nobody.Application : Started Application in 1.871 seconds (JVM running for 2.598)--- CommandLineRunner in Application...--- Application main has start...
此时可能有人会问,run 方法的 String... args
参数是干嘛用的,它的值又是哪里来的?其实它就是 main 方法的 String[] args
参数,我们在 main 和 run 方法打印输出看看。
@SpringBootApplicationpublic class Application implements CommandLineRunner { public static void main(String[] args) { System.out.println("--- Application main begin to start..."); System.out.println("main args length=" + args.length + ", args=" + Arrays.toString(args)); SpringApplication.run(Application.class, args); System.out.println("--- Application main has start..."); } @Override public void run(String... args) throws Exception { System.out.println("--- CommandLineRunner in Application..."); System.out.println("run args length=" + args.length + ", args=" + Arrays.toString(args)); }}
然后启动先配置下应用启动时传的参数,如下:
启动服务,启动日志如下,验证正确。--- Application main begin to start...main args length=2, args=[Mr.nobody, Cx330] . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.8.RELEASE)// 省略一些日志--- CommandLineRunner in Application...run args length=2, args=[Mr.nobody, Cx330]--- Application main has start...
三、ApplicationRunner 接口说明
其实 ApplicationRunner 接口和 CommandLineRunner 接口作用一样的,唯一不同的是 args 参数。CommandLineRunner 的 run 方法参数是 String… args,而 ApplicationRunner 的 run 方法参数是 ApplicationArguments args。
@SpringBootApplicationpublic class Application implements ApplicationRunner { public static void main(String[] args) { System.out.println("--- Application main begin to start..."); System.out.println("main args length=" + args.length + ", args=" + Arrays.toString(args)); SpringApplication.run(Application.class, args); System.out.println("--- Application main has start..."); } @Override public void run(ApplicationArguments args) throws Exception { System.out.println("--- CommandLineRunner in Application..."); System.out.println("run args optionNames=" + args.getOptionNames() + ", sourceArgs=" + Arrays.toString(args.getSourceArgs()) + ", name=" + args.getOptionValues("name")); }}服务启动后,启动日志如下:
--- Application main begin to start...main args length=2, args=[--name=Mr.nobody, --age=18] . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.8.RELEASE)// 省略一些日志--- CommandLineRunner in Application...run args optionNames=[name, age], sourceArgs=[--name=Mr.nobody, --age=18], name=[Mr.nobody]--- Application main has start...
三、执行顺序
如果在启动服务的时候需要初始化很多资源,并且要保证初始化资源相互之间有序,那如何保证不同的 CommandLineRunner 的执行顺序呢? 如果定义了多个 CommandLineRunner,可以实现 Ordered 接口
,或者使用 @Order 注解
。我们可以指定 Order 里的值,数字越小越早执行。
而且,CommandLineRunner 和 ApplicationRunner 是可以共存的。
@SpringBootApplication// 注解指定执行顺序@Order(0)public class Application implements CommandLineRunner { public static void main(String[] args) { System.out.println("--- Application main begin to start..."); SpringApplication.run(Application.class, args); System.out.println("--- Application main has start..."); } @Override public void run(String... args) throws Exception { System.out.println("--- CommandLineRunner in Application, Order=0 ..."); }}
@Component// 注解指定执行顺序@Order(1)public class MyCommandLineRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("--- CommandLineRunner in MyCommandLineRunner1, Order=1 ..."); }}
@Component// @Order(2)// 实现 Ordered 接口,并且重写 getOrder 方法public class MyCommandLineRunner2 implements CommandLineRunner, Ordered { @Override public void run(String... args) throws Exception { System.out.println("--- CommandLineRunner in MyCommandLineRunner2, Order=2 ..."); } // 指定执行顺序 @Override public int getOrder() { return 2; }}
@Component@Order(3)public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("--- ApplicationRunner in MyApplicationRunner, Order=3 ..."); }}
启动服务,启动日志如下:
--- Application main begin to start... . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.8.RELEASE)// 省略一些日志--- CommandLineRunner in Application, Order=0 ...--- CommandLineRunner in MyCommandLineRunner1, Order=1 ...--- CommandLineRunner in MyCommandLineRunner2, Order=2 ...--- ApplicationRunner in MyApplicationRunner, Order=3 ...--- Application main has start...
四、注意事项
- 应用启动时,即 Spring 容器加载完后,才执行 run 方法,并且整个应用生命周期只会执行一次。
- 因为 Spring 容器加载完后,才执行 run 方法,所以我们可以在 CommandLineRunner 接口里注入其他依赖(例如 @Autowired 注入),并且使用它们。
- run 方法如果抛异常,会导致应用启动失败。如果允许,可以使用 try catch 块处理。
- 如果实现 CommandLineRunner 或 ApplicationRunner 接口的类,没有将其注入到 Spring 容器管理(例如没有在类上添加 @Component 注解),是不起作用的。
此演示项目已上传到Github,如有需要可自行下载,欢迎 Star 。
转载地址:https://javalib.blog.csdn.net/article/details/113881768 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!