当前位置:

首页 > 编程开发 > Springboot中FatJar和Jar是什么

Springboot中FatJar和Jar是什么

导读SpringBoot应用可以使用spring-boot-maven-plugin快速打包,构建一个可执行jar。SpringBoot内嵌容器,通过java-jar命令便可以直接启动应用。虽然是一个简单的启动命令,背后却藏着很多知识。今天带着大家探索FATJAR启动的背后原理。本文主要包含以下几个部分:JAR是什么。首先需要了解jar是什么,才知道java-jar做了什么事情。FatJar有什么不同。SpringBoot提供的可执行jar与普通的jar有什么区别。启动时的类加载原理。启动过程中类加载器做

本文目录

      导读

      Spring Boot应用可以使用spring-boot-maven-plugin快速打包,构建一个可执行jar。Spring Boot内嵌容器,通过java -jar命令便可以直接启动应用。

      虽然是一个简单的启动命令,背后却藏着很多知识。今天带着大家探索FAT JAR启动的背后原理。本文主要包含以下几个部分:

      • JAR 是什么。首先需要了解jar是什么,才知道java -jar做了什么事情。

      • FatJar 有什么不同。 Spring Boot提供的可执行jar与普通的jar有什么区别。

      • 启动时的类加载原理。 启动过程中类加载器做了什么?Spring Boot又如何通过自定义类加载器解决内嵌包的加载问题。

      • 启动的整个流程。最后整合前面三部分的内容,解析源码看如何完成启动。

      JAR 是什么

      JAR简介

      JAR文件(Java归档,英语: Java ARchive)是一种软件包文件格式,通常用于将大量的Java类文件、相关的元数据和资源(文本、图片等)文件聚合到一个文件,以便分发Java平台应用软件或库。简单点理解其实就是一个压缩包,既然是压缩包那么为了提取JAR文件的内容,可以使用任何标准的unzip解压缩软件提取内容。或者使用Java虚拟机自带命令jar -xf foo.jar来解压相应的jar文件。

      JAR 可以简单分为两类:

      • 非可执行JAR。打包时,不用指定main-class,也不可运行。普通jar包可以供其它项目进行依赖。

      • 可执行JAR。打jar包时,指定了main-class类,可以通过java -jar xxx.jar命令,执行main-class的main方法,运行jar包。可运行jar包不可被其他项目进行依赖。

      JAR结构

      包结构

      不管是非可行JAR还是可执行JAR解压后都包含两部分:META-INF目录(元数据)和package目录(编译后的class)。这种普通的jar不包含第三方依赖包,只包含应用自身的配置文件、class 等。

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

      JAR包的配置文件是META-INF文件夹下的MANIFEST.MF文件。主要配置信息如下:

      • Manifest-Version: 用来定义manifest文件的版本,例如:Manifest-Version: 1.0

      • Created-By: 声明该文件的生成者,一般该属性是由jar命令行工具生成的,例如:Created-By: Apache Ant 1.5.1

      • Signature-Version: 定义jar文件的签名版本

      • Class-Path: 应用程序或者类装载器使用该值来构建内部的类搜索路径,可执行jar包里需要设置这个。

      上面是普通jar包的属性,可运行jar包的.MF文件中,还会有mian-class或start-class等属性。如果依赖了外部jar包,还会在MF文件中配置lib路径等信息。更多信息参见:maven为MANIFEST.MF文件添加内容的方法

      至于可运行jar包和普通jar包的目录结构,没有什么特别固定的模式,总之,无论是什么结构,在.MF文件中,配置好jar包的信息,即可正常使用jar包了。

      FatJar有什么不同

      什么是FatJar?

      普通的jar只包含当前 jar的信息,不含有第三方 jar。当内部依赖第三方jar时,直接运行则会报错,这时候需要将第三方jar内嵌到可执行jar里。将一个jar及其依赖的三方jar全部打到一个包中,这个包即为 FatJar。

      SpringBoot FatJar解决方案

      Spring Boot为了解决内嵌jar问题,提供了一套FatJar解决方案,分别定义了jar目录结构和 MANIFEST.MF。在编译生成可执行 jar 的基础上,使用spring-boot-maven-plugin按Spring Boot 的可执行包标准repackage,得到可执行的Spring Boot jar。根据可执行jar类型,分为两种:可执行Jar和可执行war。

      spring-boot-maven-plugin打包过程

      因为在新建的空的 SpringBoot 工程中并没有任何地方显示的引入或者编写相关的类。实际上,对于每个新建的 SpringBoot 工程,可以在其 pom.xml 文件中看到如下插件:

      
          
              
                  org.springframework.boot
                  spring-boot-maven-plugin
              
          
      

      这个是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 引入下面的配置:

      
          org.springframework.boot
          spring-boot-loader
      

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

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

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

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

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

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

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发
      相关文章 更多
      通过实战案例学习如何高效使用httpclient.jar
      通过实战案例学习如何高效使用httpclient.jar

      ApacheHttpClient是处理HTTP通信的强大工具库,能简化网络操作并提升效率。首先需配置HttpClient实例,设置超时和连接池以优化性能。通过各类请求对象发送GET或POST等请求,并正确处理响应状态码和响应体,注意使用try-with-resources管理资源。高级功能包括连接池管理、重试机制和拦截器,有助于增强健壮性。实践中应复用客户端

      NIO文件系统异常分析与解决方法
      NIO文件系统异常分析与解决方法

      在使用nio加载文件时,在idea中运行没有问题,但打成jar包后在windows和linux下都有问题:代码语言:javascript代码运行次数:0运行复制publicstaticvoidmain(String[]args)throwsException{//SpringApplication.run(MarketCollectApplication.class,args);URIuri=MarketCollectApplication.cla

      springBoot项目常用目录有哪些
      springBoot项目常用目录有哪些

      springBoot项目常用目录springBoot项目的目录结构及名规范介绍基于SpringBoot开发时的目录结构及命名规范,通过介绍能够为你解决,在实际项目中如何规划目录结构?如何命名目录更规范?各个目录分别代表什么含义?等三个问题。目录说明servicex//项目名|-admin-ui//管理服务前端代码(一般将UI和SERVICE放到一个工程中,便于管理)|-servicex-auth//模块1|-servicex-common//模块2|-servicex-gateway//模块3|-serv

      在 Linux 系统中如何执行 Java 可执行文件
      在 Linux 系统中如何执行 Java 可执行文件

      运行JAR文件的先决条件在Linux系统上运行JAR文件需要安装Java运行时环境(JRE),它是执行Java应用程序所需的基本组件,包括Java虚拟机(JVM)、核心类库等。许多主流Linux发行版,如Ubuntu、Debian、Fedora、openSUSE等,都提供了JRE包的软件库,方便用户进行安装。后文将详细介绍在流行的发行版上安装JRE的步骤。设置完JRE后,可以根据个人喜好选择使用命令行终端或图形用户界面来启动JAR文件。您的选择可能取决于对Linuxshell的熟悉程度和个人偏好。在Lin

      Maven实践技巧:简便导入Jar包的实用方法
      Maven实践技巧:简便导入Jar包的实用方法

      Maven实战指南:轻松导入Jar包的实用技巧Maven是Java项目管理工具中使用广泛的一个工具,通过它可以有效管理项目的构建、依赖等。在Java开发中,经常会遇到需要导入第三方Jar包的情况,而Maven可以帮助我们高效地管理这些依赖。本文将介绍几种实用的技巧,帮助读者轻松导入Jar包,并附带具体的代码示例。技巧一:在pom.xml文件中添加依赖在Mav

      解析SpringBoot和SpringMVC之间的异同
      解析SpringBoot和SpringMVC之间的异同

      解析SpringBoot和SpringMVC之间的异同SpringBoot和SpringMVC是Java领域中非常重要的开发框架。虽然它们都属于Spring框架的一部分,但是在使用和功能上有一些明显的区别。本文将对SpringBoot和SpringMVC进行比较,解析它们之间的异同。首先,让我们来了解一下SpringBoot。SpringBo

      探索SpringCloud和SpringBoot在微服务领域的不同应用方式
      探索SpringCloud和SpringBoot在微服务领域的不同应用方式

      近年来,随着云计算和分布式架构的兴起,微服务架构的应用越来越广泛。而SpringCloud和SpringBoot作为Java开发中的两个重要框架,对于微服务的实现起到了重要的作用。然而,很多人对于它们在微服务领域的不同应用方式还存在一定的疑惑。本文将从不同的角度探索SpringCloud和SpringBoot在微服务中的应用方式。首先,我们来了解一下Spri

      SpringBoot和SpringMVC有何区别?
      SpringBoot和SpringMVC有何区别?

      SpringBoot与SpringMVC的不同之处在哪里?SpringBoot和SpringMVC是两个非常流行的Java开发框架,用于构建Web应用程序。尽管它们经常分别被使用,但它们之间的不同之处也是很明显的。首先,SpringBoot可以被看作是一个Spring框架的扩展或者增强版。它旨在简化Spring应用程序的初始化和配置过程,以帮助开发人

      比较SpringBoot和SpringMVC的异同
      比较SpringBoot和SpringMVC的异同

      SpringBoot和SpringMVC都是Java开发中常用的框架,但它们之间有一些明显的差异。本文将探究这两个框架的特点和用途,并对它们的差异进行比较。首先,我们来了解一下SpringBoot。SpringBoot是由Pivotal团队开发的,它旨在简化基于Spring框架的应用程序的创建和部署。它提供了一种快速、轻量级的方式来构建独立的、可执行

      SpringBoot与SpringMVC的不同点是什么?
      SpringBoot与SpringMVC的不同点是什么?

      SpringBoot和SpringMVC是Java开发中常用的两个框架,它们都是由Spring框架所提供的,但在功能和使用方式上有着一些区别。本文将分别介绍SpringBoot和SpringMVC的特点和区别。一、SpringBoot的特点:简化配置:SpringBoot通过约定优于配置的原则,大大简化了项目的配置过程。它可以自动配置项目所需要的参数,开发人

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。