前言
相信经历过很久以前初代Java Web开发的程序员都应该有体验,在那个时候是没有Maven这种东西的,每引入一个新功能,就需要手动下载依赖的JAR包导入,还可能和已经导入的JAR包产生冲突。更难受的是,运行的时候抛出异常查半天,到处修改业务代码以为自己写错了,结果发现问题处在JAR包冲突……所以,之前一直在用师兄师姐传承下来的经过多年验证的Jar包组……
而有了Maven,所有的JAR包问题都不用担心,可以完全集中精力写逻辑了。当然,管理Jar包只是Maven的其中一个功能,它还可以用来拆分和聚合模块,将整个大项目分成多个独立的模块分别开发,然后自动部署。
本文将只注重基础的管理Jar包的基础功能,其他的以后填坑。
Maven简单入门
Maven读音
官方的读法应该是[ˈmevən],“美ven”,其实“A”也可以读成苹果“apple”的“a”,但千万别读成“马ven”。
Maven官网
使用Maven管理JAR包
(1)Maven的世界坐标
Maven通过:
【包名 / groupId】:通常是公司域名反写,如com.bewindoweb.javaweb
【项目名 / artifactId】:如webdemo
【版本 / version】:如0.0.1-SNAPSHOT
三个参数来定位一个Jar包,可以把这三个参数看作是X轴、Y轴、Z轴,一旦参数确定,Jar包也就确定了。很容易理解,如果是你开发的工程打包的Jar包,只要你的公司名确定、项目名确定、项目版本确定,这个工程也就确定了。
(2)Maven的快照版本和发布版本
我们常常可以看到许多不同的version,但对于Maven来说只有两种版本,一种是快照版本SNAPSHOT,一种是稳定版本RELEASE。识别的标准就一个,只要没有写SNAPSHOT,通通归为RELEASE。例如:1、1.12、1.0.3、2.0-rc1、4.3.5-Final、4.0.3-RELEASE、2.0-beta9等等,虽然字面意思看来,存在软件工程角度的beta测试版、Release Candidate最终测试版,但对于Maven的约定而言,全都是稳定版本。
SNAPSHOT什么时候用?就是你内部快速迭代不同功能模块的时候用。例如,我们开发了两个模块A、B,假设B发布的版本是1.0.0-RELEASE,A依赖于B的1.0.0,当B发生了新的修改,那么就必须发布一个1.0.1,A也必须修改B为1.0.1。两个模块还好,如果有100个A呢?如果B只是每次改1行代码,一天提交100个版本呢?所以,在开发期间,活跃的模块应该使用SNAPSHOT。只要采用SNAPSHOT,其他依赖它的模块会自动部署为最新的版本,而无需改变版本号。例如,B发布的版本是1.0.0-SNAPSHOT,B提交更新并部署到仓库后,A只需要重新构建就可以得到最新版本的B,完全不用去改变版本号。
但这样会带来一个问题,那就是如果B更改了A依赖的关键代码,A在没有更改自己任何代码的情况下,发现竟然构建失败了,这对于A来说是很诡异的事情。所以,线上一定要采用RELEASE版本。但如果B只提供了SNAPSHOT版本呢?你需要使用versions-maven-plugin插件来将SNAPSHOT固定下来,例如使用之后会将1.0.0-SNAPSHOT变为1.0.0.20181231.123456-1这种时间戳
,就可以把SNAPSHOT固定了。
(3)Maven解决Jar包冲突
有可能会有两个Jar依赖不同版本的相同底层Jar,Maven需要解决Jar包冲突的问题。
【方法一】让Maven自己处理。
Maven用两个原则处理Jar包冲突。
① 第一原则:最短路径优先
对于两个依赖关系
A→B→C→D(1.0.0-RELEASE)
E→D(2.0.0-RELEASE)
Maven会选择最短依赖路径E→D,加入D(2.0.0-RELEASE)。
② 第二原则:第一原则如果失效,那么最先声明优先
对于两个相同长度依赖路径的依赖关系
A→D(1.0.0-RELEASE)
E→D(2.0.0-RELEASE)
Maven会选择最先声明的A→D,加入D(1.0.0-RELEASE)。
【方法二】通过exclusions手动处理
通过exclusions排除不想要的包,例如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
就排除了spring-boot-starter-test中的junit包。
(4)scope依赖范围
对于Jar包,需要什么范围内才使用,什么范围内不使用,可以使用scope来进行约束。例如前面的spring-boot-starter-test就只会在测试阶段才会被导入。scope取值含义列表如下:
scope取值 | 编译阶段 | 测试阶段 | 运行阶段 | 典型Jar |
---|---|---|---|---|
compile | √ | √ | √ | 默认值 |
test | √ | Junit | ||
provided | √ | √ | servlet-api | |
runtime | √ | √ | JDBC-driver | |
system | √ | √ | 本地jar |
compile:默认就是comile,三个阶段均有效。
test:一些测试时才会用到的包。
provided:一些容器提供的包,编译和测试需要用,但运行不需要。
runtime:一些编译不需要的接口之类的包。
system:自己手动导入的包。
但其实最常使用的就只有test和provided而已。
POM文件
以上所有的内容都写在pom.xml里,POM的含义是项目对象模型(Project Object Model),只要编写好pom.xml,Maven就会按照你的思路去进行操作了。常见的spring-boot工程pom.xml如下,可以很容易地读懂:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<packaging>war</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<lombok.version>1.16.10</lombok.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
主要就是需要编写dependency,如果你只知道包名,不知道其他信息,你可以通过mvnrepository这个网站来查询,它会提供有哪些版本、每个版本使用情况如何等信息:
以及不同的管理工具对应的语法应该怎么写:
非常方便。
总结
这些都是基础的Maven用法,掌握了这些之后写一个Java Web就会很顺利的。如果还想了解更多,可以去了解Parent、import、type、properties中的固定约定、如何手动导入Jar包(不推荐、还是自己使用nexus搭建本地服务器比较好),和前面提到拆分模块等等。另外,如果你还在使用Java 1.7、1.6,或者还在使用eclipse、myeclipse,希望能够尝试一下Java8+、IntelliJ IDEA,你会打开新世界大门的。