深入探讨:Spring Boot Fat JAR 的利与弊

两万字长文,辩证看待Spring Boot Fat JAR,结尾有惊喜

本文分析了Spring Boot Fat JAR在项目中的得与失,并面向读者提供了从入门到精通的优缺点总结


前言:Spring Boot Fat JAR 简介

在软件开发和部署的领域中,简化和效率是至关重要的。Spring Boot,作为一款流行的Java框架,提供了许多创新机制来简化开发过程。其中,“fat JAR” 技术是Spring Boot生态系统中的一个亮点,它彻底改变了我们打包和部署应用的方式。

“Fat JAR”,字面意思是"胖 JAR",是Spring Boot应用打包的一种方式,它将所有必需的依赖项和库打包到一个可执行的JAR文件中。这个"胖"的称谓,源自于它包含丰富的内容,让开发者摆脱了管理众多依赖项的繁琐。

在传统的Java应用中,部署通常需要处理多个JAR文件和库,并确保它们正确地放置在类路径上。而Spring Boot的"fat JAR"则不同,它将所有依赖项整合到一个文件中,实现了"一站式"部署。这个单一的JAR文件包含了整个应用的所有必要组件,从核心的Spring框架到第三方库,再到你的应用代码,一应俱全。

使用"fat JAR"的优势显而易见:

  • 简化部署:只需一个JAR文件,即可轻松部署应用,无需担心依赖项的管理。
  • 便携性:所有依赖项都包含在JAR中,使得应用易于在不同环境中运行,无需额外配置。
  • 快速启动:内置的启动脚本使得应用启动变得简单,只需一条命令即可运行。
  • 版本控制:所有依赖项的版本都固定在JAR中,避免了版本冲突的问题。

本文档将深入探讨Spring Boot “fat JAR” 的打包过程、其背后的机制,以及如何充分利用这一技术来优化你的应用部署。通过阅读本文,你将了解如何创建自己的"fat JAR",并体验其带来的便利和效率提升。让我们一起探索Spring Boot生态系统中的这一强大功能吧!


初识Springboot FatJar

  1. Fat JAR 的目录结构
example-app.jar
├── META-INF
│   ├── MANIFEST.MF                        # JAR 清单文件
│   └── maven/
├── BOOT-INF
│   ├── classes/                           # 项目编译后的类文件
│   │   ├── com/example/
│   │   └── application.properties
│   └── lib/                               # 依赖的 JAR 包
│       ├── spring-boot-3.1.0.jar
│       ├── spring-core-5.7.6.jar
│       └── ...
└── org/springframework/boot/loader/       # Spring Boot Loader 类
    ├── JarLauncher.class                 # JAR 启动器
    ├── LaunchedURLClassLoader.class      # 自定义类加载器
    └── ...
  1. MANIFEST.MF 文件内容
    MANIFEST.MF 文件是 JAR 文件的清单文件,它包含了 JAR 文件的元数据信息,例如版本号、主类、类路径等等。一个典型的 Spring Boot fat JAR 的 MANIFEST.MF 文件内容如下:
Manifest-Version: 1.0
Implementation-Title: your-application-name
Implementation-Version: your-application-version
Start-Class: org.springframework.boot.loader.JarLauncher
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Main-Class: org.springframework.boot.loader.PropertiesLauncher
Spring-Boot-Version: your-spring-boot-version
Created-By: Apache Maven {maven-version}
Build-Jdk-Spec: {java-version}

让我们逐行解释一下这些字段的含义:

  • Manifest-Version: 清单文件的版本号,通常是 1.0
  • Implementation-Title: 应用的名称。
  • Implementation-Version: 应用的版本号。
  • Start-Class: Spring Boot fat JAR 的入口点,通常是 org.springframework.boot.loader.JarLauncher。这是 Spring Boot 特有的类加载器,用于加载嵌入式 JAR 中的类。
  • Spring-Boot-Classes: 应用的类文件所在路径,通常是 BOOT-INF/classes/
  • Spring-Boot-Lib: 应用的依赖库所在路径,通常是 BOOT-INF/lib/
  • Main-Class: 应用的主类,即包含 main 方法的类。 对于 Spring Boot 应用,该值为 org.springframework.boot.loader.PropertiesLauncher,它会读取项目中的application.propertiesapplication.yml 配置文件,并最终启动 Start-Class 中定义的 JarLauncher
  • Spring-Boot-Version: 使用的 Spring Boot 版本。
  • Created-By: 创建 JAR 文件的工具,例如 Apache Maven {maven-version}
  • Build-Jdk-Spec: 构建 JAR 文件使用的 JDK 版本。

关键字段说明:

  • Start-Class 和 Main-Class 的区别: 这是理解 Spring Boot fat JAR 执行机制的关键。Main-ClassPropertiesLauncher,它负责加载外部配置文件,并最终将控制权交给 Start-Class JarLauncherJarLauncher 是 Spring Boot 特有的类加载器,它能够从嵌入式的 JAR 文件中加载应用的类和依赖库。 这种机制使得 Spring Boot 应用可以独立运行,无需依赖外部容器。

  • BOOT-INF/classes 和 BOOT-INF/lib: 这两个目录是 Spring Boot fat JAR 特有的结构。BOOT-INF/classes 目录包含应用的类文件,BOOT-INF/lib 目录包含应用的依赖库。 这种结构能够防止应用的类和依赖库与其他 JAR 文件冲突。

