Spring Boot 项目如何打包外部 jar 包

起因是这样的,平时开发 Java 项目时,我积累了一些常用的工具方法,使用起来很方便,可以在一定程度上提高开发效率。最近,我想要做一个新的开源项目,任何人都可以克隆到本地并进行打包构建,然后使用。我计划在这个开源项目中引用我的工具 jar 包,但是遇到了一个问题,我的工具 jar 包没有发布到 Maven 中央仓库。我看了码农参上公众号上的一篇文章《发布了一个 jar 包到中央仓库,我的心好累…》,于是我放弃了。然而,我又想在新项目中引入我的工具 jar 包,以提高开发效率。我应该如何解决呢?

通过查找资料,找到一个解决方案,参考知乎文章《Java + SpringBoot + Maven + 外部 jar 打包》。基本思路是将 dependency.scope 设为 system,并指定 systemPath。然后在 build 中配置 resources 信息。具体详情如下:

在项目中, 新建 src 平级目录 lib, 拷贝构建好的 jar 包 avatar-core, 并以如下方式引入依赖

<dependency>
  <groupId>org.enthusa.avatar</groupId>
  <artifactId>avatar-core</artifactId>
  <version>master-SNAPSHOT</version>
  <scope>system</scope>
  <systemPath>${project.basedir}/lib/avatar-core-master-SNAPSHOT.jar</systemPath>
</dependency>

这样,在使用 IDEA 进行开发时,就能够找到所需的依赖,避免出现错误。然而,如果引用了 avatar-core 中的方法,例如 DateUtil.getDate(),在打包构建完成后执行时,会报告以下错误:

java.lang.NoClassDefFoundError: org/enthusa/avatar/core/utils/DateUtil

也就是说, 在通过 spring-boot-maven-plugin 插件构建好的可执行 jar 包, 并没有自动把 /avatar-core-master-SNAPSHOT.jar 也打进来. 通过 unzip 解压缩查看, 也验证了这一点. 于是, 我们在 build 中配置 resources 信息, 指定 lib 下的 jar 包也是 resource, 需要拷贝到 BOOT-INF/lib/ 目录下.

<resources>
  <resource>
    <directory>src/main/resources</directory>
  </resource>
  <resource>
    <directory>lib</directory>
    <targetPath>BOOT-INF/lib/</targetPath>
    <includes>
      <include>*.jar</include>
    </includes>
  </resource>
</resources>

重新打包后执行, 又会报如下错误:

java.lang.ClassNotFoundException: org.apache.commons.lang3.time.DateFormatUtils

大致可以想到,avatar-core 依赖于 commons-lang3。但是通过上述方式引入 avatar-core 时,并没有自动引入它的依赖包。这个问题很容易解决,在这个项目中再次手动引入 commons-lang3 就可以解决了。

建议

如果要使用 system scope 这种方式引入 jar 包 (实际工作中很少遇到), 建议不要在项目 project 下, 再分模块 module, 在 module 中引入 system scope 依赖. 因为这种方式引入的依赖, 不会传递, 带来更多麻烦.