创建自包含的可执行JAR


当您的应用程序超出了十几行代码时,您可能应该将代码分成多个类。在这一点上,问题是如何分配它们。在Java中,经典格式是Java ARchive,也称为JAR。但是实际应用程序可能依赖于其他JAR。

这篇文章旨在描述创建独立的可执行JAR(也称为uber-JAR或胖JAR)的方法。

什么是可执行JAR?

JAR只是类文件的集合。为了可执行,其META-INF/MANIFEST.MF文件应指向实现该main()方法的类。您可以使用Main-Class属性来执行此操作。这是一个例子:

12.png

MainClass有一种static main(String... args)方法

处理类路径

大多数应用程序依赖现有代码。Java提供了classpath的概念。类路径是运行时将查找以查找依赖代码的路径元素的列表。当运行Java类,定义通过类路径中-cp的命令行选项:

11.png

Java运行时通过聚合来自所有引用的JAR的所有类并添加主类来创建类路径。

分发依赖于其他JAR的JAR时会出现新的问题:

  1. 您需要在相同版本中定义相同的库
  2. 更重要的是,该-cp参数不适用于JARs。要引用其他JAR,需要通过Class-Path属性在JAR清单中设置类路径:

10.png

  1. 因此,您需要根据清单将JAR放在目标文件系统上的相对或绝对位置。这意味着要打开JAR并先阅读清单。 解决这些问题的一种方法是创建一个唯一的部署单元,其中包含来自所有JAR的类,并且可以作为一个工件进行分发。有几种创建此类JAR的选项:

  2. Assembly插件

  3. The Assembly plugin
  4. Spring Boot插件(用于Spring Boot项目)

Apache Assembly插件

Maven的Assembly Plugin使开发人员能够将项目输出组合到一个可分发的存档中,该存档还包含依赖项,模块,站点文档和其他文件。

一个Maven设计规则是每个项目创建一个工件。有一些例外情况,例如Javadocs工件和源工件,但是通常,如果要多个工件,则需要为每个工件创建一个项目。Assembly插件背后的想法是解决此规则。

Assembly插件依赖于特定的assembly.xml配置文件。它允许您选择要包含在工件中的文件。请注意,最终的工件不必是JAR:配置文件可让您在可用格式(例如zip,war等)之间进行选择。

该插件通过提供预定义的程序集来管理常见的用例。自包含的JAR的分布就在其中。配置如下所示:

9.png

  1. 参考预定义的自包含JAR配置
  2. 设置要执行的主类
  3. 执行single目标
  4. 将目标绑定到package阶段,即在构建原始JAR之后

运行mvn package产生两个工件:

  1. <name>-<version>.jar
  2. <name>-<version>-with-dependencies.jar

第一个JAR的内容与没有该插件时创建的内容相同。第二个是独立的JAR。您可以像这样执行它:

8.png

根据项目的不同,它可能会成功执行...。例如,它在示例Spring Boot项目中失败,并显示以下消息:

7.png

原因是不同的JAR在同一路径下 提供不同的资源,例如 META-INF/spring.factories。该插件遵循最后写入胜出策略。订单基于JAR的名称。

使用Assembly,您可以排除资源,但不能合并它们。当您需要合并资源时,您可能需要使用Apache Shade插件。

Apache Shade插件

Assembly插件是通用的;Shade插件仅专注于创建独立的JAR的任务。

该插件提供了将工件打包(包括其依赖项)并遮蔽(即重命名)某些依赖项的包的功能。

该插件基于转换器的概念:每个转换器负责处理一种类型的资源。转换器可以按原样复制资源,添加静态内容,将其与其他资源合并等。

虽然您可以开发一个转换器,但是该插件提供了一组现成的转换器:

TRANSFORMER DESCRIPTION
ApacheLicenseResourceTransformer Prevents license duplication
ApacheNoticeResourceTransformer Prepares merged NOTICE
AppendingTransformer Adds content to a resource
ComponentsXmlResourceTransformer Aggregates Plexus components.xml
DontIncludeResourceTransformer Prevents inclusion of matching resources
GroovyResourceTransformer Merges Apache Groovy extends modules
IncludeResourceTransformer Adds files from the project
ManifestResourceTransformer Sets entries in the MANIFEST
PluginXmlResourceTransformer Aggregates Mavens plugin.xml
ResourceBundleAppendingTransformer Merges ResourceBundles
ServicesResourceTransformer Relocated class names in META-INF/services resources and merges them
XmlAppendingTransformer Adds XML content to an XML resource
PropertiesTransformer Merges properties files owning an ordinal to solve conflicts
OpenWebBeansPropertiesTransformer Merges Apache OpenWebBeans configuration files
MicroprofileConfigTransformer Merges conflicting Microprofile Config properties based on an ordinal

上面程序集的Shade插件配置如下:

6.png XML格式

默认情况下,shade目标已绑定到package阶段 该转换器专用于生成清单文件 设置Main-Class条目 将最终的JAR配置为多发行版JAR。当任何初始JAR是多发行版JAR时,这都是必需的 运行mvn package产生两个工件:

<name>-<version>.jar:自包含的可执行文件JAR original-<name>-<version>.jar:没有嵌入式依赖项的“普通” JAR 对于示例项目,最终的可执行文件仍然无法按预期工作。确实,在构建过程中有很多关于重复资源的警告。其中两个阻止示例项目正常工作。为了正确地合并它们,我们需要看一下它们的格式:

  • META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat:此Log4J2文件包含预编译的Log4J2插件数据。它以二进制格式编码,任何开箱即用的转换器都无法合并此类文件。然而,随便搜索发现有人已经遇到了这个问题,并发布了一个转换器来处理合并。
  • META-INF/spring.factories:这些特定于Spring的文件具有单键/多值格式。虽然它们是基于文本的,但没有开箱即用的转换器可以正确合并它们。但是,Spring开发人员在其插件中提供了此功能(以及更多)。 要配置这些转换器,我们需要将以上库作为依赖项添加到Shade插件中:

4.png 5.png

  1. 合并Log4J2.dat文件
  2. 合并/META-INF/spring.factories档案
  3. 添加所需的变压器代码

此配置有效!仍然有剩余警告:

  1. Manifests
  2. 许可,声明和类似文件
  3. Spring Boot specific文件,即spring.handlers,spring.schemas与spring.tooling
  4. Spring Boot-Kotlin 定文件例如spring-boot.kotlin_module,spring-context.kotlin_module等等。
  5. 服务加载程序配置文件
  6. JSON文件

Spring Boot插件

Spring Boot插件采用了完全不同的方法。它不会单独合并来自JAR的资源。它增加了相关的JAR ,因为它们是尤伯杯JAR内。为了加载类和资源,它提供了一种特定的类加载机制。显然,它专用于Spring Boot项目。

配置Spring Boot插件很简单:

3.png

让我们检查一下最终JAR的结构:

2.png

  1. Project compiled classes
  2. JAR dependencies
  3. Spring Boot class-loading classes

这是我们的示例项目清单的摘录:

1.png

如您所见,主类是特定于Spring Boot的类,而“真实”主类在另一个条目下被引用。

有关JAR结构的更多信息,请查看参考文档。

结论 在这篇文章中,我们描述了三种创建独立的可执行JAR的方法:

组装非常适合简单的项目 当项目开始变得更加复杂并且您需要处理重复的文件时,请使用Shade 最后,对于Spring Boot项目,最好的选择是专用插件


原文链接:http://codingdict.com