一、前言
- Springboot 源码解析是一件大工程,逐行逐句的去钻研代码,会很干燥,也不容易坚持下去。
- 咱们不谋求大而全,而是试着每次去钻研一个小知识点,最终聚沙成塔,这就是咱们的 springboot 源码管中窥豹系列。
二、Runner
- 如果咱们想在 springboot 我的项目启动实现之后,做点什么,咱们应该怎么办呢?
- 留神咱们能够写在 bean 的初始化办法外面(咱们前面讲),然而咱们要用到其它曾经加载了的 bean 的能力,又怎么办呢?
- 当然加程序,加依赖也能解决,就是麻烦
这一节咱们讨论一下 springboot 我的项目的 Runner,Runner 是在 spring 加载结束执行的,springboot 有两种 Runner:
- ApplicationRunner
- CommandLineRunner
@FunctionalInterface
public interface ApplicationRunner {void run(ApplicationArguments args) throws Exception;
}
@FunctionalInterface
public interface CommandLineRunner {void run(String... args) throws Exception;
}
- 两种除了参数不同,其它没区别
- ApplicationArguments 是对传参数组的封装,实质也没区别
- 只有执行程序上有区别,上面源码会看到
三、用法
实现接口就能够了
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class HelloRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {System.out.println("hello runner");
}
}
- 因为这时候 spring 曾经加载结束,你能够引入其它 bean
- 启动我的项目,你会发现在日志最下方打印了下面的话
四、源码解读
咱们间接找 SpringApplication 类的 run 办法,想看整体框架的去第一节。
public ConfigurableApplicationContext run(String... args) {
...
try {
...
callRunners(context, applicationArguments);
...
}
catch (Throwable ex) {...}
...
return context;
}
咱们间接定位到 callRunners 办法。
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();
// (1) 找到 ApplicationRunner 的实现类,加到 list 外面
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
// (2) 找到 CommandLineRunner 的实现类,加到 list 外面
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
// (3) 排序
AnnotationAwareOrderComparator.sort(runners);
// (4) 钩子回调
for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);
}
}
}
总共分四步:
- (1) 找到 ApplicationRunner 的实现类,加到 list 外面
- (2) 找到 CommandLineRunner 的实现类,加到 list 外面
- (3) 排序
- (4) 钩子回调
咱们看一下 canllRunner
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {(runner).run(args);
}
catch (Exception ex) {throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {(runner).run(args.getSourceArgs());
}
catch (Exception ex) {throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
- 除了传参形式,都一样。
- 下面说的执行程序问题,是先增加的 ApplicationRunner,如果只有 @Component,先执行 ApplicationRunner
欢送关注公众号:丰极,更多技术学习分享。