前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >什么?Spring Boot CommandLineRunner 有坑!?

什么?Spring Boot CommandLineRunner 有坑!?

作者头像
程序猿DD
发布于 2021-06-08 14:12:37
发布于 2021-06-08 14:12:37
1.8K00
代码可运行
举报
文章被收录于专栏:程序猿DD程序猿DD
运行总次数:0
代码可运行

使用场景

在应用程序开发过程中,往往我们需要在容器启动的时候执行一些操作。

Spring Boot中提供了CommandLineRunner和ApplicationRunner两个接口来实现这样的需求。

两个接口的不同

参数不同,其他大体相同,可根据实际需求选择合适的接口使用。

CommandLineRunner接口中run方法的参数为String数组,ApplicationRunner中run方法的参数为ApplicationArguments。

特殊的场景

在启动项目时,有时候我们所做的操作可能不是一次性的操作,有可能循环查询数据库,根据结果来处理不同的业务,亦或是监听消息队列……

遇到的坑

看下面一个例子,我们启动一个spring boot项目,正常启动情况下,项目启动后会打印启动时间。Spring Boot 教程和示例源码都在这里了:https://blog.didispace.com/spring-boot-learning-2x/

如下图所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2018-07-16 01:48:22.378  INFO 9164 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-07-16 01:48:22.386  INFO 9164 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2018-07-16 01:48:22.386  INFO 9164 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2018-07-16 01:48:22.396  INFO 9164 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2018-07-16 01:48:22.417  INFO 9164 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
2018-07-16 01:48:22.546  INFO 9164 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8088 (http)
2018-07-16 01:48:22.555  INFO 9164 --- [           main] com.hello.word.WordParseApplication      : Started WordParseApplication in 3.811 seconds (JVM running for 4.486)

下面我们模拟一下启动项目时使用CommandLineRunner,有人说CommandLineRunner是项目启动完成后才调用的,我们看看现象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class RunService  implements CommandLineRunner {
 
    public void run(String... strings){
        int i =0;
        while(true){
            i++;
                try {
                    Thread.sleep(10000);
                    System.out.println("过去了10秒钟……,i的值为:"+i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(i==4){ //第40秒时抛出一个异常
                    throw new RuntimeException();
                }
                continue;
        }
    }
}

再次启动spring boot 项目,看看日志,直接报错,启动异常了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2018-07-16 01:56:43.703  INFO 7424 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2018-07-16 01:56:43.703  INFO 7424 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2018-07-16 01:56:43.722  INFO 7424 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2018-07-16 01:56:43.750  INFO 7424 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
2018-07-16 01:56:43.885  INFO 7424 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8088 (http)
过去了10秒钟……,i的值为:1
过去了10秒钟……,i的值为:2
过去了10秒钟……,i的值为:3
过去了10秒钟……,i的值为:4
2018-07-16 01:57:23.939  INFO 7424 --- [           main] utoConfigurationReportLoggingInitializer : 
 
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2018-07-16 01:57:23.973 ERROR 7424 --- [           main] o.s.boot.SpringApplication               : Application startup failed
 
java.lang.IllegalStateException: Failed to execute CommandLineRunner
 at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:735) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:716) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:703) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:304) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 at com.hello.word.WordParseApplication.main(WordParseApplication.java:15) [classes/:na]
Caused by: java.lang.RuntimeException: null
 at com.zhangwq.service.RunService.run(RunService.java:24) ~[classes/:na]
 at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:732) [spring-boot-1.5.9.RELEASE.jar:1.5.9.RELEASE]
 ... 6 common frames omitted
 
2018-07-16 01:57:23.975  INFO 7424 --- [           main] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@14a4e18: startup date [Mon Jul 16 01:56:39 CST 2018]; root of context hierarchy
2018-07-16 01:57:23.975  INFO 7424 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 2147483647
2018-07-16 01:57:23.975  INFO 7424 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
 
Process finished with exit code 1

说明启动CommandLineRunner的执行其实是整个应用启动的一部分,没有打印最后的启动时间,说明项目是在CommandLineRunner执行完成之后才启动完成的。

