Spring Boot 啓動過程分析(三)

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
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章