有些人可能会将 SpringBoot 打包成的 jar 包作为项目依赖导入到其它项目中,结果启动就会发现会报找不到类的错误。会有这种操作的误区,是因为混淆了 SpringBoot 打包成的 jar 包与普通的 jar 包。
我们常把 SpringBoot 打包成的 jar 包称为可执行 jar 包,而普通 jar 包往往作为依赖使用,这并不是说普通 jar 包不能执行,如果普通 jar 包中确定了 Main 入口主类,也是可以直接通过 java -jar xxx.jar
方式运行的。它们之间的主要区别在于 SpringBoot 中有一个默认打包插件 Spring-boot-maven-plugin,这个插件有 5 个方面的功能,从插件配置就可以看出来:
五个功能分别是:
*.origin
默认情况下使用就是 repackage 功能,其他功能要使用,则需要开发者显式配置。
repackage 功能的 作用,就是在打包的时候,多做一点额外的事情:
*.original
举个例子,我们对任意一个 SpringBoot 项目进行打包,可以执行 mvnpackage 命令,也可以直接在 IDEA 中点击 package,如下所示:
打包成功后,target 中的文件如下:
其中有两个 jar 包,一个叫做 tumo-0.01-SNAPHOT.jar 这是最终被打包成的可执行 jar 包,第二个就是 tumo-0.01-SNAPHOT.jar.original 则是在打包过程中,被重命名的 jar,通过对这两个文件的解压,我们可以看出这两者之间的差异。
最终的可执行 jar 解压之后,目录如下:
可以看到,可执行 jar 中,我们自己的代码是存在 于 BOOT-INF/classes/
目录下,另外,还有一个 META-INF
的目录,该目录下有一个 MANIFEST.MF
文件,打开该文件,内容如下:
Manifest-Version: 1.0 Implementation-Title: tumo Implementation-Version: 0.0.1-SNAPSHOT Built-By: zuoxiang Implementation-Vendor-Id: cn.bestzuo Spring-Boot-Version: 2.1.3.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: cn.bestzuo.TumoBlogApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Created-By: Apache Maven 3.6.0 Build-Jdk: 1.8.0_211 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/tumo
可以看到,这里定义了一个 Start-Class,这就是可执行 jar 的入口类, Spring-Boot-Classes 表示我们自己代码编译后的位置, Spring-Boot-Lib 则表示项目依赖的 jar 的位置。
换句话说,如果自己要打一个可执行 jar 包的话,除了添加相关依赖之外,还需要配置 META-INF/MANIFEST.MF 文件。
那么原始的普通 jar 包呢?我们解压一下看一下:
解压后可以看到,根目录就相当于我们的 classpath,解压之后,直接就能看到我们的代码,它也有 META-INF/MANIFEST.MF 文件,但是文件中没有定义启动类等。
Manifest-Version: 1.0 Implementation-Title: tumo Implementation-Version: 0.0.1-SNAPSHOT Built-By: zuoxiang Implementation-Vendor-Id: cn.bestzuo Created-By: Apache Maven 3.6.0 Build-Jdk: 1.8.0_211 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/tumo
这个 jar 也没有将项目的依赖打包进来。
从这里我们就可以看出,两个 jar ,虽然都是 jar 包,但是内部结构是完全不同的,因此一个可以直接执行,另一个则可以被其他项目依赖。