SpringBoot启动后执行一段逻辑主要有两种方式:实现ApplicationRunner接口、实现CommandLineRunner。两种实现方式的不同之处在于run方法中接收的参数类型不一样。
1. 实现ApplicationRunner接口
@Componentpublic class ApplicationRunnerService implements ApplicationRunner {private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationRunnerService.class);@Autowired@Qualifier("h2Template")private JdbcTemplate h2Template;@Value("${invoke.schema.location}")private String schema;@Value("${invoke.data.location}")private String data;/*** @Author: TheBigBlue* @Description: 项目启动,执行sql文件初始化* @Date: 2019/9/19* @Param args:* @Return:**/@Overridepublic void run(ApplicationArguments args) {String schemaContent = FileUtil.readFileFromClassPath(schema);String dataContent = FileUtil.readFileFromClassPath(data);//获取所有表名List<String> tableList = h2Template.queryForList("show tables").stream().map(meta -> (String) meta.get("TABLE_NAME")).collect(Collectors.toList());//如果H2中不存在任何表,则创建,如果已经存在了,则只初始化数据if (IsNullUtil.isBlank(tableList)) {LOGGER.info("初始化{}文件数据", data);h2Template.execute(schemaContent);}LOGGER.info("初始化{}文件数据", data);//初始化数据h2Template.execute(dataContent);}}
2. 实现CommandLineRunner接口
@Componentpublic class CommandLineRunnerImpl implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {System.out.println("通过实现CommandLineRunner接口,在spring boot项目启动后打印参数");for (String arg : args) {System.out.print(arg + " ");}System.out.println();}}
3.指定执行顺序
当项目中同时实现了ApplicationRunner和CommondLineRunner接口时,可使用Order注解或实现Ordered接口来指定执行顺序,值越小越先执行。
@Component@Order(1000)public class ApplicationRunnerService implements ApplicationRunner {}
@Componentpublic class ApplicationRunnerService implements ApplicationRunner, Ordered {@Overridepublic int getOrder() {return 1000;}}
4.原理
Debug可以看到,都是在 org.springframework.boot.SpringApplication#run(java.lang.String…)方法内的afterRefresh(上下文刷新后处理)方法后,会执行callRunners方法。
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);Banner printedBanner = printBanner(environment);context = createApplicationContext();exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);//刷新上下文afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}listeners.started(context);//调用runnerscallRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
callRunners方法具体实现如下,可以看出上下文完成刷新后,依次调用注册的runners, runners的类型为 ApplicationRunner 或 CommandLineRunner。
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}}
