9,000 ワードの記事で、史上最も詳細な SpringBoot 起動プロセスを画像とテキストで理解できます。

ここに画像の説明を挿入
内部監査の深層心理によるインタビュアーの魂の拷問より:「スプリングブートの起動プロセスについて教えてください。」
困惑した顔のインタビュアー:「スプリングの構成を単純化します。主に自動組み立ての機能があり、 Tomcat コンテナが埋め込まれているため、直接起動できます。」;
インタビュアー: 「そうですね、これはその概念の一部です。まだ私の質問に答えていませんね。どのようにして起動し、起動時に何が行われたのですか?始めました? ";
混乱した面接官: 「ああ~~~ わかりません... 私はそれを使用することには非常に熟練していますが、それが何をするのかわかりません!」; 面接
官: 「内部原理を理解するのは、拡大に協力し、同時にその人の学習能力を確認してください。キャリア パスを次のレベルに引き上げたい場合は、これらの低レベルのことを知っておく必要があります。わかりました。戻ってニュースを待ちます。インタビュアー
:↓
ここに画像の説明を挿入

スプリングブートとは

Springboot は Spring に依存しており、Spring と比べて Spring のすべての機能を備えていることに加え、独自の強力な自動アセンブリ機能に依存する面倒な XML 設定が不要であり、Tomcat、Jetty などの Web コンテナーが組み込まれており、統合された springmvc により、追加のコンテナなしで springboot を直接実行できるようになり、組み込みサーバー、セキュリティ、インジケーター、正常性検出、外部構成など、大規模プロジェクトで一般的な非機能機能が提供されます。

実際、春、ブートが起動を意味することは誰もが知っています。したがって、Spring Boot は実際には Spring プロジェクトを開始するためのツールであり、一言で言えば、Spring Boot はフレームワークを提供するフレームワークであり、Spring の構成を簡素化するツールであるとも言えます。

Spring Boot のコア機能

1. 独立して実行できる Spring プロジェクト: Spring Boot は、jar パッケージの形式で独立して実行できます。

2. 埋め込みサーブレット コンテナ: Spring Boot は、Tomcat、Jetty、または Undertow を埋め込むことを選択でき、war パッケージの形式でプロジェクトをデプロイする必要はありません。

3. 簡素化された Maven 構成: Spring は、Maven 構成を簡素化するために推奨される基本 POM ファイルを提供します。

4. Spring を自動的に構成する: Spring Boot は、プロジェクトの依存関係に従って Spring フレームワークを自動的に構成し、プロジェクトで使用する構成を大幅に削減します。

5. 本番環境に対応した機能の提供: パフォーマンス指標、アプリケーション情報、アプリケーションのヘルスチェックなど、本番環境で直接使用できる機能を提供します。

6. コード生成と XML 構成なし: Spring Boot はコードを生成しません。Spring のすべての構成は、XML 構成をまったく必要とせずに実現できます。

SpringBootの起動処理

springboot の起動は一連のプロセスを経ています。全体的なプロセスのフローチャートを見てみましょう。
ここに画像の説明を挿入
かなり多くの手順がありますが、気にしないでください。誰もが理解しています、上記の数字を一つずつ一般的な方法で説明します、内部で何が行われているかを分かりやすく全員に伝えます、無駄なことは言わずに、すべてのプロセスを開始してください

1. SpringApplication.run() メソッドを実行します。

確かに、すべての標準 Springboot アプリケーションは run メソッドから開始します。

package com.spring;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
 
@SpringBootApplication
public class App  {
    
    
 
    public static void main(String[] args) {
    
    
        // 启动springboot
        ConfigurableApplicationContext run = SpringApplication.run(App.class, args);
    }
 
}

