目录
Maven的官网地址为:Maven – Welcome to Apache Maven。
类似于前端的npm工具,Maven是Apache基于ANT研发的一款开源Java项目管理和整合工具,它使用项目对象模型(Project Object Model,POM)的概念,将项目的开发和管理过程使用pom.xml文件的形式进行描述,并采用依赖、继承、聚合的关系描述一个Maven项目多个模块之间存在的关系。使用Maven可以更加便捷地导入jar依赖包、拆分项目等,实现分布式架构项目的开发。
类似于SpringBoot框架,“约定优于配置(Convention Over Configuration)”是Maven最核心的理念之一,Maven项目构建工程中,会自动构建默认的项目结构,因此,开发者在编写代码时,只需要将其方Maven项目中约定好的位置处即可保证项目的正常运行。
如下图所示,为一个Maven项目的结构截图,
通常,我们默认遵循如下的约定,
【1】scr/main:java目录下用于存放Java项目源文件,resources目录下用于存放项目的配置文件;
【2】src/test:java目录下用于存放Java测试的源文件,resources目录下用于存放测试时使用的配置文件;
【3】target目录:用于存放打包输出的文件,其中classes文件夹存放的是编译输出的*.class二进制代码和配置文件;
【4】pom.xml文件:用于描述整个项目结构,包括:项目的唯一标识、版本号、名称、依赖项、jdk编译插件、tomcat插件等等配置信息。
默认情况下,只要当前计算机上安装了IDEA开发工具,那么,它会内置一个Maven工具,而无需开发者再次下载。
Maven下载地址为:Maven – Download Apache Maven。
我们先来看一下Maven的目录结构,
接下来,可以通过配置环境变量的方式,将Maven工具提供的命令暴露为在系统的全局中可使用。
如下图所示,我们可以使用:maven -v命令,查看当前Maven的版本信息。
如下图所示,我们可以在IDEA开发工具的Maven配置窗口中,手动指定Maven的安装路径、配置文件settings.xml路径、本地仓库路径。
Maven仓库:是用于存储构件的仓库。
通俗地讲,构件,就是当前Maven项目的依赖。专业地讲:在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称为构件。
而Maven工具用于统一管理这些构件的存储路径,就称为“Maven仓库”.
任何的构件都有唯一的坐标,该坐标定义了构件在Maven仓库中的唯一存储路径。
当 Maven 项目需要某些构件时,只要其 POM 文件中声明了这些构件的坐标,Maven 就会根据这些坐标找自动到仓库中找到并使用它们。
如下图所示,就是pom.xml文件中用于描述构件的坐标信息。
世界上任何一个构件都可以使用 Maven 坐标并作为其唯一标识,Maven 坐标包括: groupId、artifactId、version、packaging 等元素。只要用户提供了正确的坐标元素,Maven 就能找到对应的构件。
Maven 坐标主要由以下元素组成:
①groupId: 项目组 ID,定义当前 Maven 项目隶属的组织或公司,通常是唯一的。它的取值一般是项目所属公司或组织的网址或 URL 的反写,例如 net.biancheng.www。
②artifactId: 项目 ID,通常是项目的名称。 version:版本。
③packaging:项目的打包方式,默认值为 jar。 以上 4 个元素中 groupId、artifactId 和 version 是必须定义的,packaging 是可选的。
Maven仓库可以分为两类:
①本地仓库:即当前计算机上用于存储构件的存储路径,以文件夹的形式存在。它会在第一次执行 Maven 命令时被创建,默认的存储路径被定义在maven/config/settings.xml文件中,通常是在C盘下。
`` D:/myRepository/repository
②远程仓库:可以分为3 个小类:中央仓库、私服、其他公共仓库,其中:
[1] 中央仓库:是由 Maven 社区提供的一种特殊的远程仓库,它包含了绝大多数流行的开源构件。在默认情况下,当本地仓库没有 Maven 所需的构件时,会首先尝试从中央仓库下载。
[2] 私服:是一种特殊的远程仓库,它通常设立在局域网内,用来代理所有外部的远程仓库。它的好处是可以节省带宽,比外部的远程仓库更加稳定。
[3] 其它公共仓库:除了中央仓库和私服外,还有很多其他公共仓库,例如 JBoss Maven 库,Java.net Maven 库等等。
在实际开发中,如果在Maven项目的pom.xml文件中制定了某个依赖的坐标信息,那么,Maven就会按照如下的顺序去查找对应的构件。
1- 从本地仓库查找构件,如果没有找到,则从中央仓库查找构件; 2- 从中央仓库查找构件,如果没有找到,并且已经设置其他远程仓库,然后在远程仓库查找构件;如果找到,那么将构件下载到本地仓库中使用。 3- 如果没有设置远程仓库,Maven 则会停止处理并抛出错误。
由于默认的本地仓库地址为:${user.home}/.m2/repository,但是,随着开发时间的累积,本地仓库存储的构件也会越来越多,造成C盘内存占用过多的问题。
为了避免这个问题,我们可以通过修改maven/config/settings.xml配置文件,来主动指定本地仓库的存储路径。
只需要修改settings.xml文件中的localRespository标签对的值即可,如下,我将本地仓库的路径指定为:F:\Java-dependencies\localMavenRespotitory。
<localRepository>F:\Java-dependencies\localMavenRespotitorylocalRepository>
如此,之后项目中使用到的第三方构件,都会被自动存储到本地仓库对应的路径下了。
另一方面,有时候,Apache默认托管的、在国外的中央仓库在使用时,通常存在下载慢的问题,我们可以将重新指定镜像仓库,来加快第三方构件的下载速度。
只需要继续在settings.xml文件中配置如下信息即可。
- <mirrors>
-
- <mirror>
- <id>aliyunid>
- <name>aliyunname>
- <mirrorOf>centralmirrorOf>
-
- <url>http://maven.aliyun.com/nexus/content/groups/public/url>
- mirror>
- mirrors>
如下所示,我们可以继续在settings.xml文件中的profiles节点下,配置jdk版本信息,来使得:Maven项目在启动时,默认使用对应版本的JDK。
- <profile>
-
- <id>jdk-1.8id>
-
- <activation>
- <activeByDefault>trueactiveByDefault>
- <jdk>1.8jdk>
- activation>
-
- <properties>
-
- <maven.compiler.source>1.8maven.compiler.source>
- <maven.compiler.target>1.8maven.compiler.target>
- <maven.compiler.compilerVersion>1.8maven.compiler.compilerVersion>
- properties>
- profile>
Maven工具创建的项目,可以基于pom.xml文件中的package标签进行配置,主要是分为三类:
①POM工程:表示当前项目是一个多模块聚合的项目.使用maven分模块管理,都会有一个父级项目,父级项目的pom文件设置packaging标签属性值必须为pom。
②JAR工程:如果未指定,则默认是jar包,通常是作为内部调用或者是做服务使用;
③WAR工程:如果当前项目需要被打成war包,然后部署到容器中启动时,需要将package标签的值设置为war(通常是将业务逻辑所在的模块设置为war)。
下面我们通过IDEA创建一个Maven聚合项目,并剖析各模块之间的关系。以下,仅贴出父、子级模块的pom.xml配置文件。
内容如下,基本解读如下:
【1】父级项目唯一标识符:通过groupId、artifactId、version,分别指定项目所在的工程组、项目名称、版本号;
【2】父级项目的名称name标签、描述信息description标签;
【3】当前父级项目的类型——pom工程:package标签值为pom;
【4】通过modules标签,指定两个子模块:business-业务逻辑模块、common-通用模块。
【5】properties标签,内部用于指定项目中所有依赖的版本信息;
【6】dependencyManagement标签:用于管理项目的依赖,仅作声明,不导致实际的下载引入,只有在子模块中再次声明对象的依赖时,才会向上找到父级项目的pom.xml文件中,找到${版本信息},然后再去下载对应的构件。
【7】repositories标签:指定了远程仓库的地址,保证:在本地仓库、镜像仓库中都无法找到构件时,为其提供可靠的构件仓库;
【8】build标签:用于配置和项目运行、打包相关的信息。分别配置了:JDK编译器插件、maven站点生成插件;以及,finalName指定了项目打包之后的文件名称。
- "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.0modelVersion>
-
-
- <groupId>com.xwdgroupId>
- <artifactId>maven_servletartifactId>
- <version>1.0.0version>
-
- <name>maven项目name>
- <description>基于Maven管理子模块description>
-
- <packaging>pompackaging>
-
-
- <modules>
- <module>businessmodule>
- <module>commonmodule>
- modules>
-
-
- <properties>
-
- <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
- <maven.compiler.source>1.8maven.compiler.source>
- <maven.compiler.target>1.8maven.compiler.target>
-
- <javax.servlet.version>3.1.0javax.servlet.version>
- <javax.servlet.jsp.version>2.3.3javax.servlet.jsp.version>
- <junit.version>4.11junit.version>
- <commons-dbutils.version>1.6commons-dbutils.version>
- <org.postgresql.version>42.3.3org.postgresql.version>
- <log4j.version>1.2.17log4j.version>
- <com.alibaba.fastjson2.version>2.0.12com.alibaba.fastjson2.version>
- <lombok.version>1.18.12lombok.version>
- <geotools.version>21.1geotools.version>
- properties>
-
- <dependencyManagement>
-
- <dependencies>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>javax.servlet-apiartifactId>
- <version>${javax.servlet.version}version>
- dependency>
-
- <dependency>
- <groupId>javax.servlet.jspgroupId>
- <artifactId>javax.servlet.jsp-apiartifactId>
- <version>${javax.servlet.jsp.version}version>
- dependency>
-
- <dependency>
- <groupId>junitgroupId>
- <artifactId>junitartifactId>
- <version>${junit.version}version>
- <scope>testscope>
- dependency>
-
- <dependency>
- <groupId>commons-dbutilsgroupId>
- <artifactId>commons-dbutilsartifactId>
- <version>${commons-dbutils.version}version>
- dependency>
-
- <dependency>
- <groupId>org.postgresqlgroupId>
- <artifactId>postgresqlartifactId>
- <version>${org.postgresql.version}version>
- dependency>
-
- <dependency>
- <groupId>log4jgroupId>
- <artifactId>log4jartifactId>
- <version>${log4j.version}version>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.fastjson2groupId>
- <artifactId>fastjson2artifactId>
- <version>${com.alibaba.fastjson2.version}version>
- dependency>
-
- <dependency>
- <groupId>org.projectlombokgroupId>
- <artifactId>lombokartifactId>
- <version>${lombok.version}version>
- <scope>providedscope>
- dependency>
-
- <dependency>
- <groupId>org.geotoolsgroupId>
- <artifactId>gt-geojsonartifactId>
- <version>${geotools.version}version>
- dependency>
- dependencies>
- dependencyManagement>
-
- <repositories>
- <repository>
- <id>osgeoid>
- <name>OSGeo Release Repositoryname>
- <url>https://repo.osgeo.org/repository/release/url>
- <snapshots><enabled>falseenabled>snapshots>
- <releases><enabled>trueenabled>releases>
- repository>
- <repository>
- <id>osgeo-snapshotid>
- <name>OSGeo Snapshot Repositoryname>
- <url>https://repo.osgeo.org/repository/snapshot/url>
- <snapshots><enabled>trueenabled>snapshots>
- <releases><enabled>falseenabled>releases>
- repository>
- repositories>
-
-
- <build>
-
- <finalName>maven_servletfinalName>
- <plugins>
-
- <plugin>
-
- <groupId>org.apache.maven.pluginsgroupId>
- <artifactId>maven-compiler-pluginartifactId>
- <version>3.2version>
-
- <configuration>
-
- <source>${maven.compiler.source}source>
-
- <target>${maven.compiler.target}target>
- configuration>
- plugin>
-
- <plugin>
- <groupId>org.apache.maven.pluginsgroupId>
- <artifactId>maven-site-pluginartifactId>
- <version>3.7.1version>
- plugin>
- plugins>
- build>
-
-
- project>
内容如下,解读如下,
【1】parent标签:声明了所要继承的父级项目(POM工程);
【2】指定子项目的唯一标识信息:通过groupId、artifactId、version,分别指定项目所在的工程组、项目名称、版本号;
【3】name:当前子模块的名称;
【4】dependencies标签:声明当前子模块所要依赖的构件,此出配置会导致下载构件;
- "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.0modelVersion>
-
-
- <parent>
- <artifactId>maven_servletartifactId>
- <groupId>com.xwdgroupId>
- <version>1.0.0version>
- parent>
-
-
- <artifactId>businessartifactId>
- <version>1.0-SNAPSHOTversion>
- <packaging>warpackaging>
-
- <name>servlet Maven Webappname>
-
-
-
- <dependencies>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>javax.servlet-apiartifactId>
- dependency>
-
- <dependency>
- <groupId>org.postgresqlgroupId>
- <artifactId>postgresqlartifactId>
- dependency>
-
- <dependency>
- <groupId>commons-dbutilsgroupId>
- <artifactId>commons-dbutilsartifactId>
- dependency>
-
- <dependency>
- <groupId>com.xwdgroupId>
- <artifactId>commonartifactId>
- <version>1.0.0version>
- <scope>compilescope>
- dependency>
- dependencies>
- project>
内容如下,解读如下,
【1】parent标签:声明了所要继承的父级项目(POM工程);
【2】指定子项目的唯一标识信息:通过groupId、artifactId、version,分别指定项目所在的工程组、项目名称、版本号;
【3】name:当前子模块的名称;
【4】dependencies标签:声明当前子模块所要依赖的构件,此出配置会导致下载构件;
- "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">
- <parent>
- <artifactId>maven_servletartifactId>
- <groupId>com.xwdgroupId>
- <version>1.0.0version>
- parent>
- <modelVersion>4.0.0modelVersion>
-
- <artifactId>commonartifactId>
-
- <dependencies>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>javax.servlet-apiartifactId>
- dependency>
-
- <dependency>
- <groupId>log4jgroupId>
- <artifactId>log4jartifactId>
- dependency>
-
- <dependency>
- <groupId>com.alibaba.fastjson2groupId>
- <artifactId>fastjson2artifactId>
- dependency>
-
- <dependency>
- <groupId>org.geotoolsgroupId>
- <artifactId>gt-geojsonartifactId>
- dependency>
- dependencies>
- project>
如果A工程的开发/运行过程中需要B工程提供支持,则代表A工程依赖B工程。
在这种情况下,需要在A工程的pom.xml文件中增加对B工程坐标的配置信息。
如上所示,business工程依赖于common工程,则在business工程的pom.xml文件中添加如下依赖配置信息,
Maven 的依赖传递机制是指:不管 Maven 项目存在多少间接依赖,POM 中都只需要定义其直接依赖,不必定义任何间接依赖,Maven 会动读取当前项目各个直接依赖的 POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。
以上案例中,父级POM工程、子级business工程、子级common工程之间的依赖关系如下,
可以看到,子级工程business同时获取到了来自POM工程、Common工程提供的servlet依赖,但是根据依赖传递特性,我们可以简化配置如下,使得子级工程business只接受来自Common工程提供的servlet依赖。
此时,再次测试后端接口,依旧可以正常运行。
【1】第一原则:最短路径优先原则
这意味着:“项目依赖关系树中路径最短的版本会被使用。
例如,假设A、B、C之间的依赖关系是A->B->C->D(2.0) 和A->E->(D1.0),那么D(1.0)会被使用,因为A通过E到D的路径更短。
【2】第二原则:最先声明原则
依赖路径长度是一样的的时候,第一原则不能解决所有问题,比如这样的依赖关系: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标签,来手动排除继承过来的依赖,例如:可以在business工程中,排除掉来自common工程的servlet依赖,而只使用来自父级POM工程提供的servlet依赖。配置如下,
依赖范围决定了:当前工程的pom.xml文件中声明的依赖的坐标 在什么情况下有效,什么情况下无效。
Maven 具有以下 6 中常见的依赖范围,如下表所示。
例如:lombok依赖、servlet依赖,都是仅在编译、测试阶段生效,而在实际运行时,由于:
①被Lombok注解修饰的Java类已经生成了getter、setter、constructor构造器,因此,也就无需lombok依赖的支持;
②在运行时,servlet依赖可以由外部Tomcat容器提供,因此,两者的依赖范围都是provided类型的。
Maven工程的继承关系,其实在上面已经提及了。例如:business工程、common工程,与POM工程之间,其实就是存在着一种继承关系。如下图所示,
以上所讲的一种继承关系,其实就是各个工程的pom.xml文件之间继承关系。
在实际的开发过程中,如果我们将要开发的工程由两个以上的模块的时候,每一个模块都是一个独立的功能集合。例如:某大学系统中拥有搜索平台、学习平台、考试平台等,那么,在开发的时候,每个平台都可以独立编译、测试、运行,这时候我们就可以创建一个聚合工程,用来将子平台组装成一个完整的系统。
例如:之前我们通过IDEA创建出来的,由business工程、common工程组成的项目,其实就类似于是一个聚合项目。
对于一个聚合项目,通常地,父级POM项目的pom.xml文件是用来做依赖及其版本管理、子级工程声明的;具体的子级工程来来承担具体的功能需求实现。
这样,就可以将一个大型项目,拆分为若干个小的子项目,分成子项目进行开发,每个子项目也都可以助力运行,从而完成整个项目的开发。
那么,我们如何打包和部署Maven聚合项目呢?这就要涉及到Maven工具的生命周期,和它为我们提供的一些基本命令。
项目构建的生命周期在很早以前就已经存在了,典型的场景就是:开发人员每天都在对项目进行清理,编译,测试及部署,其实这些都是项目构建的生命周期的组成部分,只是在Maven出现之前,没有一种统一的、清晰的规范。
Maven提供了一套高度规范化的生命周期规范,可以将项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建过程进行统一完整的描述。详细描述如下图所示,
但是Maven生命周期说到底还只是一套抽象的逻辑概念,具体落实到项目构建方面,还需要Maven插件的支持,即:通过调用Maven插件的插件目标,来完成一些项目构建过程中的所有工作。
那么什么是插件目标呢?
简单来讲,Maven插件是插件目标的集合.通常一个 Maven 插件能够实现多个功能,每一个功能都是一个插件目标,即 Maven 插件是插件目标的集合。如下图所示的,Maven生命周期中的所有功能,都作为一个插件目标存在。例如:clean作为一个插件目标,可以实现清空target目录;compile作为一个插件目标,可以实现编译项目的源代码。
既然项目构建过程中的所有任务,都是通过调用插件目标实现的,那么,我们如何来调用插件目标呢?
这时候就需要用到一些相关的Maven命令,使用 Maven 命令执行插件的目标,语法如下。
- mvn [插件名]:[目标名]
例如:调用clean目标插件执行清空target目录的操作,命令为,
mvn compiler:compile
其它的一些命令及其作用如下,
* clean:清空target目录 * package:根据单个子模块pom.xml配置文件指定的打包方式生成war包或者jar包 packing标签的取值有3种:war/jar/pom [1] 如果未指定,则默认是jar包,通常是作为内部调用或者是做服务使用; [2] 指定为war包,是当项目需要部署到容器中启动时,设置为war.通常是将业务逻辑所在的模块设置为war; [3] 指定为pom,表示当前项目是一个多模块聚合的项目.使用maven分模块管理,都会有一个父级项目,父级项目的pom文件设置packaging标签属性值必须为pom,一般来说所有的父级项目的packaging都为pom。 * compile:编译项目的源代码 * test:对项目进行运行测试 * install:在本地仓库生成仓库的安装包,可供其他项目引用,同时打包后的文件放到项目的target目录下
了解了以上内容之后,我们来尝试打包当前的Maven项目,
【1】首先执行清理target目录的命令,
mvn clean:clean
【2】其次执行编译Java源码的命令,
mvn compiler:compile
编译结果如下,此时,target目录下还未出现jar包或者war包。
【3】最后执行项目打包的命令,
我们继续执行如下命令,尝试获取最终可部署的war包、可作为单独依赖引入的jar包。
# 动态 web工程打 war包,Java工程打 jar 包。
mvn package
此时,target目录下就出现了对应的jar包或者war包。
在获取到war包之后,其实,Maven项目的部署,就转变成了war包的部署。那么:直接将其丢到Tomcat的webapps目录下即可完成部署。
可以在build节点下,做如下配置,
- <plugins>
-
- <plugin>
-
- <groupId>org.apache.maven.pluginsgroupId>
- <artifactId>maven-compiler-pluginartifactId>
- <version>3.2version>
-
- <configuration>
-
- <source>1.8source>
-
- <target>1.8target>
- configuration>
- plugin>
在某些计算机上,可能无法安装Tomcat,但是我们又有通过Tomcat运行Web项目的需求,这时候就可以使用Tomcat插件. 该插件可以帮助开发者:在本地不存在Tomcat的情况下,借助Tomcat插件完成Web应用的部署,而无需在计算机上安装Tomcat.
可以在build节点下,做如下配置,
-
- <plugin>
- <groupId>org.apache.tomcat.mavengroupId>
- <artifactId>tomcat7-maven-pluginartifactId>
- <version>2.2version>
- <configuration>
-
- <port>8123port>
-
- <path>/maven_serveltpath>
-
- <uriEncoding>UTF-8uriEncoding>
- configuration>
- plugin>
Maven不仅仅是一款项目构建和依赖管理工具,它还能够聚合项目信息,促进项目团队之间地交流. POM文件中可以包含项目的各种信息,例如:项目描述、SCM 地址、许可证信息,开发者信息等。
用户可以使用Maven提供的maven-site-plugin插件,让Maven生成一个Web站点,以站点的形式发布以上POM中包含的信息. maven-site-plugin插件配置如下,
-
- <plugin>
- <groupId>org.apache.maven.pluginsgroupId>
- <artifactId>maven-site-pluginartifactId>
- <version>3.7.1version>
- plugin>
Maven在打包时默认只将src/main/resources里的配置文件拷贝到项目中并做打包处理,放入classes目录下,而非resource目录下的配置文件在打包时不会添加到项目中。
如果想要将非resources目录下的配置文件也打包到项目中,就可以使用资源拷贝插件来实现这种需求。例如:想要将java目录下的mapper.xml文件打包到classes目录下,可以做如下配置,
- <build>
-
- <resources>
-
- <resource>
- <directory>src/main/resourcesdirectory>
- <includes>
- <include>**/*.propertiesinclude>
- <include>**/*.xmlinclude>
- includes>
- resource>
-
- <resource>
- <directory>src/main/javadirectory>
- <includes>
- <include>**/*.xmlinclude>
- includes>
- resource>
- resources>
- build>