Spring Boot起動フロー分析


ことばを引く
15年前からspring bootで開発されていましたが、ずっと使うだけで、spring bootがどういう原理で働いているのかを深く理解していませんでした。恥ずかしいです。今日はspring bootからスタートして、spring bootの仕事原理を詳しく調べましょう。
どうしてspring bootを使いますか?
一つのものや道具を使う前に、私たちはいつも自分に聞きます。なぜ使うのですか?彼を使ったら私に何かいいところがありますか?
*最大の利点は、spring bootがjava**の約束に従って配置**より大きいということです。大量の配置ファイルに直面しなくてもいいです。spring bootはあなたが使っているカバンに基づいて、どのような配置を提供するかを決めます。
*サーバーはjarカバンの形でプロジェクトに埋め込まれています。マイクロサービスが飛び交う場合、spring bootは生まれつきマイクロサービスアーキテクチャに適しています。配置が便利です。
*devtoolsを提供してからコードを変更すると再起動して歴史になる必要があります。
長所があれば必ず欠点があります。短所は長所と短所から来ています。
*開発者が不透明であるため、ソースを見ないとJ DBCロードや事務管理などの方法が分かりません。
*自動配置の後、カスタム設定はjavaConfigを符号化する必要があります。これらの構成クラスのapiを知る必要があります。
*バージョンの反復が速すぎて、古いバージョンの変更が多すぎて、例えば1.3.5前のspringBootTestと1.4.0後のspringBootTestが互換性がない。
適切なアーキテクチャだけが最高のアーキテクチャです。もしspring bootのこれらの欠点を受け入れることができれば、spring bootは確かに開発効率を高めるいい選択です。
起動フロー
こんなにたくさん話して、本題に入ります。spring bootはどうやって起動して起動したのかを見てみましょう。
以下のコードはspring bootプロジェクト標準の起動方式で、注釈@Spring Bootationを使用して、mainメソッドでSprigAplicationのrunメソッドを呼び出すと完成できます。このrun法からspring bootの起動過程を見始めます。

@SpringBootApplication
public class Application {
  public static void main(String[] args){
    SpringApplication.run(Application.class,args);
  }
}
runメソッドに入ると、最終的にはnew SprigAplication(sources).run(args);new SprigAplication(sources).run(args);この方法は、springBootの起動は二つの部分に分けられ、第一部分:SprigAplicationの実用化が見られます。第二部分:この例を呼び出して、run方法を実行する。まずこのSpringApplicationの実用化過程を見てみます。

private void initialize(Object[] sources) {
    if (sources != null && sources.length > 0) {
      this.sources.addAll(Arrays.asList(sources));
    }
    //     webEnvironment
    this.webEnvironment = deduceWebEnvironment();
    //             ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    //             ApplicationListener
    setListeners((Collection)
    getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }
肝心な点は二つのセット方法で**します。setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class))** **setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))** この二つの方法は同じです。具体例を選んで、Appplication Contect Initializerに話してください。

private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, Object... args) {
      //      
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    //  loadFactoryNames       ApplicationContextInitializer      
    Set<String> names = new LinkedHashSet<String>(
        SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //        ApplicationContextInitializer      
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
        classLoader, args, names);
    //      
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
  }
自動配置の鍵はこのget SprigFactoresInstance方法です。正確にはこの方法のloadFactoryNames方法です。浪さんはこのloadFactoryNames方法が何をしているかを見てみます。自動配置はどうなりますか?

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    try {
      Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
      ArrayList result = new ArrayList();
      while(urls.hasMoreElements()) {
        URL url = (URL)urls.nextElement();
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
      }
      return result;
    } catch (IOException var8) {
      throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    }
  }
