Rumah  >  Artikel  >  Java  >  Apakah FatJar dan Jar dalam Springboot?

Apakah FatJar dan Jar dalam Springboot?

WBOY
WBOYke hadapan
2023-05-11 09:58:131832semak imbas

    Pengenalan

    Aplikasi Spring Boot boleh dibungkus dengan cepat menggunakan spring-boot-maven-plugin untuk membina balang boleh laku. Spring Boot mempunyai bekas terbenam, dan aplikasi boleh dimulakan terus melalui perintah java -jar.

    Walaupun ia adalah arahan permulaan yang mudah, terdapat banyak pengetahuan yang tersembunyi di sebaliknya. Hari ini saya akan membawa anda meneroka prinsip di sebalik permulaan FAT JAR. Artikel ini terutamanya mengandungi bahagian berikut:

    • Apakah itu JAR. Pertama, anda perlu memahami apa itu balang sebelum anda mengetahui apa yang java -jar lakukan.

    • Apa yang membezakan FatJar. Apakah perbezaan antara balang boleh laku yang disediakan oleh Spring Boot dan balang biasa?

    • Prinsip pemuatan kelas pada permulaan. Apakah yang dilakukan oleh pemuat kelas semasa permulaan? Bagaimanakah Spring Boot menyelesaikan masalah pemuatan pakej terbenam melalui pemuat kelas tersuai.

    • Keseluruhan proses dimulakan. Akhir sekali, sepadukan kandungan tiga bahagian sebelumnya dan analisis kod sumber untuk melihat cara melengkapkan permulaan.

    Apakah itu JAR

    Pengenalan JAR

    Fail JAR (Arkib Java, Bahasa Inggeris: Java ARchive) ialah format fail pakej perisian yang biasanya digunakan untuk mengagregat sejumlah besar fail kelas Java, metadata dan fail sumber (teks, imej, dll.) yang berkaitan ke dalam satu fail untuk mengedarkan perisian aplikasi platform Java atau perpustakaan . Untuk memahaminya secara ringkas, ia sebenarnya adalah pakej termampat Memandangkan ia adalah pakej termampat, untuk mengekstrak kandungan fail JAR, anda boleh menggunakan mana-mana perisian penyahmampatan standard untuk mengekstrak kandungan. Atau gunakan perintah terbina dalam mesin maya Java jar -xf foo.jar untuk menyahmampat fail balang yang sepadan.

    JAR boleh dibahagikan kepada dua kategori:

    • JAR tidak boleh laku. Semasa pembungkusan, anda tidak perlu menyatakan main-class dan ia tidak boleh dijalankan. Pakej balang biasa boleh digunakan oleh projek lain untuk bergantung.

    • JAR Boleh Laksana. Apabila membina pakej jar, jika kelas main-class ditentukan, anda boleh menggunakan perintah java -jar xxx.jar untuk melaksanakan kaedah main-class main untuk menjalankan pakej jar. Pakej balang boleh dijalankan tidak boleh dipercayai oleh projek lain.

    Struktur JAR

    Struktur pakej

    Sama ada JAR tidak berdaya maju atau JAR boleh laku, ia mengandungi dua bahagian selepas penyahmampatan: META-INF Direktori (metadata ) dan package direktori (kelas tersusun). Balang biasa ini tidak mengandungi pakej pergantungan pihak ketiga, tetapi hanya fail konfigurasi aplikasi sendiri, kelas, dsb.

    .
    ├── META-INF
    │   ├── MANIFEST.MF  #定义
    └── org  # 包路径(存放编译后的class)
        └── springframework
    Fail penerangan MANIFEST.MF

    Fail konfigurasi pakej JAR ialah fail META-INF dalam folder MANIFEST.MF. Maklumat konfigurasi utama adalah seperti berikut:

    • Versi Manifest: digunakan untuk menentukan versi fail manifes, contohnya: Versi Manifest: 1.0

    • Dicipta-Oleh: Isytiharkan penjana fail Secara amnya, atribut ini dijana oleh alat baris arahan jar, contohnya: Created-Oleh: Apache Ant 1.5.1

      <.>
    • Signature-Version: Tentukan versi tandatangan fail jar

    • Class-Path: Aplikasi atau pemuat kelas menggunakan nilai ini untuk membina carian kelas dalaman laluan, yang perlu ditetapkan dalam pakej balang boleh laku ini.

    Di atas ialah atribut

    pakej balang biasa Dalam fail .MF pakej balang boleh dijalankan , terdapat juga. atau mian-class dan atribut lain. Jika anda bergantung pada pakej balang luaran, laluan lib dan maklumat lain juga akan dikonfigurasikan dalam fail MF. start-classUntuk maklumat lanjutLihat: Bagaimana maven menambah kandungan pada fail MANIFEST.MF

    Bagi direktori

    pakej runnable jar dan pakej balang biasa Tiada corak tetap tertentu untuk struktur Secara ringkasnya, tidak kira apa strukturnya, selepas mengkonfigurasi maklumat pakej balang dalam fail .MF, anda boleh menggunakan pakej balang seperti biasa.

    Apakah perbezaan antara FatJar

    Apakah itu FatJar?

    Balang biasa hanya mengandungi maklumat tentang balang semasa dan tidak mengandungi balang pihak ketiga. Apabila secara dalaman bergantung pada balang pihak ketiga, ralat akan dilaporkan jika dijalankan secara langsung Dalam kes ini, balang pihak ketiga perlu dibenamkan ke dalam balang boleh laku.

    Letakkan balang dan balang pihak ketiga yang bergantung kepada satu pakej ini ialah FatJar.

    SpringBoot FatJar Solution

    Untuk menyelesaikan masalah balang terbenam, satu set penyelesaian FatJar disediakan, yang masing-masing mentakrifkan Spring Bootstruktur direktori balang dan . Atas dasar menyusun dan menjana balang boleh laku, gunakan MANIFEST.MF mengikut standard pakej boleh laku Spring Boot spring-boot-maven-plugin untuk mendapatkan balang Boot Spring boleh laku. repackageMengikut jenis balang boleh laku, ia terbahagi kepada dua jenis: Balang boleh laku dan perang boleh laku.

    proses pembungkusan spring-boot-maven-plugin
    Oleh kerana tiada pengenalan atau penulisan eksplisit kelas berkaitan di mana-mana sahaja dalam projek SpringBoot kosong yang baru dibuat. Malah, untuk setiap projek SpringBoot yang baru dibuat, anda boleh melihat pemalam berikut dalam fail pom.xmlnya:

    <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中。

    Atas ialah kandungan terperinci Apakah FatJar dan Jar dalam Springboot?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam