Spring Boot 打包成的可执行 jar ,为何不能被其余项目依赖?

前两天被人问到这样一个问题:java

“松哥,为何个人 Spring Boot 项目打包成的 jar ,被其余项目依赖以后,老是报找不到类的错误?”spring

大伙有这样的疑问,就是由于还没搞清楚可执行 jar 和普通 jar 到底有什么区别?今天松哥就和你们来聊一聊这个问题。后端

多了一个插件

Spring Boot 中默认打包成的 jar 叫作 可执行 jar,这种 jar 不一样于普通的 jar,普通的 jar 不能够经过 java -jar xxx.jar 命令执行,普通的 jar 主要是被其余应用依赖,Spring Boot 打成的 jar 能够执行,可是不能够被其余的应用所依赖,即便强制依赖,也没法获取里边的类。可是可执行 jar 并非 Spring Boot 独有的,Java 工程自己就能够打包成可执行 jar 。bash

有的小伙伴可能就有疑问了,既然一样是执行 mvn package 命令进行项目打包,为何 Spring Boot 项目就打成了可执行 jar ,而普通项目则打包成了不可执行 jar 呢?restful

这咱们就不得不提 Spring Boot 项目中一个默认的插件配置 spring-boot-maven-plugin ,这个打包插件存在 5 个方面的功能,从插件命令就能够看出:前后端分离

五个功能分别是:maven

  • build-info:生成项目的构建信息文件 build-info.properties
  • repackage:这个是默认 goal,在 mvn package 执行以后,这个命令再次打包生成可执行的 jar,同时将 mvn package 生成的 jar 重命名为 *.origin
  • run:这个能够用来运行 Spring Boot 应用
  • start:这个在 mvn integration-test 阶段,进行 Spring Boot 应用生命周期的管理
  • stop:这个在 mvn integration-test 阶段,进行 Spring Boot 应用生命周期的管理

这里功能,默认状况下使用就是 repackage 功能,其余功能要使用,则须要开发者显式配置。spring-boot

打包

repackage 功能的 做用,就是在打包的时候,多作一点额外的事情:微服务

  1. 首先 mvn package 命令 对项目进行打包,打成一个 jar,这个 jar 就是一个普通的 jar,能够被其余项目依赖,可是不能够被执行
  2. repackage 命令,对第一步 打包成的 jar 进行再次打包,将之打成一个 可执行 jar ,经过将第一步打成的 jar 重命名为 *.original 文件

举个例子:ui

对任意一个 Spring Boot 项目进行打包,能够执行 mvn package 命令,也能够直接在 IDEA 中点击 package ,以下 :

打包成功以后, target 中的文件以下:

这里有两个文件,第一个 restful-0.0.1-SNAPSHOT.jar 表示打包成的可执行 jar ,第二个 restful-0.0.1-SNAPSHOT.jar.original 则是在打包过程当中 ,被重命名的 jar,这是一个不可执行 jar,可是能够被其余项目依赖的 jar。经过对这两个文件的解压,咱们能够看出这二者之间的差别。

两种 jar 的比较

可执行 jar 解压以后,目录以下:

能够看到,可执行 jar 中,咱们本身的代码是存在 于 BOOT-INF/classes/ 目录下,另外,还有一个 META-INF 的目录,该目录下有一个 MANIFEST.MF 文件,打开该文件,内容以下:

Manifest-Version: 1.0
Implementation-Title: restful
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: org.javaboy.restful.RestfulApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.6.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
复制代码

能够看到,这里定义了一个 Start-Class,这就是可执行 jar 的入口类,Spring-Boot-Classes 表示咱们本身代码编译后的位置,Spring-Boot-Lib 则表示项目依赖的 jar 的位置。

换句话说,若是本身要打一个可执行 jar 包的话,除了添加相关依赖以外,还须要配置 META-INF/MANIFEST.MF 文件。

这是可执行 jar 的结构,那么不可执行 jar 的结构呢?

咱们首先将默认的后缀 .original 除去,而后给文件重命名,重命名完成,进行解压:

解压后能够看到,不可执行 jar 根目录就至关于咱们的 classpath,解压以后,直接就能看到咱们的代码,它也有 META-INF/MANIFEST.MF 文件,可是文件中没有定义启动类等。

Manifest-Version: 1.0
Implementation-Title: restful
Implementation-Version: 0.0.1-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0
复制代码

注意

这个不能够执行 jar 也没有将项目的依赖打包进来。

从这里咱们就能够看出,两个 jar ,虽然都是 jar 包,可是内部结构是彻底不一样的,所以一个能够直接执行,另外一个则能够被其余项目依赖。

一次打包两个 jar

通常来讲,Spring Boot 直接打包成可执行 jar 就能够了,不建议将 Spring Boot 做为普通的 jar 被其余的项目所依赖。若是有这种需求,建议将被依赖的部分,单独抽出来作一个普通的 Maven 项目,而后在 Spring Boot 中引用这个 Maven 项目。

若是非要将 Spring Boot 打包成一个普通 jar 被其余项目依赖,技术上来讲,也是能够的,给 spring-boot-maven-plugin 插件添加以下配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <classifier>exec</classifier>
            </configuration>
        </plugin>
    </plugins>
</build>
复制代码

配置的 classifier 表示可执行 jar 的名字,配置了这个以后,在插件执行 repackage 命令时,就不会给 mvn package 所打成的 jar 重命名了,因此,打包后的 jar 以下:

第一个 jar 表示能够被其余项目依赖的 jar ,第二个 jar 则表示一个可执行 jar。

好了,关于 Spring Boot 中 jar 的问题,咱们就说这么多,有问题欢迎留言讨论。

关注公众号【江南一点雨】,专一于 Spring Boot+微服务以及先后端分离等全栈技术,按期视频教程分享,关注后回复 Java ,领取松哥为你精心准备的 Java 干货!

相关文章
相关标签/搜索