SpringBoot启动后执行一段逻辑主要有两种方式:实现ApplicationRunner接口、实现CommandLineRunner。两种实现方式的不同之处在于run方法中接收的参数类型不一样。

1. 实现ApplicationRunner接口

  1. @Component
  2. public class ApplicationRunnerService implements ApplicationRunner {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRunnerService.class);
  4. @Autowired
  5. @Qualifier("h2Template")
  6. private JdbcTemplate h2Template;
  7. @Value("${invoke.schema.location}")
  8. private String schema;
  9. @Value("${invoke.data.location}")
  10. private String data;
  11. /**
  12. * @Author: TheBigBlue
  13. * @Description: 项目启动,执行sql文件初始化
  14. * @Date: 2019/9/19
  15. * @Param args:
  16. * @Return:
  17. **/
  18. @Override
  19. public void run(ApplicationArguments args) {
  20. String schemaContent = FileUtil.readFileFromClassPath(schema);
  21. String dataContent = FileUtil.readFileFromClassPath(data);
  22. //获取所有表名
  23. List<String> tableList = h2Template.queryForList("show tables")
  24. .stream().map(meta -> (String) meta.get("TABLE_NAME"))
  25. .collect(Collectors.toList());
  26. //如果H2中不存在任何表,则创建,如果已经存在了,则只初始化数据
  27. if (IsNullUtil.isBlank(tableList)) {
  28. LOGGER.info("初始化{}文件数据", data);
  29. h2Template.execute(schemaContent);
  30. }
  31. LOGGER.info("初始化{}文件数据", data);
  32. //初始化数据
  33. h2Template.execute(dataContent);
  34. }
  35. }

2. 实现CommandLineRunner接口

  1. @Component
  2. public class CommandLineRunnerImpl implements CommandLineRunner {
  3. @Override
  4. public void run(String... args) throws Exception {
  5. System.out.println("通过实现CommandLineRunner接口,在spring boot项目启动后打印参数");
  6. for (String arg : args) {
  7. System.out.print(arg + " ");
  8. }
  9. System.out.println();
  10. }
  11. }

3.指定执行顺序

当项目中同时实现了ApplicationRunner和CommondLineRunner接口时,可使用Order注解或实现Ordered接口来指定执行顺序,值越小越先执行。

  1. @Component
  2. @Order(1000)
  3. public class ApplicationRunnerService implements ApplicationRunner {}
  1. @Component
  2. public class ApplicationRunnerService implements ApplicationRunner, Ordered {
  3. @Override
  4. public int getOrder() {
  5. return 1000;
  6. }
  7. }

4.原理

Debug可以看到,都是在 org.springframework.boot.SpringApplication#run(java.lang.String…)方法内的afterRefresh(上下文刷新后处理)方法后,会执行callRunners方法。

  1. public ConfigurableApplicationContext run(String... args) {
  2. StopWatch stopWatch = new StopWatch();
  3. stopWatch.start();
  4. ConfigurableApplicationContext context = null;
  5. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  6. configureHeadlessProperty();
  7. SpringApplicationRunListeners listeners = getRunListeners(args);
  8. listeners.starting();
  9. try {
  10. ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  11. args);
  12. ConfigurableEnvironment environment = prepareEnvironment(listeners,
  13. applicationArguments);
  14. configureIgnoreBeanInfo(environment);
  15. Banner printedBanner = printBanner(environment);
  16. context = createApplicationContext();
  17. exceptionReporters = getSpringFactoriesInstances(
  18. SpringBootExceptionReporter.class,
  19. new Class[] { ConfigurableApplicationContext.class }, context);
  20. prepareContext(context, environment, listeners, applicationArguments,
  21. printedBanner);
  22. refreshContext(context);
  23. //刷新上下文
  24. afterRefresh(context, applicationArguments);
  25. stopWatch.stop();
  26. if (this.logStartupInfo) {
  27. new StartupInfoLogger(this.mainApplicationClass)
  28. .logStarted(getApplicationLog(), stopWatch);
  29. }
  30. listeners.started(context);
  31. //调用runners
  32. callRunners(context, applicationArguments);
  33. }
  34. catch (Throwable ex) {
  35. handleRunFailure(context, ex, exceptionReporters, listeners);
  36. throw new IllegalStateException(ex);
  37. }
  38. try {
  39. listeners.running(context);
  40. }
  41. catch (Throwable ex) {
  42. handleRunFailure(context, ex, exceptionReporters, null);
  43. throw new IllegalStateException(ex);
  44. }
  45. return context;
  46. }

callRunners方法具体实现如下,可以看出上下文完成刷新后,依次调用注册的runners, runners的类型为 ApplicationRunner 或 CommandLineRunner。

  1. private void callRunners(ApplicationContext context, ApplicationArguments args) {
  2. List<Object> runners = new ArrayList<>();
  3. runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
  4. runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
  5. AnnotationAwareOrderComparator.sort(runners);
  6. for (Object runner : new LinkedHashSet<>(runners)) {
  7. if (runner instanceof ApplicationRunner) {
  8. callRunner((ApplicationRunner) runner, args);
  9. }
  10. if (runner instanceof CommandLineRunner) {
  11. callRunner((CommandLineRunner) runner, args);
  12. }
  13. }
  14. }