/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
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 {
// application 启动参数列表
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 容器:准备上下文,装配 bean
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);
}
try {
// 监听器正式运行
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
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);
}
}
}
springboot 完全初始化完毕后
@Component
public class ServerDispatcher implements CommandLineRunner {
@Override
public void run(String... args){
// 逻辑代码
}
}
@Component
public class ServerDispatcher implements ApplicationRunner {
@Override
public void run(ApplicationArguments args){
// 逻辑代码
}
}
参数不同
可以添加 @Order 注解等注解:指定执行的顺序 (数字小的优先)
@Priority 或其他排序的接口也可
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
/**
* The order value.
* Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
监听机制
@Component
public class ServerDispatcher implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 逻辑代码
}
}
在 IOC 容器的启动过程,当所有的 bean 都已经处理完成之后,spring 的 ioc 容器会有一个发布 ContextRefreshedEvent 事件的动作。
系统会存在两个容器,一个是 root application context
, 另一个就是我们自己的 projectName-servlet context
(作为root application context的子容器)
这种情况下,就会造成 onApplicationEvent 方法被执行两次。为了避免上面提到的问题,我们可以只在 root application context 初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理
加入判断即可:if (event.getApplicationContext().getParent() == null)
@Component
public class ServerDispatcher implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
// 当 spring 容器初始化完成后就会执行该方法
if (event.getApplicationContext().getParent() == null) {
//逻辑代码
}
}
}
如果刚好可以在一个方法内执行 Spring 启动后的动作,可以让 SpringBoot 的启动类继承上述的某个接口,然后重写相应的方法即可。