run メソッドに入ると、新しい SpringApplication オブジェクトが作成され、このオブジェクトを作成するコンストラクターがいくつかの準備作業を行っています。番号のステップ 2 ~ 5 がコンストラクターで行われる処理です。

	/**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified sources using default settings and user supplied arguments.
	 * @param primarySources the primary sources to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
    
    
		return new SpringApplication(primarySources).run(args);
	}

なお、springboot の起動には 3 つの方法があることを説明します。その他の起動方法については、別の投稿「SpringBoot を起動する 3 つの方法」を参照してください。

2. アプリケーションの種類を決定する

SpringApplicationの構築方法では、まずWebApplicationType.deduceFromClasspath()メソッドを用いて現在のアプリケーションのコンテナを判定し、デフォルトではServletコンテナが使用されます。サーブレットの他にNONEとREACTIVE(レスポンシブプログラミング)があります。 );
ここに画像の説明を挿入

3. すべてのイニシャライザをロードします

ここでロードされるイニシャライザは、META-INF/spring.factories 構成ファイルからロードされる springboot 独自のイニシャライザです。このファイルはどこにあるのでしょうか? ソースコードのjarパッケージのspring-boot-autoconfigureプロジェクトとspring-bootプロジェクトにspring.factoriesファイルがあり
ここに画像の説明を挿入
、先頭のインターフェースがorg.springframework.contextであることがわかる。 .ApplicationContextInitializer は初期化子です。
ここに画像の説明を挿入
もちろん、カスタム初期化子を自分で実装することもできます: ApplicationContextInitializer インターフェイスを実装します。

MyApplicationContextInitializer.java

package com.spring.application;
 
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
 * 自定义的初始化器
 */
public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    
    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
    
    
        System.out.println("我是初始化的 MyApplicationContextInitializer...");
    }
}

以下の内容の META-INF/spring.factories 設定ファイルをリソースディレクトリに追加し、カスタムイニシャライザを登録します。

org.springframework.context.ApplicationContextInitializer=\
com.spring.application.MyApplicationContextInitializer

ここに画像の説明を挿入
springboot を起動すると、コンソールにコンテンツが表示されます。ここでは、バナーの印刷後に実行される実行シーケンスを直感的に確認できます。
ここに画像の説明を挿入

4. すべてのリスナーをロードします

リスナーのロードは、META-INF/spring.factories 構成ファイルからもロードされます。初期化とは異なり、リスナーは、ApplicationListener インターフェイスを実装するクラスをロードします。カスタム リスナーもイニシャライザと同じです。大丈夫、そうではありませ
ここに画像の説明を挿入
んここで例を挙げます。

5. 実行するプログラムのメインクラスを設定します。

deduceMainApplicationClass(); このメソッドは、後続のパッケージ スキャンに備えて、メイン メソッドが配置されているクラスを見つけることだけを目的としており、推論とは推論することを意味するため、正確には、このメソッドの機能はメイン メソッドが配置されているクラスを推論することです。メソッドが見つかりました。
ここに画像の説明を挿入

6. タイマーをスタートする

ここでプログラムが実行されると、run メソッドの本体に入ります。最初のステップで呼び出される run メソッドは静的メソッドです。この時点では、SpringApplication オブジェクトはインスタンス化されていません。現在呼び出される run メソッドは、静的でインスタンス化する必要があります。呼び出すことができます。入力後、最初にタイマーが開始されます。このタイマーの機能は何ですか? 名前が示すように、これはタイミングを計るために使用され、springboot の起動にかかる時間を計算します。キー コードは次のとおりです。

// 实例化计时器
StopWatch stopWatch = new StopWatch(); 
// 开始计时
stopWatch.start();

run メソッドのコードセグメントのスクリーンショット
ここに画像の説明を挿入

7. java.awt.headless を true に設定します

ここでは java.awt.headless が true に設定されています。これは、java.awt.headless がサーバー側で実行され、モニター、マウス、キーボードなしのモードでも動作し、入出力デバイスの機能をシミュレートできることを意味します。

このような操作を行った後、SpringBoot は何をしたいのでしょうか? 実際には、ディスプレイが検出されなくてもアプリケーションが起動できるように設定したいのですが、サーバーの場合、ディスプレイは必要ないので、このように設定されます。

