关于使用maven编译可执行代码

简单介绍三个maven插件:Exec Maven PluginApache Maven Assembly PluginApache Maven Shade Plugin

一、Exec Maven Plugin

这个插件有两个goal,exec:javaexec:exec, 这二者差异表现在以下两个方面:

  1. VM进程的差别 exec:java,是在同一个VM中执行mvn命令和java程序,即它们在同个进程中;而exec:exec会创建一个新的VM去运行java程序,因此它可以配置单独的VM参数
  2. configuration参数不同 注意,二者的configuration必须写在<plugin>根结点之下,否则不认识。exec:java的配置更简单易用,exec:exec的配置更原始(因此也更强大)

exec:java的基本配置
这里指定了main入口和额外的classpath,这里的classpath的分隔符使用的是逗号”,”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<!--程序入口-->
<mainClass>test.child1.Main</mainClass>
<!--额外的classpath-->
<additionalClasspathElements>
${project.basedir}/target/conf,${project.basedir}/../conf
</additionalClasspathElements>
</configuration>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
</plugin>

exec:exec的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<!-- 使用java命令来执行应用程序 -->
<executable>java</executable>
<arguments> 
<!-- 设置java命令后面附带的参数 -->
<argument>-classpath</argument>
  <classpath />
  <!-- 插件将收集所有的依赖库和编译好的class文件, 也可以指定使用哪些依赖,如果依赖太多,这样做太费事。根据java命令的规则,classpath必须放在程序入口之前 -->
  <!-- 程序入口 -->
<argument>test.child1.Main</argument>
<argument>-Dkey=value</argument>
</arguments>
</configuration>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>

从这个配置可以明显的看出,它会根据配置组合出一条类似于下面的这条java执行命令:

1
java -classpath C:\~\.m2\~\xx1.jar;C:\~\.m2\~\xx2.jar;D:\project\target\classes test.child1.Main -Dkey=value

然后执行命令

1
mvn exec:exec -DaddResourcesToClasspath=true

addResourcesToClasspath是指将资源文件的目录也加入到classpath, 它是可选的,如果资源文件已经在classes目录(即资源文件已经处于classpath中),则不需要附加这个参数。但是如果开发者通过maven-resources-plugin更改了目标路径,会导致找不到资源文件的目录,依赖于资源文件的程序无法运行。

