目次
はじめに
JAR とは
JAR の概要
パッケージ構造
実行不可能な JAR であっても、実行可能な JAR であっても、解凍後には 2 つの部分が含まれます。
JAR パッケージの構成ファイルは、
SpringBoot FatJar ソリューション
および
打包结果
启动时的类加载原理
启动的整个流程
ホームページ Java &#&チュートリアル Springboot の FatJar と Jar とは何ですか

Springboot の FatJar と Jar とは何ですか

May 11, 2023 am 09:58 AM
jar springboot fatjar

    はじめに

    Spring Boot アプリケーションは、spring-boot-maven-plugin を使用して迅速にパッケージ化し、 実行可能ファイルを構築できます。瓶。 Spring Boot には埋め込みコンテナーがあり、アプリケーションは java -jar コマンドを通じて直接起動できます。

    これは単純な起動コマンドですが、その背後には多くの知識が隠されています。今日は、FAT JAR のスタートアップの背後にある原則を探っていきます。この記事は主に次の部分で構成されています:

    • JAR とは。 java -jar が何をするのかを知る前に、まず、jar とは何かを理解する必要があります。

    • FatJar との違いは何ですか。 Spring Boot が提供する実行可能 jar と通常の jar の違いは何ですか?

    • 起動時のクラスロードの原則。クラスローダーは起動時に何をしますか? Spring Boot は、カスタム クラス ローダーを介した埋め込みパッケージの読み込みの問題をどのように解決しますか。

    • プロセス全体が開始されました。最後に、前の 3 つの部分の内容を統合し、ソース コードを分析して起動を完了する方法を確認します。

    JAR とは

    JAR の概要

    JAR ファイル (Java アーカイブ、英語: Java AR#) # #chive) は、Java プラットフォーム アプリケーション ソフトウェアまたはライブラリを配布するために、多数の Java クラス ファイル、関連メタデータ、およびリソース (テキスト、画像など) ファイルを 1 つのファイルに集約するために通常使用されるソフトウェア パッケージ ファイル形式です。簡単に説明すると、これは圧縮パッケージです。圧縮パッケージなので、JAR ファイルの内容を抽出するには、標準的な解凍解凍ソフトウェアを使用して内容を抽出できます。または、Java 仮想マシンに付属のコマンド jar -xf foo.jar を使用して、対応する jar ファイルを解凍します。

    JAR は、

    • 非実行可能 JAR の 2 つのカテゴリに単純に分類できます。パッケージ化する場合、main-class を指定する必要はなく、実行することもできません。通常の jar パッケージは、他のプロジェクトが依存するために使用できます。

    • 実行可能 JAR。 jar パッケージをビルドする場合、main-class クラスが指定されており、java -jar xxx.jar を通じて main-classmain# を実行できます。 コマンド。##方法は、jar パッケージを実行します。実行可能な jar パッケージ は、他のプロジェクトから 依存することはできません。

    • JAR 構造

    パッケージ構造

    実行不可能な JAR であっても、実行可能な JAR であっても、解凍後には 2 つの部分が含まれます。
    META-INF

    ディレクトリ (メタデータ) と package ディレクトリ (コンパイルされたクラス)。この通常の jar にはサードパーティの依存関係パッケージは含まれておらず、アプリケーション独自の構成ファイル、クラスなどのみが含まれています。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>. ├── META-INF │ ├── MANIFEST.MF #定义 └── org # 包路径(存放编译后的class) └── springframework</pre><div class="contentsignin">ログイン後にコピー</div></div> 説明ファイル MANIFEST.MF

    JAR パッケージの構成ファイルは、
    META-INF

    フォルダー内の MANIFEST.MF ファイルです。 主な構成情報は次のとおりです:

      Manifest-Version: マニフェスト ファイルのバージョンを定義するために使用されます。例: Manifest-Version: 1.0
    • #Created-By: ファイルのジェネレーターを宣言します。通常、この属性は jar コマンド ライン ツールによって生成されます (例: Created-By: Apache Ant 1.5.1
    • #)
    • ## Signature-Version: jar ファイルの署名バージョンを定義します。

    • ##Class-Path: アプリケーションまたはクラス ローダーは、この値を使用して内部クラス検索を構築します。パス。これは実行可能 jar パッケージに設定する必要があります。

    • 上記は

      通常の jar パッケージ

    • 実行可能な jar パッケージの .MF ファイル
    の属性ですが、

    も存在します。 mian- classstart-class などの属性。外部の jar パッケージに依存する場合、lib パスおよびその他の情報も MF ファイルで構成されます。 詳細情報参照: Maven を使用して MANIFEST.MF ファイルにコンテンツを追加する方法実行可能な jar パッケージ および 通常の jar パッケージのディレクトリについては、 特に決まった構造はありませんが、つまり、どのような構造であっても、.MF ファイルに jar パッケージの情報を設定すれば、通常通り jar パッケージを使用することができます。

    FatJar の違いは何ですか。FatJar とは何ですか? 通常の jar には、現在の jar に関する情報のみが含まれ、サードパーティの jar は含まれません。内部的にサードパーティの jar に依存している場合、直接実行するとエラーが報告されます。この場合、サードパーティの jar を実行可能 jar に埋め込む必要があります。

    jar とそれに依存するサードパーティの jar を 1 つのパッケージに入れます。このパッケージは FatJar です。

    SpringBoot FatJar ソリューション

    Spring Boot埋め込み jar の問題を解決するために、

    jar を定義する一連の FatJar ソリューションが提供されています。構造体

    および

    MANIFEST.MF

    。実行可能 jar のコンパイルと生成に基づいて、Spring Boot 実行可能パッケージ標準 repackage に従って spring-boot-maven-plugin を使用して、実行可能 Spring Boot jar を取得します。 実行可能jarの種類により、実行可能jarと実行可能warの2種類に分かれます。 spring-boot-maven-plugin パッケージ化プロセス #新しく作成された空の SpringBoot プロジェクトのどこにも、関連クラスの明示的な導入や記述がないためです。実際、新しく作成された SpringBoot プロジェクトごとに、pom.xml ファイルに次のプラグインが含まれています。

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    ログイン後にコピー

    这个是SpringBoot官方提供的用于打包FatJar的插件,org.springframework.boot.loader下的类其实就是通过这个插件打进去的;

    下面是此插件将 loader 相关类打入 FatJar 的一个执行流程:

    org.springframework.boot.maven#execute->
    org.springframework.boot.maven#repackage -> org.springframework.boot.loader.tools.Repackager#repackage->
    org.springframework.boot.loader.tools.Repackager#writeLoaderClasses->
    org.springframework.boot.loader.tools.JarWriter#writeLoaderClasses
    ログイン後にコピー

    最终的执行方法就是下面这个方法,通过注释可以看出,该方法的作用就是将 spring-boot-loader 的classes 写入到 FatJar 中。

    /**
     * Write the required spring-boot-loader classes to the JAR.
     * @throws IOException if the classes cannot be written
     */
    @Override
    public void writeLoaderClasses() throws IOException {
    	writeLoaderClasses(NESTED_LOADER_JAR);
    }
    ログイン後にコピー
    打包结果

    Spring Boot项目被编译以后,在targert目录下存在两个jar文件:一个是xxx.jarxxx.jar.original

    • 其中xxx.jar.original是maven编译后的原始jar文件,即标准的java jar。该文件仅包含应用本地资源。 如果单纯使用这个jar,无法正常运行,因为缺少依赖的第三方资源。

    • 因此spring-boot-maven-plugin插件对这个xxx.jar.original再做一层加工,引入第三方依赖的jar包等资源,将其 "repackage"xxx.jar。可执行Jar的文件结构如下图所示:

    .
    ├── BOOT-INF
    │   ├── classes
    │   │   ├── application.properties  # 用户-配置文件
    │   │   └── com
    │   │       └── glmapper
    │   │           └── bridge
    │   │               └── boot
    │   │                   └── BootStrap.class  # 用户-启动类
    │   └── lib
    │       ├── jakarta.annotation-api-1.3.5.jar
    │       ├── jul-to-slf4j-1.7.28.jar
    │       ├── log4j-xxx.jar # 表示 log4j 相关的依赖简写
    ├── META-INF
    │   ├── MANIFEST.MF
    │   └── maven
    │       └── com.glmapper.bridge.boot
    │           └── guides-for-jarlaunch
    │               ├── pom.properties
    │               └── pom.xml
    └── org
        └── springframework
            └── boot
                └── loader
                    ├── ExecutableArchiveLauncher.class
                    ├── JarLauncher.class
                    ├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
                    ├── LaunchedURLClassLoader.class
                    ├── Launcher.class
                    ├── MainMethodRunner.class
                    ├── PropertiesLauncher$1.class
                    ├── PropertiesLauncher$ArchiveEntryFilter.class
                    ├── PropertiesLauncher$PrefixMatchingArchiveFilter.class
                    ├── PropertiesLauncher.class
                    ├── WarLauncher.class
                    ├── archive
                    │   ├── # 省略
                    ├── data
                    │   ├── # 省略
                    ├── jar
                    │   ├── # 省略
                    └── util
                        └── SystemPropertyUtils.class
    ログイン後にコピー
    • META-INF: 存放元数据。MANIFEST.MF 是 jar 规范,Spring Boot 为了便于加载第三方 jar 对内容做了修改;

    • org: 存放Spring Boot 相关类,比如启动时所需的 Launcher 等;

    • BOOT-INF/class: 存放应用编译后的 class 文件;

    • BOOT-INF/lib: 存放应用依赖的 JAR 包。

    Spring Boot的MANIFEST.MF和普通jar有些不同:

    Spring-Boot-Version: 2.1.3.RELEASE
    Main-Class: org.springframework.boot.loader.JarLauncher
    Start-Class: com.rock.springbootlearn.SpringbootLearnApplication
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    Build-Jdk: 1.8.0_131
    ログイン後にコピー

    Main-Class:java -jar启动引导类,但这里不是项目中的类,而是Spring Boot内部的JarLauncher
    Start-Class: 这个才是正在要执行的应用内部主类

    所以java -jar启动的时候,加载运行的是JarLauncher。Spring Boot内部如何通过JarLauncher 加载Start-Class 执行呢?为了更清楚加载流程,我们先介绍下java -jar是如何完成类加载逻辑的。

    启动时的类加载原理

    这里简单说下java -jar启动时是如何完成记载类加载的。Java 采用了双亲委派机制,Java语言系统自带有三个类加载器:

    • Bootstrap CLassloder: 最顶层的加载类,主要加载核心类库

    • Extention ClassLoader: 扩展的类加载器,加载目录%JRE_HOME%/lib/ext目录下的jar包和class文件。 还可以加载-D java.ext.dirs选项指定的目录。

    • AppClassLoader: 是应用加载器。

    默认情况下通过java -classpathjava -cpjava -jar使用的类加载器都是AppClassLoader。 普通可执行jar通过java -jar启动后,使用AppClassLoader加载Main-class类。 如果第三方jar不在AppClassLoader里,会导致启动时候会报ClassNotFoundException。

    例如在Spring Boot可执行jar的解压目录下,执行应用的主函数,就直接报该错误:

    Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
    at com.glmapper.bridge.boot.BootStrap.main(BootStrap.java:13)
    Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more

    从异常堆栈来看,是因为找不到SpringApplication这个类;这里其实还是比较好理解的,BootStrap类中引入了SpringApplication,但是这个类是在BOOT-INF/lib下的,而java指令在启动时未指明classpath,依赖的第三方jar无法被加载。

    Spring Boot JarLauncher启动时,会将所有依赖的内嵌 jar (BOOT-INF/lib 目录下) 和class(BOOT-INF/classes 目录)都加入到自定义的类加载器LaunchedURLClassLoader中,并用这个ClassLoder去加载MANIFEST.MF配置Start-Class,则不会出现类找不到的错误。

    LaunchedURLClassLoader是URLClassLoader的子类, URLClassLoader会通过URL[] 来搜索类所在的位置。Spring Boot 则将所需要的内嵌文档组装成URL[],最终构建LaunchedURLClassLoader类。

    启动的整个流程

    有了以上知识的铺垫,我们看下整个 FatJar 启动的过程会是怎样。为了以便查看源码和远程调试,可以在 pom.xml 引入下面的配置:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-loader</artifactId>
    </dependency>
    ログイン後にコピー

    简单概括起来可以分为几步:

    • java -jar 启动,AppClassLoader 则会加载 MANIFEST.MF 配置的Main-Class, JarLauncher。

    • JarLauncher启动时,注册URL关联协议。

    • 获取所有内嵌的存档(内嵌jar和class)

    • 根据存档的URL[]构建类加载器。

    • 然后用这个类加载器加载Start-Class。 保证这些类都在同一个ClassLoader中。

    以上がSpringboot の FatJar と Jar とは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

    リアルなヌード写真を作成する AI 搭載アプリ

    AI Clothes Remover

    AI Clothes Remover

    写真から衣服を削除するオンライン AI ツール。

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    Video Face Swap

    Video Face Swap

    完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

    ホットツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    SublimeText3 中国語版

    SublimeText3 中国語版

    中国語版、とても使いやすい

    ゼンドスタジオ 13.0.1

    ゼンドスタジオ 13.0.1

    強力な PHP 統合開発環境

    ドリームウィーバー CS6

    ドリームウィーバー CS6

    ビジュアル Web 開発ツール

    SublimeText3 Mac版

    SublimeText3 Mac版

    神レベルのコード編集ソフト(SublimeText3)

    Linux で jar ファイルを実行する方法 Linux で jar ファイルを実行する方法 Feb 20, 2024 am 10:40 AM

    JAR ファイルを実行するための前提条件 Linux システムで JAR ファイルを実行するには、Java 仮想マシン (JVM) やコア クラス ライブラリなどの Java アプリケーションを実行するために必要な基本コンポーネントである Java ランタイム環境 (JRE) をインストールする必要があります。 Ubuntu、Debian、Fedora、openSUSE などの多くの主流 Linux ディストリビューションは、ユーザーのインストールを容易にするための JRE パッケージのソフトウェア ライブラリを提供しています。次の記事では、一般的なディストリビューションに JRE をインストールする手順について詳しく説明します。 JRE をセットアップした後、個人の好みに応じて、コマンド ライン ターミナルまたはグラフィカル ユーザー インターフェイスの使用を選択して、JAR ファイルを開始できます。選択は、Linux シェルの知識と個人の好みによって決まるかもしれません。

    Springboot が Jasypt を統合して構成ファイルの暗号化を実装する方法 Springboot が Jasypt を統合して構成ファイルの暗号化を実装する方法 Jun 01, 2023 am 08:55 AM

    Jasypt の概要 Jasypt は、開発者が最小限の労力で基本的な暗号化機能を自分のプロジェクトに追加できる Java ライブラリであり、暗号化の仕組みを深く理解する必要はありません。一方向および双方向暗号化の高いセキュリティ。標準ベースの暗号化テクノロジー。パスワード、テキスト、数値、バイナリを暗号化します... Spring ベースのアプリケーション、オープン API への統合、JCE プロバイダーでの使用に適しています... 次の依存関係を追加します: com.github.ulisesbocchiojasypt-spring-boot-starter2. 1.1 Jasypt の特典はシステムのセキュリティを保護し、コードが漏洩した場合でもデータ ソースは保証されます。

    Redis を使用して SpringBoot に分散ロックを実装する方法 Redis を使用して SpringBoot に分散ロックを実装する方法 Jun 03, 2023 am 08:16 AM

    1. Redis は分散ロックの原則を実装しており、分散ロックが必要な理由 分散ロックについて話す前に、分散ロックが必要な理由を説明する必要があります。分散ロックの反対はスタンドアロン ロックです。マルチスレッド プログラムを作成するとき、共有変数を同時に操作することによって引き起こされるデータの問題を回避します。通常、ロックを使用して共有変数を相互に除外し、データの正確性を確保します。共有変数の使用範囲は同じプロセス内です。共有リソースを同時に操作する必要があるプロセスが複数ある場合、どうすれば相互排他的になるのでしょうか?今日のビジネス アプリケーションは通常マイクロサービス アーキテクチャであり、これは 1 つのアプリケーションが複数のプロセスをデプロイすることも意味します。複数のプロセスが MySQL の同じレコード行を変更する必要がある場合、順序の乱れた操作によって引き起こされるダーティ データを避けるために、分散が必要です。今回導入するスタイルはロックされています。ポイントを獲得したい

    SpringBoot が Redisson を統合して遅延キューを実装する方法 SpringBoot が Redisson を統合して遅延キューを実装する方法 May 30, 2023 pm 02:40 PM

    使用シナリオ 1. 注文は正常に行われましたが、支払いが 30 分以内に行われませんでした。支払いがタイムアウトになり、注文が自動的にキャンセルされました 2. 注文に署名があり、署名後 7 日間評価が行われませんでした。注文がタイムアウトして評価されない場合、システムはデフォルトでプラスの評価を設定します 3. 注文は正常に行われます。販売者が 5 分間注文を受け取らない場合、注文はキャンセルされます。 4. 配送がタイムアウトします。 SMS リマインダーをプッシュします... 遅延が長く、リアルタイム パフォーマンスが低いシナリオでは、タスク スケジュールを使用して定期的なポーリング処理を実行できます。例: xxl-job 今日は選択します

    Springbootがjarパッケージにファイルを読み込んだ後にファイルにアクセスできない問題を解決する方法 Springbootがjarパッケージにファイルを読み込んだ後にファイルにアクセスできない問題を解決する方法 Jun 03, 2023 pm 04:38 PM

    Springboot はファイルを読み取りますが、jar パッケージにパッケージ化した後、最新の開発にアクセスできません。jar パッケージにパッケージ化した後、Springboot がファイルを読み取れない状況があります。その理由は、パッケージ化後、ファイルの仮想パスが変更されるためです。は無効であり、ストリーム経由でのみアクセスできます。読み取ります。ファイルはリソースの下にあります publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

    SQL ステートメントを使用せずに Springboot+Mybatis-plus を実装して複数のテーブルを追加する方法 SQL ステートメントを使用せずに Springboot+Mybatis-plus を実装して複数のテーブルを追加する方法 Jun 02, 2023 am 11:07 AM

    Springboot+Mybatis-plus が SQL ステートメントを使用して複数テーブルの追加操作を実行しない場合、私が遭遇した問題は、テスト環境で思考をシミュレートすることによって分解されます: パラメーターを含む BrandDTO オブジェクトを作成し、パラメーターをバックグラウンドに渡すことをシミュレートします。 Mybatis-plus で複数テーブルの操作を実行するのは非常に難しいことを理解してください。Mybatis-plus-join などのツールを使用しない場合は、対応する Mapper.xml ファイルを設定し、臭くて長い ResultMap を設定するだけです。対応する SQL ステートメントを記述します。この方法は面倒に見えますが、柔軟性が高く、次のことが可能です。

    SpringBootとSpringMVCの比較と差異分析 SpringBootとSpringMVCの比較と差異分析 Dec 29, 2023 am 11:02 AM

    SpringBoot と SpringMVC はどちらも Java 開発で一般的に使用されるフレームワークですが、それらの間には明らかな違いがいくつかあります。この記事では、これら 2 つのフレームワークの機能と使用法を調べ、その違いを比較します。まず、SpringBoot について学びましょう。 SpringBoot は、Spring フレームワークに基づいたアプリケーションの作成と展開を簡素化するために、Pivo​​tal チームによって開発されました。スタンドアロンの実行可能ファイルを構築するための高速かつ軽量な方法を提供します。

    SpringBoot が Redis をカスタマイズしてキャッシュのシリアル化を実装する方法 SpringBoot が Redis をカスタマイズしてキャッシュのシリアル化を実装する方法 Jun 03, 2023 am 11:32 AM

    1. RedisAPI のデフォルトのシリアル化メカニズムである RedisTemplate1.1 をカスタマイズします。API ベースの Redis キャッシュ実装では、データ キャッシュ操作に RedisTemplate テンプレートを使用します。ここで、RedisTemplate クラスを開いて、クラスのソース コード情報を表示します。publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations、BeanClassLoaderAware{//キーを宣言、値の各種シリアル化メソッド、初期値は空 @NullableprivateRedisSe

    See all articles