メソッド本体は次のとおりです。

	private void configureHeadlessProperty() {
    
    
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
				SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

このメソッドを通して、setProperty() メソッド内に別の getProperty() があることがわかりますが、これは不要ではないでしょうか。実際、getProperty() メソッドには 2 つのパラメータがあり、1 つ目のキー値と 2 つ目はデフォルト値であり、キー値を通じてプロパティ値を検索することを意味します。プロパティ値が空の場合、デフォルト値は true です。値 Case が存在することが保証されます。

8. リスナーを取得して有効にする

このステップでは、リスナーを介した初期化の基本操作を実装します。このステップでは 2 つのことを行います。

  1. すべての Spring run リスナーを作成し、アプリケーション開始イベントを発行します
  2. リスナーを有効にする
    ここに画像の説明を挿入

9. アプリケーションパラメータを設定する

runメソッド実行時に渡されるパラメータをオブジェクトにカプセル化
ここに画像の説明を挿入
する パラメータをオブジェクトにカプセル化するだけです。言うことはありません。オブジェクトのコンストラクタは次のとおりです。

	public DefaultApplicationArguments(String[] args) {
    
    
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
	}

そこで問題は、このパラメータがどこから来たのかということです。実際、これは main メソッドで静的 run メソッドを実行することによって渡されるパラメーターです。
ここに画像の説明を挿入

10. 環境変数を準備する

システム プロパティとユーザー構成プロパティを含む環境変数を準備します。実行されたコード ブロックは、 prepareEnvironment メソッドのブレークポイントを解除した後に表示されます。これにより、ここに画像の説明を挿入
Maven 環境変数とシステム環境変数の両方がロードされます。
ここに画像の説明を挿入

11. Bean 情報を無視する

このメソッドconfigureIgnoreBeanInfo() このメソッドは、spring.beaninfo.ignore のデフォルト値を true に設定します。これは、beanInfo の検索をスキップすることを意味します。デフォルト値を設定する原理はステップ 7 と同じです。

    private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
    
    
		if (System.getProperty(
				CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
    
    
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
					Boolean.class, Boolean.TRUE);
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,
					ignore.toString());
		}
	}

もちろん、次の構成を構成ファイルに追加して false に設定することもできます。

spring.beaninfo.ignore=false

この構成の具体的な機能はまだわかっていないので、作者が見つけたら追加します

12. バナー情報を印刷する

明らかに、このプロセスは、次のような大きな春のバナーをコンソールに印刷するために使用されます。
ここに画像の説明を挿入
彼はそれをどこに印刷したのでしょうか? 彼はそれを SpringBootBanner.java に出力しました。このクラスは Banner インターフェイスを実装します。

バナー情報はコードに直接書き込まれます。
ここに画像の説明を挿入
企業によっては、バナー情報をカスタマイズすることを好みますが、お気に入りのアイコンに変更したい場合はどうすればよいですか。実際には非常に簡単で、リソースに Banner.txt を追加するだけです。ディレクトリ txt ファイルの内容は次のとおりです

                 _           _
                (_)         | |
 _   _  _____  ___ _ __   __| | ___  _ __   __ _
| | | |/ _ \ \/ / | '_ \ / _` |/ _ \| '_ \ / _` |
| |_| |  __/>  <| | | | | (_| | (_) | | | | (_| |
 \__, |\___/_/\_\_|_| |_|\__,_|\___/|_| |_|\__, |
  __/ |                                     __/ |
 |___/                                     |___/
:: yexindong::

必ずリソースディレクトリに追加してください。間違って追加しないでください
ここに画像の説明を挿入
。必要なのはファイルを追加するだけで、他には何もする必要はありません。その後、springboot を直接起動すると、効果が確認できます。
ここに画像の説明を挿入

13. アプリケーションコンテキストの作成

アプリケーションのコンテキストをインスタンス化し、createApplicationContext() メソッドを呼び出します。ここではリフレクションを使用してオブジェクトを作成します。言うことはありません。
ここに画像の説明を挿入

14. 例外レポーターをインスタンス化する

例外レポーターは、グローバル例外をキャプチャするために使用されます。スプリングブート アプリケーションで例外が発生すると、例外レポーターはそれをキャプチャし、それに応じて処理します。デフォルトの例外レポーターは spring.factories ファイルで構成されます。

ここに画像の説明を挿入
この例外レポーターは、起動プロセス中にスローされた例外のみをキャッチすることに注意してください。起動完了後にユーザー リクエストでエラーが報告された場合、例外レポーターはリクエストで発生した例外をキャッチしません。原理は理解できたと思います。
ここに画像の説明を挿入
次に、例外レポーターを自分自身で操作するように設定します。

MyExceptionReporter.java は SpringBootExceptionReporter インターフェースを継承します

package com.spring.application;
 
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext;
 
public class MyExceptionReporter implements SpringBootExceptionReporter {
    
    
 
 
    private ConfigurableApplicationContext context;
    // 必须要有一个有参的构造函数,否则启动会报错
    MyExceptionReporter(ConfigurableApplicationContext context) {
    
    
        this.context = context;
    }
 
    @Override
    public boolean reportException(Throwable failure) {
    
    
        System.out.println("进入异常报告器");
        failure.printStackTrace();
        // 返回false会打印详细springboot错误信息,返回true则只打印异常信息 
        return false;
    }
}

spring.factories ファイルに例外レポーターを登録します。

# Error Reporters 异常报告器
org.springframework.boot.SpringBootExceptionReporter=\
com.spring.application.MyExceptionReporter

ここに画像の説明を挿入
次に、application.yml でポート番号を大きな値に設定します。これにより、間違いなくエラーが発生します。

server:
  port: 80828888

起動後、コンソールに次の画像が表示されます。
ここに画像の説明を挿入

15. コンテキストを準備する

ここで準備されたコンテキスト環境は、次の更新に備えるためのものであり、その中でいくつかの追加の処理が行われます。
ここに画像の説明を挿入

15.1. シングルトンをインスタンス化するための BeanName ジェネレーター

postProcessApplicationContext(context); メソッド内。BeanNameGenerator オブジェクトはシングルトン モードを使用して作成されます。これは、実際には Bean オブジェクトの名前を生成するために使用される BeanName ジェネレーターです。

15.2. 初期化メソッドの実行

初期化方法にはどのようなものがありますか? ステップ 3 でロードされたイニシャライザを覚えていますか? 実際には、手順 3 でロードされたすべてのイニシャライザが実行され、ApplicationContextInitializer インターフェイスを実装するクラスが実行されます。

15.3. 起動パラメータをコンテナに登録する

ここでは、将来の便宜のために、起動パラメータがシングルトン モードでコンテナに登録されています。

16. コンテキストを更新する

コンテキストの更新はすでに Spring のカテゴリーに含まれており、Tomcat の自動アセンブリと起動はこのメソッドで完了します。ここでは詳しく説明しない他の Spring 構築メカニズムもあります。
ここに画像の説明を挿入

17. コンテキストの後処理を更新する

afterRefresh メソッドは起動後の処理であり、ユーザー拡張用に予約されており、現在は空のメソッドです。

/**
	 * Called after the context has been refreshed.
	 * @param context the application context
	 * @param args the application arguments
	 */
	protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
    
    
	}

18. 終了タイマー

この時点で、springboot は実際に完了しており、タイマーは springboot を開始する時間を出力します。
ここに画像の説明を挿入
コンソールに表示されるように、起動は依然として非常に高速であり、起動は 2 秒未満で完了します。
ここに画像の説明を挿入

19. コンテキスト準備完了イベントのパブリッシュ

仕事に行く準備ができていることをアプリに伝えます
ここに画像の説明を挿入

20. カスタム run メソッドを実行する

これは拡張関数であり、callRunners(context, applicationArguments) は起動完了後にカスタム run メソッドを実行できます。これを実現するには 2 つの方法があります。

  1. ApplicationRunner インターフェイスを実装する
  2. CommandLineRunner インターフェイスを実装する

次に、コードを読みやすくするために、これら 2 つのメソッドを同じクラスに入れたことを確認してみましょう。

package com.spring.init;
 
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
/**
 * 自定义run方法的2种方式
 */
@Component
public class MyRunner implements ApplicationRunner, CommandLineRunner {
    
    
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
    
    
        System.out.println(" 我是自定义的run方法1,实现 ApplicationRunner 接口既可运行"        );
    }
 
    @Override
    public void run(String... args) throws Exception {
    
    
        System.out.println(" 我是自定义的run方法2,实现 CommandLineRunner 接口既可运行"        );
    }
}

springboot を起動すると、コンソールに情報が表示されます。
ここに画像の説明を挿入

以上

実際、開発者にとってスプリングブートの起動原理を理解することは良いことです。少なくとも、何を拡張できるか、どのように拡張するか、内部原理がどのようになっているのかがわかります。これらのアイデアを理解した後、スプリングブートを自分で作成してみるとよいと思います。それも可能です。しかし、ここにあるのは起動プロセスのリストだけで、すべてが関係しているわけではありません。ソース コードは非常に複雑です。大きな牛がこう言ったのを覚えています。「ソース コードを見ても、何が起こっているのか推測することしかできません。」私たちが幼い頃に古詩を習ったときと同じように、私たちは古代人の考えを推測することしかできません。『道経経』を例にとると、それを読んだ後は人それぞれ異なる意見があります。 、それには異なる意見が必要です。」

おすすめ

転載: blog.csdn.net/weixin_44947701/article/details/124055713
おすすめ