此时CommandLineRunner的run方法执行的是一个循环,循环到第四次的时候,抛出异常,直接影响主程序的启动。Spring Boot 教程和示例源码都在这里了:https://blog.didispace.com/spring-boot-learning-2x/

填坑

这样的问题该如何解决呢?

这个操作影响了主线程,那么我们是否可以重新开启一个线程,让他单独去做我们想要做的操作呢。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class RunService implements CommandLineRunner {
 
    public void run(String... strings){
        new Thread(){
            public void run() {
                int i = 0;
                while (true) {
                    i++;
                    try {
                        Thread.sleep(10000);
                        System.out.println("过去了10秒钟……,i的值为:" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (i == 4) { //第40秒时抛出一个异常
                        throw new RuntimeException();
                    }
                    continue;
                }
            }
        }.start();
    }
}

我们再看看这次的日志是什么样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2018-07-16 02:05:52.680  INFO 7148 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 2147483647
2018-07-16 02:05:52.680  INFO 7148 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2018-07-16 02:05:52.695  INFO 7148 --- [           main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2018-07-16 02:05:52.717  INFO 7148 --- [           main] s.d.s.w.s.ApiListingReferenceScanner     : Scanning for api listing references
2018-07-16 02:05:52.815  INFO 7148 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8088 (http)
2018-07-16 02:05:52.819  INFO 7148 --- [           main] com.hello.word.WordParseApplication      : Started WordParseApplication in 3.406 seconds (JVM running for 4.063)
过去了10秒钟……,i的值为:1
过去了10秒钟……,i的值为:2
过去了10秒钟……,i的值为:3
过去了10秒钟……,i的值为:4
Exception in thread "Thread-10" java.lang.RuntimeException
 at com.zhangwq.service.RunService$1.run(RunService.java:26)

此时CommandLineRunner执行的操作和主线程是相互独立的,抛出异常并不会影响到主线程。

程序打印了启动时间,并且CommandLineRunner中run方法报错后,应用程序并没有因为异常而终止。填坑成功。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿DD 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
我们项目要java执行命令“dmidecode -s system-uuid”获取结果,然而碰到问题,当项目一直执行好久后,Runtime.getRuntime().exec()获取结果为空,但也不报错,重启项目就又可以了,所以猜测属于陷阱2,并进行记录。
刘大猫
2024/11/17
8460
java 执行shell命令及日志收集避坑指南
有时候我们需要调用系统命令执行一些东西,可能是为了方便,也可能是没有办法必须要调用。涉及执行系统命令的东西,则就不能做跨平台了,这和java语言的初衷是相背的。
烂猪皮
2021/01/14
2.7K0
【转】Java中Runtime.exec的一些事 
Runtime类是一个与JVM运行时环境有关的Singleton类,有以下几个值得注意的地方:
yiduwangkai
2019/09/17
3.1K0
Java魔法堂:调用外部程序
Java虽然五脏俱全但总有软肋,譬如获取CPU等硬件信息,当然我们可以通过JNI调用C/C++来获取,但对于对C/C++和Windows API不熟的码农是一系列复杂的学习和踩坑过程。那能不能通过简单一些、学习成本低一些的方式呢?答案是肯定的,在功能实现放在首位的情况下,借他山之石是最简洁有力的做法。
^_^肥仔John
2021/02/25
1.7K0
Java魔法堂:调用外部程序
Java 配 Shell 等于美酒加咖啡
技术上又何尝不是如此呢?先假设一个场景:BOSS 让你实现一个服务监控的指挥室,能看到每个服务器的磁盘剩余空间,能看到。。。能看到。。。
一猿小讲
2019/09/17
8590
Java 配 Shell 等于美酒加咖啡
Java调用CMD命令
Windows下复制: copy C:\server\data\ccgavr\1.png C:\server\data\ccgavr\2.png Windows重命名: ren 1.png 3.png ren C:\server\data\ccgavr\1.png C:\server\data\ccgavr\3.png(DOS中提示命令语法不正确,powershell则可以)
JaneYork
2023/10/11
4050
Java调用CMD命令
Linux:java通过Runtime.getRuntime().exec()执行shell,Process.waitFor()返回Required key not available(126)问题
因为看到错误码对应的原因是:Required key not available,所需的Key不可用。查找了很多相关解决办法,发现都不太相关。
鲲志说
2025/04/07
830
Linux:java通过Runtime.getRuntime().exec()执行shell,Process.waitFor()返回Required key not available(126)问题
Process类详解
ProcessBuilder是一个final类,Process是一个抽象类。ProcessBuilder.start() 和 Runtime.exec() 方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process 子类的一个实例,该实例可用来控制进程状态并获得相关信息。
matt
2022/10/25
1.7K0
聊聊PowerJob的AbstractScriptProcessor
tech/powerjob/official/processors/impl/script/AbstractScriptProcessor.java
code4it
2024/01/04
1100
ProcessBuilder API 指南-Java快速进阶教程
Process API提供了一种在 Java 中执行操作系统命令的强大方法。但是,它有几个选项,可能会使其使用起来很麻烦。
jack.yang
2025/04/05
1090
红队笔记专属-shell备忘录
建议直接复制粘贴到笔记,或点赞收藏,因为时常会用到,这是整理的一些常见的反向shell和特权提升的笔记文档,红队成员必会!
Gamma实验室
2022/04/26
1.1K0
Java程序员的日常 —— 多进程开发
最近再弄进程管理相关的工作,因此必要的就涉及到各种系统下关于进程的管理。 这里简单的介绍下: 如何在Java中执行命令 在windows下肯定是dos命令了,而在linux则为shell命令。执行的方式差不多相同: 方法1:Runtime windows版本: Process process = Runtime.getRuntime().exec("ipconfig /all"); Linux版本: Process process = Runtime.getRuntime().exec("ifcon
用户1154259
2018/01/17
1.2K0
Java程序员的日常 —— 多进程开发IO阻塞问题
本篇仍旧是源于最近的工作,总结一下纪念那些年埋下的坑... 背景故事 需求:“使用进程方式启动另一个程序!” 开发:“OK! Runtime.getRuntime().exec("xxxx")” 需求:“启动以后能看到输出消息不!” 开发:“OK!” Process process = null; try { process = Runtime.getRuntime().exec("ipconfig /all"); } catch (IOException e) { e.printSt
用户1154259
2018/01/17
1.5K0
Process API 指南-Java快速进阶教程
它所指的进程是一个正在执行的应用程序。Process类提供与这些进程交互的方法,包括提取输出、执行输入、监视生命周期、检查退出状态以及销毁(终止)它。
jack.yang
2025/04/05
1270
java:执行linux sudo命令
版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/78913746
10km
2019/05/25
6.4K1
java执行和停止Logcat命令及多线程实现
本人在使用UiAutomator的时候,想多写一个自动收集手机log的方法,使用runtime类执行了adb logcat的方法,但是一直找不到好的方法结束这个线程,网上说有kill pid的,但是这个操作起来略微麻烦了。自己也想了一个destroy线程的方法,一直不好用。提示错误信息如下:
FunTester
2019/08/14
1.7K0
Java 使用Runtime在一个Java程序中启动和关闭另一个Java程序
重点是Java启动的process,不能直接执行java、jps等命令,也获取不到环境变量,会报command not found
heasy3
2020/08/02
2.6K0
UiAutomator测试中如何恢复手机输入法
本人在使用UiAutomator测试的时候,需要用到utf7输入法,每次执行之前都会切换到utf7输入法,然后每次执行结束之后再切换到正常输入法,由于测试机器比较多,所以写了一个自动切换到其他任意输入法的方法。分享代码,供大家参考。
FunTester
2019/08/27
9450
Java执行Shell命令的方式
Java可以使用Runtime和ProcessBuilder两种方式执行Shell命令。
很酷的站长
2023/09/24
3.7K0
Java执行Shell命令的方式
RunNiFi.java 源码解读
RunNiFi类是由 nifi.sh脚本执行java命令指定的主类,RunNiFi类主要是干一些 查找文件,接受脚本指令,启动停止NIFI进程(主类 org.apache.nifi.NiFi),自动重启NIFI,发送NIFI通知等等操作;关于代码的详细解读都在注释当中,可以从 main方法下自行跟踪阅读(自己跟着源码逻辑读更好):
@阿诚
2020/09/01
1.3K0
相关推荐
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验