因为业务需要,做了一个小项目,不让植入太多依赖,所以集成了H2用着很爽,但是在配置多数据源时因为修改了加载数据源的方式,导致初始化sql总是不成功,所以就想着在SpringBoot启动后自动执行一段逻辑,加载sql文件,进行表的初始化。
SpringBoot启动后执行一段逻辑主要有两种方式:实现ApplicationRunner接口、实现CommandLineRunner。两种实现方式的不同之处在于run方法中接收的参数类型不一样。
1. 实现ApplicationRunner接口
@Component
public 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:
**/
@Override
public 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接口
@Component
public class CommandLineRunnerImpl implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("通过实现CommandLineRunner接口,在spring boot项目启动后打印参数");
for (String arg : args) {
System.out.print(arg + " ");
}
System.out.println();
}
}
指定执行顺序
当项目中同时实现了ApplicationRunner和CommondLineRunner接口时,可使用Order注解或实现Ordered接口来指定执行顺序,值越小越先执行。
@Component
@Order(1000)
public class ApplicationRunnerService implements ApplicationRunner {}
@Component
public class ApplicationRunnerService implements ApplicationRunner, Ordered {
@Override
public int getOrder() {
return 1000;
}
}
原理
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);
//调用runners
callRunners(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);
}
}
}
案例地址
https://github.com/linj6/springboot-learn/tree/master/springboot-runner