1、使用spring-boot-starter-parent或spring-boot-dependencies固化依赖
在创建Spring Boot程序的时候通常将Maven项目的模块继承于spring-boot-starter-parent,如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
这样我们添加相关的Starter依赖时就不用指定版本号了,这种特性属于Maven的依赖管理,降低了Spring Boot应用管理依赖的成本。
不过这种通过继承spring-boot-starter-parent的方式存在一定的限制,以为Maven是单继承,限制只能继承Spring Boot相关的依赖,但通常应用很有可能有自定义的Parent。这样的话就可以使用import范围依赖导入依赖管理配置,如下:
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
依赖范围有个import范围,import依赖范围只有在dependencyManagement下才有效果,使用该范围的依赖通常指向一个POM,作用是将目标中的dependencyManagement配置导入到当前POM的dependencyManagement中配置,除了复制配置或者继承这两种方式之外,还可以使用import范围依赖将这一配置导入。
如果应用有自定义的parent依赖,可以使用这种方式来管理Spring Boot依赖,使用dependencyManagement来配置Spring Boot依赖在执行mvn package会报如下错误:
错误提示指出Maven编译插件maven-compiler-plugin3.1版本不再支持编译Java5,在pom.xml中添加如下代码再次执行打包命令,显示maven-compiler-plugin:3.1构建成功。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
我们将应用程序pom.xml中的打包方式改为war(war),然后再次执行mvn package命令,这次又报了个错:
Maven构建日志提示maven-war-plugin插件执行失败,这是因为Web应用部署描述文件WEB-INF/web.xml在项目中不存在的缘故。当项目pom.xml设置为war后他将执行maven-war-plugin,而WEB-INF/web.xml默认为必选项,可以通过插件配置属性将其忽略。
那么问题就来了,为何直接通过继承spring-boot-starter-parent的方式构建项目就没问题,而通过dependencyManagement引入spring-boot-dependencies就会出现上面两个问题呢?下面就来弄清这个问题。
2、理解spring-boot-starter-parent或spring-boot-dependencies
打开spring-boot-starter-parent的pom.xml文件:
可以发现spring-boot-starter-parent继承于spring-boot-dependencies,并且将源码版本和构建的目标版本设定为java1.8,spring-boot-maven-plugin插件加入了一个repackage。
在来看看spring-boot-dependencies的pom.xml文件:
通过测试和对比两份pom.xml文件可以得出结论:
- 当项目pom.xml文件中的引用spring-boot-starter-parent时,mvn package将使用maven-compiler-plugin:3.8.1和maven-war-plugin:3.2.3并且将编译级别设定为java1.8,spring-boot-maven-plugin插件加入了一个repackage。
- 使用导入spring-boot-dependencies的方式与前者同源,但是只有节点会生效,所以maven-compiler-plugin采用了默认的3.1版本编译级别为java5、maven-war-plugin采用了2.2的版本。
因此使用导入spring-boot-dependencies的方式,需要额外加入下面代码依然可以构建成功:
打包成功后启动当前应用:
java -jar target/app-1.0-SNAPSHOT.war
Error: Invalid or corrupt jarfile target/app-1.0-SNAPSHOT.war
提示war包无效无法启动,通过对构建日志的观察发现是没有spring-boot-maven-plugin的信息,说明这个插件未执行,原因在于没加入该插件,在项目pom.xml中加入如下代码再次执行mvn package并通过上面命令启动war包。
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
java -jar target/app-1.0-SNAPSHOT.war
target/app-1.0-SNAPSHOT.war中没有主清单属性
这次运行失败是找不到war包中的清单文件,原因在于该插件未指定版本号,加入如下版本号重复上面操作。通过观察构建日志发现spring-boot-maven-plugin依然没有执行,需要添加为repackage如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
在此执行mvn package打包命令,终于看到spring-boot-maven-plugin生效,并且运行正常。
3、总结
尽管应用正常启动,不过以上依赖和插件的配置方式需要开发人员清晰的认识到其中的差异:
- maven-war-plugin插件在新老版本的差异,在老版本maven-war-plugin:2.2中默认的打包规则是必须存在Web应用部署描述文件WEB-INF/web.xml,而maven-war-plugin:3.2.3调整了该行为。
- 单独引入spring-boot-maven-plugin插件时需要配置repackage元素,否则不会添加Spring Boot引导依赖,进而无法启动应用。