概述
在实际项目开发过程中,有时候需要在服务启动时进行一些业务初始化操作,这些操作只需要在服务启动后执行一次,那么通过Spring Boot如何实现该需求呢? Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口,这两种服务接口都可以实现上面的业务需求,本文将对这两种服务接口实现进行介绍。
ApplicationRunner与CommandLineRunner
异同点
-
相同点
- 两者均在服务启动完成后执行,并且只执行一次。
- 两者都能获取到应用的命令行参数。
- 两者触发执行的时间点是一致的。
-
不同点
- 虽然两者都是获取到应用的命令行参数,但是ApplicationRunner获取到的是封装后的ApplicationArguments对象,而CommandLine获取到的是ApplicationArguments中的sourceArgs属性(List<String>),即原始参数字符串列表.
执行顺序
很多误认为CommandLineRunner会先于ApplicationRunner执行,但是实际上两者是一起触发执行的,可以阅读SpringApplication.class方法中的源码
- 1.SpringApplication.class中的run方法,会在执行完一些列初始化工作之后,调用callRunners方法执行Runner中的相关初始化代码。
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; CollectionexceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
- 2.在callRunners方法源码中,可以看出,ApplicationRunner和CommandLineRunner会被加载到同一个List中,之后排序并循环执行,所以并没有CommandLineRunner先执行一说。实际上,如果没有指定执行顺序,默认是ApplicationRunner先执行的。
private void callRunners(ApplicationContext context, ApplicationArguments args) { List
实现示例
1.实现Runner服务接口
- 创建ApplicationRunnerFirst和ApplicationRunnerSecond两个类,实现ApplicationRunner接口。
@Component@Slf4jpublic class ApplicationRunnerFirst implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { log.info("This is {} Application Runner", "first"); }}
@Component@Slf4jpublic class ApplicationRunnerSecond implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { log.info("This is {} Application Runner", "second"); }}
- 创建两个类,实现CommandLineRunner接口。
@Component@Slf4jpublic class CommandlineRunnerFirst implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("CommandLineRunner Args:{}",JSON.toJSONString(args)); log.info("This is {} Command Line Runner", "first"); }}
@Component@Slf4jpublic class CommandlineRunnerSecond implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("This is {} Command Line Runner", "second"); }}
2.指定执行顺序
可以使用org.springframework.core.annotation.Order注解设置执行顺序,其中数值越小越优先执行。例如:
@Component@Slf4j@Order(3)public class ApplicationRunnerFirst implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { log.info("ApplicationRunner Args:{}",JSON.toJSONString(args)); log.info("This is {} Application Runner", "first"); }}
分别将CommandlineRunnerSecond的Order设置为1,ApplicationRunnerSecond设置为2,ApplicationRunnerFirst设置为3,CommandlineRunnerFirst不设置。
3.运行结果
启动服务,运行结果如下:
还有需注意的是,Runner的实现类必须注册为Spring Bean,否则不回被执行,阅读SpringApplication.run方法的源码就知道原因了。
示例源码
码云:
尾巴
无论ApplicationRunner还是CommandLineRunner,都是在应用启动完成后执行一次业务初始化代码,达到的效果也比较类似,由于ApplicationRunner的方法参数是ApplicationArguments对象,使用起来更加方便,所以更推荐使用。