附录 E.可执行 jar 格式

spring-boot-loader模块允许 Spring Boot 支持可执行的 jar 和 war 文件。如果您使用的是 Maven 或 Gradle 插件,则会自动生成可执行 jar,通常无需了解其工作方式的详细信息。

如果您需要从其他构建系统创建可执行 jar,或者您只是对基础技术感到好奇,本节将提供一些背景知识。

E.1 嵌套 JAR

Java 没有提供任何标准方法来加载嵌套的 jar 文件(即 jar 中本身包含的 jar 文件)。如果您希望分发一个自包含的应用程序,而该应用程序可以从命令行运行而无需解压缩,则可能会出现问题。

为了解决这个问题,许多开发人员使用“shade”Jar子。shade Jar子将所有Jar子中的所有类打包到单个“超级Jar子”中。带 shade 的 jar 的问题在于,很难看到您的应用程序中实际使用了哪些库。如果在多个 jar 中使用了相同的文件名(但内容不同),也可能会产生问题。 Spring Boot 采用了不同的方法,并允许您实际直接嵌套 jar。

E.1.1 可执行 jar 文件结构

与 Spring Boot Loader 兼容的 jar 文件的结构应采用以下方式:

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-BOOT-INF
    +-classes
    |  +-mycompany
    |     +-project
    |        +-YourClasses.class
    +-lib
       +-dependency1.jar
       +-dependency2.jar

应用程序类应放在嵌套的BOOT-INF/classes目录中。依赖项应放在嵌套的BOOT-INF/lib目录中。

E.1.2 可执行 war 文件结构

与 Spring Boot Loader 兼容的 war 文件的结构应采用以下方式:

example.war
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
 |     +-boot
 |        +-loader
 |           +-<spring boot loader classes>
 +-WEB-INF
    +-classes
    |  +-com
    |     +-mycompany
    |        +-project
    |           +-YourClasses.class
    +-lib
    |  +-dependency1.jar
    |  +-dependency2.jar
    +-lib-provided
       +-servlet-api.jar
       +-dependency3.jar

依赖项应放在嵌套的WEB-INF/lib目录中。在运行嵌入式程序时需要但在部署到传统 Web 容器时不需要的任何依赖项都应放在WEB-INF/lib-provided中。

E.2 Spring Boot 的“ JarFile”类

用于支持加载嵌套 jar 的核心类是org.springframework.boot.loader.jar.JarFile。它允许您从标准 jar 文件或嵌套的子 jar 数据加载 jar 内容。首次加载时,每个JarEntry的位置都 Map 到外部 jar 的物理文件偏移:

myapp.jar
+-------------------+-------------------------+
| /BOOT-INF/classes | /BOOT-INF/lib/mylib.jar |
|+-----------------+||+-----------+----------+|
||     A.class      |||  B.class  |  C.class ||
|+-----------------+||+-----------+----------+|
+-------------------+-------------------------+
 ^                    ^           ^
 0063                 3452        3980

上面的示例显示了如何在myapp.jar位置0063/BOOT-INF/classes中找到A.class。嵌套Jar中的B.class实际上可以在myapp.jar位置3452中找到,而C.class在位置3980中。

有了这些信息,我们可以通过简单地查找外部 jar 的适当部分来加载特定的嵌套条目。我们不需要解压缩归档文件,也不需要将所有条目数据读入内存。

E.2.1 与标准 Java“ JarFile”的兼容性

Spring Boot Loader 努力保持与现有代码和库的兼容性。 org.springframework.boot.loader.jar.JarFilejava.util.jar.JarFile的扩展,应作为替代产品。 getURL()方法将返回URL,该URL将打开java.net.JarURLConnection兼容的连接,并且可以与 Java 的URLClassLoader一起使用。

E.3 启动可执行 jar

org.springframework.boot.loader.Launcher类是特殊的引导程序类,用作可执行 jar 的主要入口点。它是 jar 文件中实际的Main-Class,用于设置适当的URLClassLoader并最终调用main()方法。

