先点赞再看,养成好习惯

Spring Boot 的打包插件用着很爽吧,间接把源码和所有依赖打成一个 Jar 包还能间接java -jar运行。那如果非 Spring Boot 我的项目,想打成一个可执行的 Jar 包该怎么办呢?

别慌,Maven 这种老牌构建工具,搞定这个事件还不是轻轻松松!

上面介绍一些其余的 Maven 插件,同样能够间接将 Maven 我的项目打包成一个可执行的 Jar 包(uber jar/executable jar),而且性能更弱小,利用场景更丰盛!

对于这个 uber jar/executable jar 的名称由来,能够参考我之前的一篇文章《Executable Jar/Uber Jar/Shade Jar/Shadow Jar/Fat Jar 到底是什么货色?》

maven-dependency-plugin

maven-dependency-plugin是 Maven 的一个内置插件,从名字就能看进去,它的性能就是解决依赖的。内置了很多指标(goals),性能十分全:

  • dependency:copydependency:copy-dependencies
  • dependency:unpack
  • dependency:unpack-dependencies
  • dependency:resolve
  • dependency:sources
  • dependency:resolve-plugins
  • dependency:go-offline
  • dependency:purge-local-repository
  • dependency:analyze
  • dependency:analyze-dep-mgt
  • dependency:analyze-report
  • dependency:tree
  • dependency:build-classpath
  • dependency:list-repositories
  • dependency:get

通过 unpack-dependencies 这个指标来解压依赖的包/源码,就能够实现一个 all-in-one 的打包形式:

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-dependency-plugin</artifactId>  <executions>    <execution>      <id>unpack-dependencies</id>      <!-- 绑定到 prepare-package 阶段 -->      <phase>prepare-package</phase>      <goals>        <goal>unpack-dependencies</goal>      </goals>      <configuration>        <includeScope>runtime</includeScope>        <outputDirectory>${project.build.outputDirectory}</outputDirectory>      </configuration>    </execution>  </executions></plugin>

这个 unpack 的形式,会把所有依赖包(外部模块依赖和内部模块依赖)都“解压”,就是说会把依赖包的代码(class)都拷贝到 outputDirectory 目录里,相似一个合并的操作,后果就像这样:

而后再给 Jar 指定一个 main-class 让他间接可执行:

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-jar-plugin</artifactId>  <configuration>    <archive>      <manifest>        <mainClass>          com.github.kongwu.mavenbuild.BuildExample        </mainClass>      </manifest>    </archive>  </configuration></plugin>

当然!这个插件能干的事可不止这一种……看看下面的命令就晓得,它性能十分多,这里介绍的只是它的一个小性能。

maven-shade-plugin

maven-shade-plugin 也是 Maven 内置的一款插件,也能够间接打一个可执行 Jar 进去。和 dependency 插件成果一样,也是“解压”的模式,将所有依赖包的 class 放在一起,配置一个 transformer 和 mainClass 就行:

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-shade-plugin</artifactId>  <executions>    <execution>      <goals>        <goal>shade</goal>      </goals>      <configuration>        <shadedArtifactAttached>true</shadedArtifactAttached>        <transformers>          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">            <mainClass>com.github.kongwu.mavenbuild.BuildExample</mainClass>          </transformer>        </transformers>      </configuration>    </execution>  </executions></plugin>

构建后的默认 jar 文件为:${artifactId}-${version}-shaded.jar,不喜爱的话你也能够通过<outputFile>去批改输入的 jar 文件

这个插件的精华在 shade ,而这个“解压”只是根本的性能。

有没有感觉这个插件名字很奇怪,shade是什么意思?

shade机翻为暗影、遮蔽,shade jar 是指将 jar 包及其依赖包打包到一个 jar 文件内,同时提供 shade“遮蔽 / 重命名” 某些依赖包的性能

对于 shade 的具体解释,能够参考我的另一篇文章《Shade Jar/Shadow Jar 的解释》

maven-assembly-plugin

最初介绍的这个 maven-assembly-plugin 插件,算是 maven 里最强的构建插件了,尽管它有用的 goal 只有一个,但性能真的十分十分弱小:

  1. 能够通过独立的形容文件配置具体的构建规定
  2. 蕴含或者排除某个模块/目录/文件
  3. 反对 maven filter
  4. 一套代码,同时构建多个不同配置的 bin 包
  5. 不同的构建包格局,比方 jar/zip/tar/gz 等等
  6. ……

比方 Zookeeper/Nacos/Arthas/Jenkins ,或者最近比拟火的 pulsar 之类须要独立运行的软件,很多都是用这个插件构建的

先看它的一个简略场景,构建一个可执行 Jar 包:

<plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-assembly-plugin</artifactId>  <executions>    <execution>      <phase>package</phase>      <goals>        <goal>single</goal>      </goals>      <configuration>        <archive>          <manifest>            <mainClass>              com.github.kongwu.mavenbuild.BuildExample            </mainClass>          </manifest>        </archive>        <descriptorRefs>          <descriptorRef>jar-with-dependencies</descriptorRef>        </descriptorRefs>      </configuration>    </execution>  </executions></plugin>

