这里写自定义目录标题
问题现象
错误1
spring类型应用启动后报错,no main manifest attribute, in /app.jar
错误2
mvn build过程中,提示repackage failed
原因分析
大多数情况下,都是因为在spring应用的pom.xml中没有使用spring-boot-maven-plugin打包插件,或者插件的版本没有指定导致,需要在pom.xml里加入以下内容
<build>
</plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version> //你的springboot的版本号是多少,这里就填多少
<executions>
<execution>
<goals>
<goal>repackage</goal> //如果你的项目的parent是spring-boot-starter-parent,executions这一段也可以省略,因为在parent中已经指定了。反之则不可省
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
深入研究
spring-boot-maven-plugin
spring-boot-maven-plugin是spring自带的一个用来打包的插件,其原理就是在mvn package的时候,将本应用自身的class,及他的依赖重新打包(repackage)成一个大的jar。只要安装指定版本jre,就可以通过java -jar xxx.jar运行,而不需要去系统的lib库中下载依赖,有兴趣看详情的可以看看spring-boot-maven-plugin原理,我们这里看下效果。
先建一个springboot的helloword项目,注意在pom里引入spring-boot-maven-plugin插件
public class CsdemoApplication {
@RequestMapping("/")
public String greeting() {
return "Greeting!";
}
public static void main(String[] args) {
SpringApplication.run(CsdemoApplication.class, args);
}
}
执行mvn clean package,在target目录下会生成一个jar文件和一个jar.original文件,original的还不到5K,其实这个是maven自带的打包工具对本工程自有代码进行打包的产物。而jar文件却有近26MB,这是spring-boot-maven-plugin对original进行repackage的产物。
把两个文件解压缩后对比,可以发现主要变化是jar里面多了一个lib文件夹,里面存储了本项目所依赖的所有jar。这个文件夹足足有25M,是导致整个jar包体积变大,以及为什么可以直接java -jar xxx.jar启动程序的原因。
写多了spring应用的同学估计注意到了,有时候可以省略${spring-boot.version}这一行,但是有时候就会报错,这又是为什么呢。
这个取决于你的项目的依赖关系,如果你的项目的parent是spring-boot-starter-parent(或其子项目),那么就可以在spring-boot-maven-plugin处省略version属性,mvn会自动去spring-boot-starter-parent中寻找相应的版本。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
但如果你的项目的parent不是spring-boot-starter-parent(或其子项目),例如是继承于公司自己做的parent,那spring-boot-maven-plugin的version就必不可少,否则就会出现第一章中的错误。
maven依赖的版本管理机制
maven可以通过两种方式实现denpendencies模块中组件的版本统一管理,一种是整个项目parent指定为spring-boot-starter-parent,另一种是在dependencyManagement中引入spring-boot-dependencies,二者有一点点区别:
parent指定为spring-boot-starter-parent
如果你的项目的parent是spring-boot-starter-parent,那么在dependency中引入任何spring相关的starter时,均可以省略其版本号,原因就是在parent中已经引入了Spring Boot Dependencies,里面指定了大部分常用依赖的版本:
如果想在自己的应用中,修改某个依赖的版本,可以直接在properties中去指定:
<properties>
<spring-data-releasetrain.version>Fowler-SR2</spring-data-releasetrain.version>
</properties>
如果要排除parent中默认使用的某个依赖,换成别的,可以按如下方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<exclusions>
<!-- 过滤lettuce,使用jedis作为redis客户端 -->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
在dependencyManagement中引入spring-boot-dependencies
如果你的项目不是继承于spring-boot-starter-parent,而是通过dependencyManagement引入的sprintboot插件依赖,那么你依然可以在dependencies模块中省略各spring依赖的版本号,但是在build模块中就必须去指定版本号(比如前文所提到的spring-boot-maven-plugin就是在build模块下的)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
但这种方式,不能使用properties中直接去指定spring依赖的版本,而是需要在dependencyManagement里面的spring-boot-dependencies之前修改依赖的版本。例如同样修改Spring Data release train:
<dependencyManagement>
<dependencies>
<!-- Override Spring Data release train provided by Spring Boot -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>Fowler-SR2</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>