Rumah >Java >javaTutorial >Apakah FatJar dan Jar dalam Springboot?
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.
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.
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 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
<.>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-class
Untuk maklumat lanjutLihat: Bagaimana maven menambah kandungan pada fail MANIFEST.MF
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 FatJarApakah 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 SolutionUntuk menyelesaikan masalah balang terbenam, satu set penyelesaian FatJar disediakan, yang masing-masing mentakrifkan Spring Boot
struktur 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. repackage
Mengikut jenis balang boleh laku, ia terbahagi kepada dua jenis: Balang boleh laku dan perang boleh laku.
<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.jar
和xxx.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 -classpath
,java -cp
,java -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!