简介
Maven是Apache软件基金会组织维护的一款自动化构建工具,专注服务于Java平台的项目构建和依赖管理。Maven这个单词的本意是:专家,内行。读音是['meɪv(ə)n]或['mevn]。
依赖
依赖范围
maven中依赖常见的范围有 compile、provided、test 三种。
compile范围依赖:
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:参与
- 是否参与部署:参与
- 典型例子:spring-core
provided范围依赖:
- 对主程序是否有效:有效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
- 典型例子:server-api.jar
test范围依赖:
- 对主程序是否有效:无效
- 对测试程序是否有效:有效
- 是否参与打包:不参与
- 是否参与部署:不参与
- 典型例子:junit
provided主要是针对web工程的。以前web开发,总是缺少server-api.jar,所以我们常去Tomcat的lib目录下拷过来使用。但是部署的时候会忽略,因为容器会提供,所以不会打包上去。故将 server-api.jar 设置为provided范围。
依赖传递&排除
当A工程依赖B工程,那么我们在给B工程导入其他依赖的话,A工程也会自动导入相同的依赖,这样依赖只用在pom.xml文件中导入一次,就比较方便。这就是依赖的传递性。
当然依赖的传递也不是绝对的,有些依赖就传递不过来。非compile范围的依赖不能传递。 这样我们也可以看出,provided和test范围就是给当前工程自己用的。所以在各个工程模块中,如果有需要就得重复声明依赖。
-----------------------------------------------------------华丽的分割线-----------------------------------------------------------
现在假设有这么个场景,B工程导入的依赖有可能是一个不稳定版,或对当前工程有不良影响。这时我们可以在引入 A 的时候将 B 排除。想要排除某个依赖,就在依赖的gav坐标中,使用 exclusions 标签,里面可以定义多个 exclusion。
比如在当前的mavenpro_b工程中,依赖了 mavenpro_c,mavenpro_c 又依赖了lombock。但是我mavenpro_b工程不想要lombok依赖,就可以使用哦 exclusions 标签进行排除。
排除前:
排除后:
依赖冲突的原则
之前说过,依赖存在传递性,当A依赖于B,而B又依赖于C,则A也会引入C的compile范围的依赖。
假设现在有这么一个场景,有三个工程,分别名为 mavenpro_a,mavenpro_b,mavenpro_c。
如上图所示,此时就发生依赖冲突了。mavenpro_a是依赖lombok 1.18.20还是 lombok 1.16.18呢?
一般来说选择版本较高的,因为版本较高的都会向下兼容。但是maven有自己的解决依赖版本问题的原则,就是就进优先,也就是路径最短者优先。那么怎么去看这个路径呢?
mavenpro_a到mavenpro_b再到lombok 1.16.18的路径短于 mavenpro_a到mavenpro_b到mavenpro_c再到lombok 1.18.20。故mavenpro_a最终会选择 lombok 1.16.18的依赖版本。
那么如果我就是想用 1.18.20 的版本的依赖呢?那就在该工程pom.xml中显示的声明即可。
-----------------------------------------------------------华丽的分割线-----------------------------------------------------------
现在换一个场景,假设还是三个工程 mavenpro_a,mavenpro_b,mavenpro_c。
mavenpro_a 依赖了 mavenpro_b和mavenpro_c,而在mavenpro_b和mavenpro_c都有lombok依赖,那么mavenpro_a 用谁的依赖呢?
此时从 mavenpro_a到lombok 1.16.18的路径和mavenpro_b到lombok 1.18.20的路径相等,该如何是好?
maven中规定,路径相同的时候,对依赖的取舍标准就叫做先声明者优先。 先声明指的是dependency标签的声明顺序。
依赖的统一管理
在最早学Spring的时候,导入依赖,我们都是根据Spring的模块按需导入。如:
当我们使用框架时,使用框架的一组jar包,最好都是版本一致的,毕竟不同版本之间总会有些改动。那么如果我想将所有的jar包版本都升级到 5.3.6 怎么办呢?最笨的办法就是手动去挨个修改,这样有多少个jar包就要修改多少次,这样并不可取。
推荐的方法就是,在 properties 标签内来自定义标签来统一版本号。这样我们只要修改了自定义标签内的值,那么其他地方凡是引用到了该自定义标签的值都会跟着修改。
properties标签配合自定义标签声明数据的配置并不是只能用于版本号管理,但是用的最多的是版本号管理。凡是需要统一声明后再引用的场合都可以使用。
继承
parent标签
假设现在有四个工程 mavenpro_a,mavenpro_b,mavenpro_c,mavenpro_d。mavenpro_a 、mavenpro_b 、mavenpro_c 都依赖mavenpro_d,此时如果mavenpro_d的版本发生变动,其他三个工程想要升级依赖版本,也要各自去修改,十分麻烦。
之前在单个工程中可以通过properties标签和自定义标签来统一依赖版本。那么多个工程之间如何统一呢? 就需要定义一个父工程来进行统一管理。
定义一个父工程 mavenpro_parent。在父工程中引入mavenpro_d 的依赖。
<?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>
<groupId>org.example</groupId>
<artifactId>mavenpro_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- 父工程打包类型一定是 pom -->
<properties>
<mavenpro_d.version>3.0-SNAPSHOT</mavenpro_d.version>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>mavenpro_d</artifactId>
<version>${mavenpro_d.version}</version>
</dependency>
</dependencies>
</project>
我们只需继承父工程,就能得到父工程中的compile范围的依赖。这样如果版本发生变动,我们只需要在父工程中修改即可,不需要变动子工程。
注意,父工程的打包类型一定要定义成 pom 类型 否则会报如下错误:
dependencyManagement
之前说过,我们在可以在工程中使用parent标签,这样就能获取到父工程中所有的依赖。假设现在有这么一个场景,还是四个工程 mavenpro_a,mavenpro_b,mavenpro_c,mavenpro_d以及一个mavenpro_parent父工程。此时mavenpro_a,mavenpro_b两个工程完全依赖父工程,而mavenpro_c只只需要父工程的部分依赖。那么该怎么办呢? 就是使用 dependencyManagement标签,可以按需导入。
此时父工程中有两个依赖
<?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>
<groupId>org.example</groupId>
<artifactId>mavenpro_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<mavenpro_d.version>3.0-SNAPSHOT</mavenpro_d.version>
<spring.aop.version>5.3.6</spring.aop.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>mavenpro_d</artifactId>
<version>${mavenpro_d.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.aop.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
此时我们再去看 mavenpro_c ,即使使用了parent标签,但是依赖全无。因为,parent标签中的依赖,maven会帮我们自动下载,而dependencyManagement标签中的依赖,maven不会帮我们下载,需要我们自己按需引入!。
于是我按需导入 aop 依赖
<?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>
<groupId>org.example</groupId>
<artifactId>mavenpro_c</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>mavenpro_parent</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../mavenpro_parent/pom.xml</relativePath> <!-- 父工程 pom.xml 的相对路径-->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
</dependencies>
</project>
可以看到,在导入aop依赖的时候,我并没有写版本号,这也是 dependencyManagement 标签带来的好处,我们在父工程中定义的依赖,子类在引入依赖时,可以不定义版本号,那么就默认的使用父工程的版本号。这称为maven的版本仲裁机制。
聚合
一个工程中的类想要被其它工程使用,那么我们需要将该工程打包成jar包的方式,被其它工程引用,即可使用。在maven使用 maven install 即可打包但装到我们的maven仓库中。
当工程渐渐变多,一个一个的maven install就变得繁琐起来,于是我们可以在父工程中通过 modules标签引入子工程,这样我们 maven install 父工程,则 modules 标签中的子工程也会被一键安装。
<?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>
<groupId>org.example</groupId>
<artifactId>mavenpro_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>../mavenpro_a</module> <!-- 工程的相对位置 -->
<module>../mavenpro_b</module>
</modules>
<properties>
<mavenpro_d.version>3.0-SNAPSHOT</mavenpro_d.version>
<spring.aop.version>5.3.6</spring.aop.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>mavenpro_d</artifactId>
<version>${mavenpro_d.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.aop.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>