和下面两个插件成果一样,都是“解压”的形式,默认构建的文件为 ${artifactId}-${version}-jar-with-dependencies.jar

这么弱小的插件,只拿他构建一个 uber jar 可有点太节约了,如果只是简略的 uber jar 场景,用后面两种形式就足够了。

所以这个插件更适宜简单的构建需要,简略的uber jar场景拿这种加特林级别的工具有一点节约了……

来看看 Nacos 中的应用形式:

在 Nacos 的源码中,独自放了一个 distribution 的模块用于构建,借助 assembly 插件 + profile 性能,能够很不便的构建出各种环境的 bin 包:

<!-- nacos distribution/pom.xml--><profile>  <id>release-nacos</id>  <dependencies>    <dependency>      <groupId>${project.groupId}</groupId>      <artifactId>nacos-console</artifactId>    </dependency>  </dependencies>  <build>    <plugins>      <plugin>        <groupId>org.apache.maven.plugins</groupId>        <artifactId>maven-assembly-plugin</artifactId>        <configuration>          <descriptors>            <descriptor>release-nacos.xml</descriptor>          </descriptors>          <tarLongFileMode>posix</tarLongFileMode>        </configuration>        <executions>          <execution>            <id>make-assembly</id>            <phase>install</phase>            <goals>              <goal>single</goal>            </goals>          </execution>        </executions>      </plugin>    </plugins>    <finalName>nacos</finalName>  </build></profile> <profile>   <id>release-core</id>   <dependencies>     <dependency>       <groupId>${project.groupId}</groupId>       <artifactId>nacos-core</artifactId>     </dependency>   </dependencies>   <build>     <plugins>       <plugin>         <groupId>org.apache.maven.plugins</groupId>         <artifactId>maven-assembly-plugin</artifactId>         <executions>           <execution>             <id>release-core</id>             <goals>               <goal>single</goal>             </goals>             <phase>package</phase>             <configuration>               <descriptors>                 <descriptor>release-core.xml</descriptor>               </descriptors>               <appendAssemblyId>false</appendAssemblyId>             </configuration>           </execution>         </executions>       </plugin>     </plugins>     <finalName>nacos-core</finalName>   </build></profile>

Nacos 这种构建形式,也是比拟“支流”的形式了,如果哪天你有构建独立运行包的需要,置信你也会用这种形式。

总结

残缺代码放在https://github.com/kongwu-/maven-build-examples/blob/master/pom.xml ,有趣味的小伙伴能够拉下来试试

好了,介绍完了这几种插件构建 uber-jar 的玩法,当初做个比照:

dependencyshadeassembly
长处goals 丰盛,除了 unpack 还有很多其余的性能,比方清理/查看依赖树等等专为 uber-jar 而生,而且反对 shade 性能,如果有重定位的需要,只能选它性能最强,配置非常灵活,但没有 shade 性能
毛病毕竟只是个解决依赖的插件,在构建方面的性能比拟弱简单的构建需要下,性能会有些有余没有 shade 性能,而且配置比较复杂
利用场景适宜简略的 uber-jar 性能最适宜 uber-jar 的构建,配合 shade 性能几乎完满适宜简单场景下的构建,不止是 uber jar

本文介绍的 3 种插件,他们在构建 uber jar 的机制上和 Spring Boot 有所不同:

Spring Boot 构建插件将会将依赖的 Jar 包打在 uber jar 内,是一种 "jars-in-a-jar" 的形式, 通过它的自定义 ClassLoader 去加载 Jar 包内的 Jar 包;而下面介绍的几种插件,并不会干涉 mainClass 和 ClassLoader ,无奈做到加载 Jar 包内的 Jar 包,所以都是通过“解压”的形式。

留神,Spring Boot 的构建插件,不只能用在 Spring Boot 我的项目中。它的外围性能还是构建,只是把启动类换成了 Spring Boot 的,而后通过它自定义的 ClassLoader 来加载。

所以,用spring-boot-maven-plugin将一些非 Spring(Boot) 我的项目打包成一个 uber jar 也齐全没问题,JDK 和 Maven 的版本匹配就好。

参考

对于以上几个插件的具体性能,能够参考上面插件的官网文档,Maven 的文档还是比拟具体的:

  • https://juejin.cn/post/6941618160386703390
  • http://maven.apache.org/plugins/maven-dependency-plugin/index.html
  • http://maven.apache.org/plugins/maven-jar-plugin/
  • http://maven.apache.org/plugins/maven-shade-plugin/
  • http://maven.apache.org/plugins/maven-assembly-plugin/index.html
  • https://github.com/alibaba/nacos

收费小菜

附上一张本人重绘的 Maven default 生命周期图,还是比拟清晰的,表明了每个 phase 对应的不同 plugin 的不同 goal,如有须要自行保留(原图略大,点击查看大图)

原创不易,禁止未受权的转载。如果我的文章对您有帮忙,就请点赞/珍藏/关注激励反对一下吧❤❤❤❤❤❤