この方法を見て一つのことをしました。META-INF/spring.factoresというルートからすべての「url」を取り出してきました。このルートの下で何かを見に行きます。

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
springはあなたがロードしたjarカバンの中から必要なApple Contection Contect Initializerを見つけて動的に配置しています。特定のmavenパッケージを使えば、初期化の時にこのカバンのMETA-INF/spring.factoresの必要な種類を探しています。設定は不要です。
これはすでにSpringApplicationをすべて実例化しましたが、Appliation Contact InitializerとAppliation Listenerをロードし終わった後にもう一つのステップがあります。スタートクラスの位置を見つけて、属性manApplication Classに設定します。
次に、new SpringApplication(sources).run(args)方法に戻って、run方法がどうなっているかを見てみましょう。

public ConfigurableApplicationContext run(String... args) {
    //       ,              
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    //  SpringApplicationRunListener      
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
          args);
      //           
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
          applicationArguments);
      //   console                
      Banner printedBanner = printBanner(environment);
      //   boss->ApplicationContext   
      context = createApplicationContext();
      analyzers = new FailureAnalyzers(context);
      prepareContext(context, environment, listeners, applicationArguments,
          printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      listeners.finished(context, null);
      stopWatch.stop();
      if (this.logStartupInfo) {
        new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
      }
      return context;
    }
    catch (Throwable ex) {
      handleRunFailure(context, listeners, analyzers, ex);
      throw new IllegalStateException(ex);
    }
  }
この方法の中で一番重要なことは三つです。
モニターを取得して起動します。
環境変数をロードします。この環境変数は、system environment、classipath environment、ユーザーが自分で加えたappication.propertiesを含みます。
Apple Comptextを作成します。

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    context.getBeanFactory().registerSingleton("springApplicationArguments",
        applicationArguments);
    if (printedBanner != null) {
      context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }
    // Load the sources
    Set<Object> sources = getSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[sources.size()]));
    listeners.contextLoaded(context);
  }
前の2時は何も言いませんでした。第3回を重点的に話して、Application Contectを作ります。application Comptextを作成して、またいくつかの部に分けられます。実例化aplication Contect、preparext、refshComptext。具体例化appection Comptextは、前に述べたwebEnvironmentという属性によって、webContect類AnnotationConfigEmbodWebAppliation Computextを使って反射によって実行されます。appicationComptextの実装が完了したらprepare Comptextのプロセスに入ります。このprepare Contectの方法は、ロードする前に準備したenvironmentがcontextに入ります。そしてbeanNameGeneratorとreource Loaderがあれば、事前にbeanを作成してappration Contextにロードします。前の実装のすべてのinitializersを初期化します。すべてのbeanはここでbeanのスキャンとローディングを行います。今回の話は起動プロセスですので、詳しくは説明しません。最後に作成したappicationContectをlistenerに設定し、prepare Contectプロセスは終了しました。最後にrefreshContectです。これはspringのbeanロード過程と一致しています。beanの注入、beanFactory、postProcess BenFactoryなど、詳しくはspring beanのライフサイクルを見に行きます。
締め括りをつける
spring boot初期化の内容はまだ多いですが、まとめたら4時です。
*SprigAplicationの例を作成し、環境を判定します。ウェブ環境ですか?それとも普通環境ですか?すべての必要なInitializersとListenersをロードして、ここで約束より大きい理念を使って自動配置のベールを開けました。
*環境変数をロードし、環境変数には、system environment、classipath environment、appication environment(つまり、私たちがカスタマイズしたappication.propertiesプロファイル)が含まれます。
*SprigAplication RunListenersを作成する
*Application Contectを作成し、組み立てcontextを設定し、ここで全てのbeanをスキャンし、最後にrefsh Contectの時にロード、注入します。最終的に組み立てられたcontextを属性としてSpringAppliation RunListenersに設定し、これでspring bootプロジェクトの起動が完了しました。
以上は小编でご绍介したSpring Bootの起动过程です。皆さんに何かお聞きしたいことがあれば、メッセージをください。小编はすぐにご返事します。ここでも私たちのサイトを応援してくれてありがとうございます。