自定义 MANIFEST.MF:

可以通过 Maven 或 Gradle 的插件来定制 MANIFEST.MF 文件的内容,例如添加自定义属性或修改现有属性的值。

了解 MANIFEST.MF 文件的内容对于理解 Spring Boot fat JAR 的运行机制至关重要。 通过分析该文件,可以了解应用的版本、依赖、入口点等关键信息。
3. 启动原理

JarLauncher 作为 Spring Boot fat JAR 的入口点,扮演着至关重要的角色。 它的主要作用是创建一个自定义的类加载器,用于加载 fat JAR 中的应用程序类和依赖库。 让我们更详细地了解一下它的工作机制:

JarLauncher 的作用:

  1. 创建 LaunchedURLClassLoader: JarLauncher 的核心功能是创建 LaunchedURLClassLoader。这是一个自定义的类加载器,它能够从 fat JAR 内部的 BOOT-INF/classesBOOT-INF/lib 目录加载应用程序类和依赖库。

  2. 设置线程上下文类加载器: JarLauncher 会将创建的 LaunchedURLClassLoader 设置为当前线程的上下文类加载器。 这确保了应用程序代码和依赖库能够被正确加载和访问。

  3. 启动应用程序主类: JarLauncher 会使用 LaunchedURLClassLoader 加载应用程序的主类(由 Main-Class 属性指定,通常是 org.springframework.boot.loader.PropertiesLauncher),并调用其 main 方法,从而启动应用程序。

JarLauncher 的优势:

  • 隔离应用程序依赖: 通过自定义类加载器,JarLauncher 将应用程序的依赖库与运行环境隔离开来,避免了依赖冲突。 这意味着 fat JAR 可以在任何环境中运行,无需担心与已安装的库发生冲突。

  • 简化部署: 由于所有依赖都打包在 fat JAR 中,部署应用程序变得非常简单,只需将 fat JAR 复制到目标环境即可运行,无需配置额外的依赖。

  • 支持嵌入式 Web 服务器: JarLauncher 支持在 fat JAR 中嵌入 Web 服务器(例如 Tomcat、Jetty 或 Undertow),使得可以直接运行 Web 应用程序,无需外部 Web 服务器。

JarLauncher 的类型:

Spring Boot 提供了三种类型的 JarLauncher

  • JarLauncher: 这是最基本的 JarLauncher,用于从 JAR 文件启动应用程序。

  • WarLauncher: 用于从 WAR 文件启动应用程序,主要用于部署到传统的 Servlet 容器中。

  • PropertiesLauncher: 这是 Spring Boot 默认使用的 JarLauncher。 它会在启动前读取应用程序的配置文件(application.propertiesapplication.yml),并将配置信息传递给应用程序。

JarLauncherPropertiesLauncher 的关系:

在 Spring Boot fat JAR 中,MANIFEST.MF 文件中的 Main-Class 属性通常指定为 PropertiesLauncherPropertiesLauncher 会首先加载应用程序的配置文件,然后启动 JarLauncher 来加载应用程序类和依赖库。 这种机制允许 Spring Boot 应用程序根据不同的配置文件进行不同的配置。

总而言之,JarLauncher 是 Spring Boot fat JAR 能够独立运行的关键组件。它通过自定义类加载器隔离应用程序依赖,简化了部署流程,并支持嵌入式 Web 服务器。 理解 JarLauncher 的工作机制对于开发和部署 Spring Boot 应用程序至关重要。

// 1. JarLauncher 作为入口类
public class JarLauncher extends ExecutableArchiveLauncher {
   
    public static void main(String[] args) throws Exception {
   
        new JarLauncher().launch(args);
    }
}

// 2. 自定义类加载器
public class LaunchedURLClassLoader extends URLClassLoader {
   
    @Override
    protected Class<?> loadClass(String name, boolean resolve) 
        throws ClassNotFoundException {
   
        // 优先从 BOOT-INF/classes 加载
        // 然后从 BOOT-INF/lib 中的 JAR 加载
        // 最后委托给父类加载器
    }
}

// 3. 启动流程
class ExecutableArchiveLauncher {
   
    protected void launch(String[] args) throws Exception {
   
        // 1. 注册 URL 协议处理器
        JarFile.registerUrlProtocolHandler();
        
        // 2. 创建类加载器
        ClassLoader classLoader = createClassLoader(getClassPathArchives());
        
        // 3. 加载启动类
        launch(args, getMainClass(), classLoader);
    }
}
  1. Maven 打包配置
    在 Maven 中打包 Spring Boot fat JAR,需要使用 spring-boot-maven-plugin` 插件。 以下是几种常见的配置方式,以及它们的区别和适用场景:

1. 默认配置 (最常用):

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

这是最简单的配置方式,无需任何额外的配置。插件会自动检测 Spring Boot 应用程序,并将其打包成可执行的 fat JAR。

2. 指定主类:

如果你的项目结构比较复杂,或者有多个带有 main 方法的类,你可以显式指定主类:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>com.example.YourMainClass</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>

com.example.YourMainClass 替换为你实际的主类名。

3. 自定义 MANIFEST.MF 文件:

你可以通过配置 spring-boot-maven-plugin 来定制 MANIFEST.MF 文件的内容,例如添加自定义属性:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <manifest>
                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                    <addDefaultSpecificationEntries>true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值