Spring Boot 启动过程分析(三)

本文详细介绍了Spring容器的刷新过程及后置处理步骤,包括refresh方法的调用流程和关闭容器时的钩子函数注册。此外还解析了ApplicationRunner与CommandLineRunner接口的区别及应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

private void refreshContext(ConfigurableApplicationContext context) {
  // 由于这里需要调用父类一系列的refresh操作,涉及到了很多核心操作,因此耗时会比较长,本文不做具体展开
    refresh(context);

    // 注册一个关闭容器时的钩子函数
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

// 调用父类的refresh方法完成容器刷新的基础操作
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext)applicationContext).refresh();
}

注册关闭容器时的钩子函数的默认实现是在 AbstractApplicationContext 类中:

public void registerShutdownHook() {
  if(this.shutdownHook == null) {
    this.shutdownHook = new Thread() {
      public void run() {
        synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
          AbstractApplicationContext.this.doClose();
        }
      }
    };
    Runtime.getRuntime().addShutdownHook(this.shutdownHook);
  }
}

如果没有提供自定义的shutdownHook,那么会生成一个默认的,并添加到Runtime中。默认行为就是调用它的doClose方法,完成一些容器销毁时的清理工作。

3.2.6 Spring Context后置处理
protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
    callRunners(context, args);
}

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<Object>();
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    for (Object runner : new LinkedHashSet<Object>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

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);
    }
}

所谓的后置操作,就是在容器完成刷新后,依次调用注册的Runners。Runners可以是两个接口的实现类:

  • org.springframework.boot.ApplicationRunner
  • org.springframework.boot.CommandLineRunner

CommandLineRunner、ApplicationRunner 接口是在容器启动成功后的最后一步回调(类似开机自启动)

这两个接口有什么区别呢:

public interface ApplicationRunner {

    void run(ApplicationArguments args) throws Exception;

}

public interface CommandLineRunner {

    void run(String... args) throws Exception;

}

其实没有什么不同之处,除了接口中的run方法接受的参数类型是不一样的以外。一个是封装好的 ApplicationArguments 类型,另一个是直接的String不定长数组类型。因此根据需要选择相应的接口实现即可。



作者:徐志毅
链接:https://www.jianshu.com/p/dc12081b3598
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值