springboot使用ApplicationRunner加入死循环后只有一个启动问题

springboot的项目中,经常会有需求是在容器启动之后执行一些操作。
springboot提供了ApplicationRunner和CommandLineRunner两个接口可以帮助我们实现这种需求。

当项目中实现了多个ApplicationRunner接口,并且其中一个使用了类似于while(true)这样不会退出的循环体。将会导致只有第一个被启动的ApplicationRunner在执行,而后续的ApplicationRunner接口不会被调用。

例子如下

@Component
@Log4j2
public class RunnerTest1 implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        while (true) {
            System.out.println("print RunnerTest1");
            Thread.sleep(100);
        }
    }
}
@Component
@Log4j2
public class RunnerTest2 implements ApplicationRunner {
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
 
        while (true) {
            System.out.println("print RunnerTest2");
            Thread.sleep(100);
        }
    }
}

输出结果如下

print runner 1
print runner 1
print runner 1

可以看到项目启动后RunnerTest2 将不会被执行。

通过源码分析springboot启动的源码可以发现,在applicationContext容器加载完成之后,会调用SpringApplication类的callRunners方法

该方法中会获取所有实现了ApplicationRunner和CommandLineRunner的接口bean,然后依次执行对应的run方法,并且是在同一个线程中执行。因此如果有某个实现了ApplicationRunner接口的bean的run方法一直循环不返回的话,后续的代码将不会被执行。

解决方案

把循环里的处理逻辑独立出来

@Component
@Log4j2
public class RunnerTest1 implements ApplicationRunner {
    @Resource
    private ExecuteTest1 executeTest1;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        new Thread(executeTest1).start();
    }
}
@Component
@Log4j2
public class RunnerTest2 implements ApplicationRunner {
    @Resource
    private ExecuteTest2 executeTest2;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        new Thread(executeTest2).start();
    }
}
@Service
@Log4j2
public class ExecuteTest1 implements Runnable {
    @Override
    public void run() {
        System.out.println("print RunnerTest1");
    }
}
@Service
@Log4j2
public class ExecuteTest2 implements Runnable {
    @Override
    public void run() {
        System.out.println("print RunnerTest2");
    }
}

将ApplicationRunner当成线程启动器使用,实际的内容在独立的Runnable中实现,不是很完美 但是可以临时解决问题。

其实可以使用一个妥善的方式比如做一个触发器,一个ApplicationRunner一次性触发多个runnable,增加保活机制,定期检查线程是否存活。


ApplicationRunner和CommandLineRunner区别

  1. 两个接口中都有run方法,负责接收来自应用外部的参数,ApplicationRunner的run方法会将外部参数封装成一个ApplicationArguments对象,而CommandLineRunner接口中run方法的参数则为String数组。

ApplicationRunner和CommandLineRunner哪个先执行

从测试结果来看,ApplicationRunner默认先于CommandLineRunner执行

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容