springboot的 CommandLineRunner 接口主要用于实现应用初始化之后,去执行一段逻辑代码,并且在整个项目的声明周期中,执行一次,源码如下:
package org.springframework.boot;
public interface CommandLineRunner {
void run(String... var1) throws Exception;
}
1.两种实现方式
1.1继承CommandLineRunner接口 和注解 @Componerent一起使用,如果涉及到多个,用@Order()注解去设置执行顺序
@Component
@Order(value = 1)
public class CLR1 implements CommandLineRunner{
public void run(String... strings) throws Exception {
//do something
}
}
@Component
@Order(value = 2)
public class CLR2 implements CommandLineRunner{
public void run(String... strings) throws Exception {
//do something
}
}
1.2 和@Bean连用,返回CommandLineRunner实例
@Bean
public CommandLineRunner initialize(SomeModel ModelInstance) {
return (abc) -> {
ModelInstance.dosomething(...)
};
}
3、源码跟踪
通过上面的实践操作,大家应该理解如何使用的,下面带着大家理解一下底层如何实现的; 常规操作
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//设置线程启动计时器
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置系统属性:默认缺失外部显示屏等允许启动
configureHeadlessProperty();
//获取并启动事件监听器,如果项目中没有其他监听器,则默认只有EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//将事件广播给listeners
listeners.starting();
try {
//对于实现ApplicationRunner接口,用户设置ApplicationArguments参数进行封装
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//配置运行环境:例如激活应用***.yml配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//加载配置的banner(gif,txt...),即控制台图样
Banner printedBanner = printBanner(environment);
//创建上下文对象,并实例化
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//配置SPring容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新Spring上下文,创建bean过程中
refreshContext(context);
//空方法,子类实现
afterRefresh(context, applicationArguments);
//停止计时器:计算线程启动共用时间
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//停止事件监听器
listeners.started(context);
//开始加载资源
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
本篇文章主要是熟悉SpringBoot的CommandLineRunner接口实现原理。因此上面SpringBoot启动过程方法不做过多介绍。我们直接进入正题CallRunners()方法内部。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//将实现ApplicationRunner和CommandLineRunner接口的类,存储到集合中
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);
}
}
}
上面部分代码非常简单,就是放到一个list里面,循环调用
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
//调用各个实现类中的逻辑实现
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
https://blog.csdn.net/weixin_43050247/article/details/89249394