Maven工具基于POM(Project Object Model)项目对象模型模式实现,在Maven中每个项目都相当于是一个对象,对象(项目)和对象(项目)之间是有关系的,关系包含了:依赖、继承和聚合。通过Maven可以更加方便的实现导包、拆分项目等效果。

1、依赖

 A工程在开发或运行的过程中需要B工程提供支持,这就叫A工程依赖B工程。在这种情况下,需要在A工程的pom.xml文件中增加配置定义依赖关系,通俗理解就是在A中导入B工程jar包。B工程既可以是自己的项目打的jar包,也可以是中央仓库的jar包。

如何在项目中注入依赖?

在pom.xml文件:根元素project下的 dependencies标签中,配置依赖信息,标签内可以包含多个 dependence元素,以声明多个依赖。每个依赖dependence标签都应该包含以下元素:groupId, artifactId, version : 依赖的基本坐标, 对于任何一个依赖来说,基本坐标是最重要的, Maven根据坐标才能找到需要的依赖。

创建一个Maven项目projectA

maven管理依赖版本 maven 依赖_java

从中央仓库中复制需要导入的包(中央仓库地址:https://mvnrepository.com/

maven管理依赖版本 maven 依赖_java_02

不仅省去了程序员手动添加jar包的操作,还可以解决jar包冲突问题

maven管理依赖版本 maven 依赖_java_03

依赖的传递性

传递性依赖是Maven2.0的新特性。假设你的项目依赖于一个库,而这个库又依赖于其他库。你不必自己去找出所有这些依赖,你只需要加上你直接依赖的库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。这个特性是靠解析从远程仓库中获取的依赖库的项目文件实现的。一般的,这些项目的所有依赖都会加入到项目中,或者从父项目继承,或者通过传递性依赖。也就是说如果A依赖了B,那么C依赖A时会自动把A和B都导入进来。

再创建一个Maven项目projectB

maven管理依赖版本 maven 依赖_maven_04

先将projectA执行打包安装命令:mvn clean package install

maven管理依赖版本 maven 依赖_传递性_05

然后让projectB依赖projectA,也就是将projectA的坐标注入进projectB的pom.xml文件中

maven管理依赖版本 maven 依赖_maven管理依赖版本_06

可以看到projectA已经自动导入进projectB中,而且projectA依赖的fastjson2也同步导入进projectB中,这也证明了Maven工程之间依赖的传递性。

依赖的两个原则

第一原则:最短路径优先原则
“最短路径优先”意味着项目依赖关系树中路径最短的版本会被使用。例如,假设A、B、C之间的依赖关系是A->B->C->D(2.0)  和A->E->(D1.0),那么D(1.0)会被使用,因为A通过E到D的路径更短。

第二原则:最先声明原则
依赖路径长度是一样的的时候,第一原则不能解决所有问题,比如这样的依赖关系:A–>B–>Y(1.0),A–>C–>Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。那么到底谁会被解析使用呢?在maven2.0.8及之前的版本中,这是不确定的,但是maven2.0.9开始,为了尽可能避免构建的不确定性,maven定义了依赖调解的第二原则:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜。

排除依赖

exclusions: 用来排除传递性依赖 其中可配置多个exclusion标签,每个exclusion标签里面对应的有groupId, artifactId, version三项基本元素,注意:不用写版本号。简单说起来就是projectB可以通过exclusions标签将projectA中的fastjson2排除掉,仅仅只依赖projectA自身。

maven管理依赖版本 maven 依赖_传递性_07

依赖范围

依赖范围就决定了依赖的坐标在什么情况下有效,什么情况下无效:

❀compile
这是默认范围。如果没有指定,就会使用该依赖范围。表示该依赖在编译和运行时都生效。

❀provided
已提供依赖范围。使用此依赖范围的Maven依赖。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍(如:servlet-api)

❀runtime
runtime范围表明编译时不需要生效,而只在运行时生效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

❀system
系统范围与provided类似,不过你必须显式指定一个本地系统路径的Jar包,此类依赖应该一直有效,Maven也不会去仓库中寻找它。但是,使用system范围依赖时必须通过systemPath元素显式地指定依赖文件的路径。

❀test
test范围表明使用此依赖范围的依赖,只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。典型的例子就是JUnit,它只有在编译测试代码及运行测试的时候才需要。Junit的jar包就在测试阶段用就行了,你导出项目的时候没有必要把junit的东西到处去了就,所在在junit坐标下加入scope-test

❀Import
import范围只适用于pom文件中的<dependencyManagement>部分。表明指定的POM必须使用<dependencyManagement>部分的依赖。
注意:import只能用在dependencyManagement的scope里。

2、继承

如果projectB继承projectA,则代表projectB默认依赖projectA依赖的所有资源,且可以应用projectA中定义的所有资源信息,而被继承的工程(B工程)只能是POM工程。注意,在父项目中放在<dependencyManagement>中的内容时不被子项目继承,不可以直接使用。放在<dependencyManagement>中的内容主要目的是进行版本管理。里面的内容在子项目中依赖时坐标只需要填写<group id>和<artifact id>即可,如果子项目不希望使用父项目的版本,可以明确配置version。

一般来讲,企业级的应用中会定义一个父工程来专门做依赖jar包的管理,例如将projectA作为父工程来进行jar包的管理:

maven管理依赖版本 maven 依赖_传递性_08

此时可以看到projectA只是用来管理jar包,自身其实没有需要用到jar包,所以不会将管理的jar包进行导入 。然后再将projectB定义为子工程:

maven管理依赖版本 maven 依赖_java_09

子工程通过parent标签指向父工程,就可以直接将父工程中定义好的jar包的版本拿来使用。但是此时作为子工程来讲,jar包的版本可用可不用父工程中管理的。针对此情况,父工程可用使用import范围来强制指定子工程必须使用父工程中管理的版本,不可自作主张随意使用版本。

maven管理依赖版本 maven 依赖_java_10

3、聚合

当我们开发的工程拥有2个以上模块的时候,每个模块都是一个独立的功能集合。比如某大学系统中拥有搜索平台,学习平台,考试平台等。开发的时候每个平台都可以独立编译,测试,运行。这个时候我们就需要一个聚合工程。在创建聚合工程的过程中,总的工程必须是一个POM工程(Maven Project)(聚合项目必须是一个pom类型的项目,jar项目war项目是没有办法做聚合工程的),各子模块可以是任意类型模块(Maven Module)。

聚合包含了继承的特性,聚合时多个项目的本质还是一个项目。这些项目被一个大的父项目包含。且这时父项目类型为pom类型。同时在父项目的pom.xml中出现<modules>表示包含的所有子模块。

先创建一个父工程作为总工程来管理jar包

maven管理依赖版本 maven 依赖_java_11

maven管理依赖版本 maven 依赖_maven管理依赖版本_12

再在父工程下创建一个子模块

maven管理依赖版本 maven 依赖_maven管理依赖版本_13

maven管理依赖版本 maven 依赖_jar包_14

此时moduleA和parent是继承的关系,这个整体就是聚合的关系

maven管理依赖版本 maven 依赖_maven_15

4、Maven中的插件

资源拷贝插件

Maven在打包时默认只将src/main/resources里的配置文件拷贝到项目中并做打包处理,而非resource目录下的配置文件在打包时不会添加到项目中。配置文件,一般都放在src/main/resources,打包后配置文件就会在target的classes下面放着。想把非resources下面的文件也打包到classes下面,就需要在pom文件中配置:


<build>    <resources>        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
                <include>**/*.properties</include>
            </includes>
        </resource>
    </resources>
</build>


maven管理依赖版本 maven 依赖_maven管理依赖版本_16

Tomcat插件

使用Tomcat插件发布部署并执行war工程的时候,需要使用启动命令,启动命令为:tomcat7:run。命令中的tomcat7是插件命名,由插件提供商决,run为插件中的具体功能。值得注意的是,之前用的编译器插件,资源拷贝插件,不是可运行的插件,maven直接帮我们运行了,但是tomcat属于可运行插件,它什么时候工作需要程序员通过命令来运行控制。


<build>    <plugins>        <!-- 配置Tomcat插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <!-- 配置Tomcat监听端口 -->
                <port>8080</port>
                <!-- 配置项目的访问路径(Application Context) -->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>