基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

Spring Boot 打出的可执行 JAR 包和普通 JAR 包有什么区别?

知识点图片

Spring Boot 打出的可执行 JAR 包(Executable JAR)(通常被称为 Fat JAR 或 Uber JAR)与普通的 Java 普通 JAR 包(Plain JAR)在结构、运行机制和依赖管理上有显著的区别。

以下是核心区别的详细对比:

1. 内部目录结构不同

这是最直观的区别。如果你解压这两个 JAR 包,你会看到完全不同的结构。

  • 普通 JAR 包

    • 结构扁平。
    • 直接包含编译后的 .class 文件(按包路径存放)。
    • 通常不包含依赖的第三方库(如 Spring Framework、Log4j 等)。
    • 结构示例:
      plaintext
      my-app.jar
      ├── META-INF
      │   └── MANIFEST.MF
      └── com
          └── example
              └── MyApplication.class
  • Spring Boot 可执行 JAR 包

    • 结构嵌套。
    • BOOT-INF/classes:存放你自己编写的代码编译后的 .class 文件。
    • BOOT-INF/lib:存放项目依赖的所有第三方 JAR 包(包括内嵌的 Tomcat/Jetty)。
    • org/springframework/boot/loader:存放 Spring Boot 的类加载器代码(这是它能直接运行的关键)。
    • 结构示例:
      plaintext
      my-boot-app.jar
      ├── META-INF
      │   └── MANIFEST.MF
      ├── org
      │   └── springframework
      │       └── boot
      │           └── loader ... (Spring Boot 的启动加载器)
      └── BOOT-INF
          ├── classes
          │   └── com/example/MyApplication.class (你的代码)
          └── lib
              ├── spring-web.jar
              ├── tomcat-embed-core.jar
              └── ... (所有依赖)

2. MANIFEST.MF 文件(入口配置)不同

JAR 包中的 META-INF/MANIFEST.MF 文件定义了 JAR 的元数据和入口点。

  • 普通 JAR 包

    • Main-Class 属性直接指向你编写的主类(包含 public static void main 的类)。
    • 示例: Main-Class: com.example.MyApplication
  • Spring Boot 可执行 JAR 包

    • Main-Class:指向 Spring Boot 提供的特殊启动类(通常是 JarLauncher)。这是因为 Java 默认的类加载器无法加载 JAR 包内部嵌套的 JAR 包,必须先运行 Spring Boot 的加载器。
    • Start-Class:这是一个自定义属性,指向你编写的业务主类。
    • 示例:
      plaintext
      Main-Class: org.springframework.boot.loader.JarLauncher
      Start-Class: com.example.MyApplication

3. 类加载机制(ClassLoader)不同

  • 普通 JAR 包

    • 使用 JDK 默认的类加载机制。
    • 运行时,必须在 CLASSPATH 环境变量或命令行参数中显式指定所有依赖的 JAR 路径。
  • Spring Boot 可执行 JAR 包

    • 使用 Spring Boot 自定义的类加载器(LaunchedURLClassLoader)。
    • 当执行 java -jar 时,先启动 JarLauncher,它会创建一个自定义的 ClassLoader,这个 Loader 知道如何去读取 BOOT-INF/lib/ 下的嵌套 JAR,以及 BOOT-INF/classes/ 下的业务代码,从而实现“自包含”运行。

4. 运行方式不同

  • 普通 JAR 包

    • 不能直接独立运行(除非它不依赖任何第三方库)。
    • 通常需要编写复杂的启动脚本来拼接 Classpath。
    • 命令: java -cp lib/*:my-app.jar com.example.MyApplication
  • Spring Boot 可执行 JAR 包

    • 开箱即用。因为它包含了内嵌的 Web 容器(如 Tomcat)和所有依赖。
    • 命令: java -jar my-boot-app.jar

5. 用途不同

  • 普通 JAR 包

    • 通常作为依赖库(Library)被其他项目引用。
    • 或者用于传统的部署模式(部署到外部安装好的 Tomcat/WebLogic 中,此时通常打成 WAR 包,但也可以是普通 JAR 配合外部 lib)。
  • Spring Boot 可执行 JAR 包

    • 用于微服务部署或独立应用程序。
    • 注意:Spring Boot 的可执行 JAR 包不能直接作为依赖被其他项目引用(即不能在其他项目的 pom.xml 中依赖它)。因为它的类都在 BOOT-INF 目录下,外部项目无法直接访问。
    • 补充: 如果你既想让它能运行,又想让它能被依赖,需要在 Maven 插件配置中开启 classifier 参数生成两个 JAR(一个 exec,一个 plain)。

总结对比表

特性 普通 JAR 包 (Plain) Spring Boot 可执行 JAR (Executable)
依赖包含 仅包含项目自身代码 包含自身代码 + 所有第三方依赖 (Fat JAR)
目录结构 扁平 (com/example/...) 嵌套 (BOOT-INF/classes, BOOT-INF/lib)
Main-Class 指向你的主类 指向 Spring Boot 的 JarLauncher
运行命令 java -cp ... MainClass java -jar app.jar
能否被依赖 可以 默认不可以 (类路径不可见)
核心原理 标准 Java 类加载 自定义 ClassLoader 加载嵌套 JAR
00:00
00:00