exec:exec添加额外的classpath
上面的例子,无法将自定义的路径加入到classpath,解决办法是手动拼接classpath, 为了方便收集所有的依赖库,可以使用maven-dependency-plugin插件将依赖的库事先拷贝到特定的目录,比如${project.basedir}/target/lib/。 示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<executable>java</executable>
<arguments>
<argument>-classpath</argument>
<argument>
${project.basedir}/target/classes${path.separator}${project.basedir}/target/lib/*${path.separator}${project.basedir}/target/conf
</argument>
<argument>test.child1.Main</argument>
</arguments>
</configuration>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
</plugin>

示例中的${path.seperator}是java的系统变量,表示classpath的分隔符, linux上是冒号”:”, windows上是分号”;”

二、Apache Maven Assembly Plugin

Maven的Assembly插件主要是为了允许用户将项目输出及其依赖项,模块,站点文档和其他文件聚合到一个可分发的归档中。
项目可以方便的使用预制装配描述符之一轻松构建分发“assemblies”.这些描述符处理许多常见操作,例如将项目的组件和生成的文档打包到单个zip存档中.或者,您的项目可以提供自己的描述符,并对程序集中的依赖项,模块,文件集和单个文件的打包方式采取更高级别的控制。

目前,它可以使用以下格式创建分发:

  • zip
  • tar
  • tar.gz (or tgz)
  • tar.bz2 (or tbz2)
  • tar.snappy
  • tar.xz (or txz)
  • jar
  • dir
  • war
  • and any other format that the ArchiveManager has been configured for

如果您的项目想要将工件打包在uber-jar中,则assembly插件仅提供基本支持。要获得更多控制,请使用Maven Shade插件.
要在Maven中使用Assembly Plugin,您只需:

  • 选择或编写要使用的汇编描述符
  • 在项目的pom.xml中配置Assembly Plugin
  • 在你的项目上运行mvn assembly:single

要编写自己的自定义程序集,您需要参考程序集描述符格式

Assembly Plugin入门非常简单。如果要使用其中一个预制程序集描述符,请配置要与<descriptorRefs>/<descriptorRef>参数一起使用的描述符。如果要使用自定义程序集描述符,可以使用<descriptors>/<descriptor>参数配置描述符的路径。
请注意,对Assembly Plugin的单个调用实际上可以从多个描述符生成程序集,从而允许您最大程度地灵活地自定义项目生成的二进制文件集。创建程序集时,它将使用assemblyId作为组件的分类器,并将创建的程序集附加到项目,以便在安装和部署阶段将其上载到存储库中。
例如,假设我们的项目产生了一个JAR。如果我们想要创建一个包含项目依赖项的汇编二进制文件,我们可以利用Assembly Plugin的一个预制描述符。您可以在项目的pom.xml中按如下方式对其进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<!-- NOTE: We don't need a groupId specification because the group is
org.apache.maven.plugins ...which is assumed by default.
-->
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
[...]
</project>

请注意,Assembly Plugin允许您一次指定多个descriptorRefs,以在单个调用中生成多种类型的程序集。
或者,我们在src/assembly目录中创建了一个名为src.xml的自定义程序集描述符(有关更多信息,请参阅Resources部分)。我们可以告诉Assembly Plugin使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptors>
<descriptor>src/assembly/src.xml</descriptor>
</descriptors>
</configuration>
[...]
</project>

再次注意,我们可以在这里指定多个自定义程序集描述符。此外,可以在同一配置中指定描述符和descriptorRef的混合。
注意:许多其他配置选项可用于Assembly Plugin中的各种目标。有关更多信息,请参阅示例部分插件参数文档

一旦为项目生成的程序集配置了各种descriptorsdescriptorRef,就可以构建它们了。
在大多数情况下,您需要确保在正常构建过程中创建程序集。这可确保装配存档可用于安装和部署,并且可在项目发布期间创建它们。这可以通过assembly:single指令配置。
要将single目标绑定到项目的构建生命周期,您可以添加此配置(假设您正在使用jar-with-dependencies预制描述符):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
[...]
</project>

为了创建项目程序集,简单地执行默认生命周期中的正常的package阶段,当此构建完成时,您应该在目标目录中看到一个名称类似于以下内容的文件:

1
target/sample-1.0-SNAPSHOT-jar-with-dependencies.jar

创建可执行JAR
毫无疑问,无论如何,Assembly Plugin都是为项目创建自包含二进制工件的一种非常有用的方法。但是,一旦创建了这个自包含的JAR,您可能希望能够使用-jarJVM开关执行它。
为了适应这种情况,Assembly Plugin支持配置由maven-archiver处理的<archive>元素(请参阅参考资料)。使用此配置,可以轻松配置JAR清单的Main-Class属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
<configuration>
[...]
<archive>
<manifest>
<mainClass>org.sample.App</mainClass>
</manifest>
</archive>
</configuration>
[...]
</plugin>
[...]
</project>

如果我们将此配置添加到上面的single示例并重建,我们将在生成的JAR的META-INF/MANIFEST.MF文件中看到这样的条目:

1
2
[...]
Main-Class: org.sample.App

有关Assembly Plugin的高级配置的更多信息,请参阅参考资料部分。

有关maven-archiver的更多信息,请查看此处
有关高级maven-assembly-plugin配置的更多信息,请参阅示例

三、Apache Maven Shade Plugin

Shade插件只有一个目标:

  • shade:shade 与maven的package生命周期绑定,创建shaded jar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<!-- put your configurations here -->
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

可执行的JAR
要创建可执行的uber JAR,只需设置用作应用程序入口点的主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.sonatype.haven.HavenCli</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

此代码段配置一个特殊的资源转换器,用于设置着色JAR的MANIFEST.MF中的Main-Class条目。其他条目也可以通过<manifestEntries>部分中的键值对添加到MANIFEST.MF中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>org.sonatype.haven.ExodusCli</Main-Class>
<Build-Number>123</Build-Number>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
</project>

如果你喜欢我的blog,请鼓励我一杯咖啡☕️
0%