Java -jar 运行Spring Boot多模块项目提示没有主清单文件
1.前言
我使用Maven自定义parent的方式搭建SpringBoot多模块项目,然后在本地开发运行没有问题,但是当要用Maven打包成jar包丢到服务器运行时,执行通过java -jar web.jar
运行时提示web.jar中没有主清单属性却报了下面的错误:
java -jar web.jar
web.jar中没有主清单属性
2.解决web.jar中没有主清单属性
2.1.寻找问题原因出现的原因
既然本地开发运行没有问题,那应该思考是Maven打包时出现问题,根据提示是主清单属性找不到,在网上搜了一下,注清单是指Jar包中的META-INF目录MANIFEST.MF
打开jar包,META-INF目录下的MANIFEST.MF,内容如下:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dell
Created-By: Apache Maven 3.8.1
Build-Jdk: 1.8.0_202
然后再对比公司其他项目的可运行的Jar包
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dell
Start-Class: com.jrsmart.transfer.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.2.1.RELEASE
Created-By: Apache Maven 3.8.1
Build-Jdk: 1.8.0_202
Main-Class: org.springframework.boot.loader.JarLauncher
就大概猜出是缺少了下面一些配置信息
Start-Class: com.jrsmart.transfer.Application
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.2.1.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
其中比较关键还是我们没有主启动类Start-Class
,是什么原因导致的呢?
2.2.为什么会缺少主启动类Start-Class
我web模块的pom文件如下:
<parent>
<artifactId>printer</artifactId>
<groupId>com.jrsmart.print</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>web</artifactId>
然后父工程的pom是以spring-boot-dependencies
依赖的形式来引入SpringBoot的定义的依赖的,没有使用到官方脚手架的方式来构建即下面的形式构建
官方脚手架的方式是这样的
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这就为我打包jar包埋下了坑,我开始尝试在网上寻找办法解决,网上大多数资料指出需要在pom.xml中配置maven插件,如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
我试了一下,发现在我自定义的parent的pom.xml下加入这些配置是没办法生效的
折腾了挺久的,于是我好奇点进去能运行的其他SpringBoot的jar包的spring-boot-starter-parent寻找问题
spring-boot-starter-parent 中maven插件的配置如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${start-class}</mainClass>
</configuration>
</plugin>
好家伙一看到mainClass
以及start-class
,基本就觉得是这里的问题了,能上面缺少的Main-Class
以及Start-Class
对应
我们可以看到这里配置了主类信息以及一个重要的标签,对repackage的描述如下:
Repackages existing JAR and WAR archives so that they can be executed from the command line using java -jar.
重新打包现有的JAR和WAR归档文件,以便可以使用java -jar从命令行执行它们。
看到这里我们就清楚了,当使用自定义的 parent 时,我们需要自行配置maven插件的<goal>
属性,如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2.3.解决问题
如果你是使用SpringBoot默认的parent来构建的话,那么就可以加入下面的配置应该就可以解决大多数问题
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
如果你是使用SpringBoot自定义parent来构建的话,那么就可以加入mainClass
配置应该就可以解决问题,mainClass
对应的是SpringBoot全限定类名。
配置如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.1.RELEASE</version>
<configuration>
<outputDirectory>
${project.basedir}/deploy
</outputDirectory>
<fork>
true
</fork>
<mainClass>
com.jrsmart.print.WebApplication
</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>
repackage
</goal>
</goals>
</execution>
</executions>
</plugin>
之后指定mvn clean package指令打包jar包后看一下清单文件,内容如下:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: dell
Start-Class: com.jrsmart.print.WebApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.2.1.RELEASE
Created-By: Apache Maven 3.8.1
Build-Jdk: 1.8.0_202
Main-Class: org.springframework.boot.loader.JarLauncher
这样项目就打包成功了,通过java -jar也可以正确运行了。
3.经验总结
这里自己总结一下吧,如果项目没有特殊需求,就使用SpringBoot的默认脚手架方式搭建,也就是SpringBoot的Spring initializr
来构建。
我这里是因为公司要搭建的服务很小型,不需要用到SpringBoot全部的依赖,自己按需引入spring-boot-dependencies
然后手动排除一些依赖来达到轻量化项目大小的目的,因此踩了一些坑。