Maven 各种花式构建,不用 SpringBoot 也能打出可执行 Jar 包

 

 

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

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

下面介绍一些其他的 Maven 插件,同样可以直接将 Maven 项目打包成一个可执行的 Jar 包(uber jar/executable 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 目录里,类似一个合并的操作,结果就像这样: Maven 各种花式构建,不用 SpringBoot 也能打出可执行 Jar 包 然后再给 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“遮蔽 / 重命名” 某些依赖包的功能

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 中的使用方式: Maven 各种花式构建,不用 SpringBoot 也能打出可执行 Jar 包 在 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 这种构建方式,也是比较“主流”的方式了,如果哪天你有构建独立运行包的需求,相信你也会用这种方式。

总结

完整代码可以加我QQ:3177181324,有兴趣的小伙伴可以来试试

好了,介绍完了这几种插件构建 uber-jar 的玩法,现在做个对比:

  dependency shade assembly
优点 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 的版本匹配就好。


如果觉得文章对你有帮助,麻烦 点赞、评论、收藏 你的支持是我最大的动力!!!

最后小编在学习过程中整理了一些学习资料,可以分享给做java的工程师朋友们,相互交流学习,需要的可以加入我的学习交流群 323432957 即可免费获取Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

Maven 各种花式构建,不用 SpringBoot 也能打出可执行 Jar 包

作者:dingyu002

来源:dinyu002

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

上一篇:CRITICAL_STRUCTURE_CORRUPTION(109)蓝屏错误的分析难点


下一篇:spring boot Caused by: java.nio.charset.MalformedInputException: Input length = 1