有 3 个启动器子类(JarLauncherWarLauncherPropertiesLauncher)。它们的目的是从目录中的嵌套 jar 文件或 war 文件(而不是在 Classpath 中显式)加载资源(.class文件等)。在JarLauncherWarLauncher的情况下,嵌套路径是固定的。 JarLauncher出现在BOOT-INF/lib/中,WarLauncher出现在WEB-INF/lib/WEB-INF/lib-provided/中,因此,如果需要,您只需在这些位置添加额外的 jar。默认情况下,PropertiesLauncher在应用程序归档文件中的BOOT-INF/lib/中查找,但是您可以通过在loader.properties(目录,归档文件或归档文件中的目录的逗号分隔列表)中设置环境变量LOADER_PATHloader.path来添加其他位置。

E.3.1 启动器清单

您需要指定一个适当的Launcher作为META-INF/MANIFEST.MFMain-Class属性。您应该在Start-Class属性中指定要启动的实际类(即,您编写的包含main方法的类)。

例如,以下是可执行 jar 文件的典型MANIFEST.MF

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

对于 War 文件,它将是:

Main-Class: org.springframework.boot.loader.WarLauncher
Start-Class: com.mycompany.project.MyApplication

Note

您无需在清单文件中指定Class-Path条目,Classpath 将从嵌套的 jar 中推导出。

E.3.2Exploded archives

某些 PaaS 实施可能选择在运行之前解压缩存档。例如,Cloud Foundry 以这种方式运行。您可以通过简单地启动适当的启动器来运行解压缩的存档:

$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher

E.4 属性启动器功能

PropertiesLauncher具有一些可以通过外部属性(系统属性,环境变量,清单条目或loader.properties)启用的特殊功能。

Note

PropertiesLauncher支持从loader.properties以及application.properties(出于历史原因)加载属性。我们建议仅使用loader.properties,因为对application.properties的支持已弃用,将来可能会删除。

Key Purpose
loader.path 以逗号分隔的 Classpath,例如lib,${HOME}/app/lib。较早的条目优先,就像javac命令行上的常规-classpath一样。
loader.home 用于解析loader.path中的相对路径。例如。 loader.path=lib,然后${loader.home}/lib是 Classpath 位置(以及该目录中的所有 jar 文件)。也用于查找loader.properties文件。示例/opt/app(默认为${user.dir})。
loader.args main 方法的默认参数(以空格分隔)
loader.main 要启动的主要类别的名称,例如com.app.Application
loader.config.name 属性文件的名称,例如launcher(默认为loader)。
loader.config.location 属性文件的路径,例如classpath:loader.properties(默认为loader.properties)。
loader.system Boolean 标志,指示应将所有属性添加到系统属性(默认为false)

当指定为环境变量或清单条目时,应使用以下名称:

Key Manifest entry Environment variable
loader.path Loader-Path LOADER_PATH
loader.home Loader-Home LOADER_HOME
loader.args Loader-Args LOADER_ARGS
loader.main Start-Class LOADER_MAIN
loader.config.location Loader-Config-Location LOADER_CONFIG_LOCATION
loader.system Loader-System LOADER_SYSTEM

Tip

构建胖Jar时,构建插件会自动将Main-Class属性移动到Start-Class。如果使用的是,请使用Main-Class属性指定要启动的类的名称,而忽略Start-Class

E.5 可执行 jar 限制

使用 Spring Boot Loader 打包的应用程序时,需要考虑许多限制。

E.5.1Zip 压缩

嵌套 jar 的ZipEntry必须使用ZipEntry.STORED方法保存。这是必需的,以便我们可以直接在嵌套 jar 中查找单个内容。嵌套 jar 文件本身的内容仍然可以压缩,外部 jar 中的任何其他条目也可以压缩。

E.5.2 系统 ClassLoader

加载类时,启动的应用程序应使用Thread.getContextClassLoader()(默认情况下,大多数库和框架都将使用Thread.getContextClassLoader())。尝试通过ClassLoader.getSystemClassLoader()加载嵌套 jar 类将失败。请注意java.util.Logging始终使用系统类加载器,因此,您应考虑使用其他日志记录实现。

E.6 替代独立 Jar 包解决方案

如果以上限制意味着您不能使用 Spring Boot Loader,则可以考虑以下替代方法:

上一章 首页 下一章