groupId、artifactId、version必须定义,packaging可选,classifier不能直接定义
项目构建的文件名始于坐标相对应的,一般规则为artifactId-version[-classifier].packaging
Maven在编译、测试、运行(含打包)阶段中所需的依赖并不完全一致,依赖范围就是用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系。
范围 | classpath范围 | 例子 | 补充 |
---|---|---|---|
编译 compile |
编译、测试、运行 | spring-core | 默认依赖范围 |
供应 provided |
编译、测试 | servlet-api | 如果不显示指定该依赖范围,并且容器依赖的版本和maven依赖的版本不一致的话,可能会引起版本冲突,造成不良影响。 |
运行 runtime |
测试、运行 | JDBC驱动 | |
测试 test |
测试 | Junit | 编译主代码和运行项目时使无法使用此依赖 |
系统 system |
编译、测试 | 必须通过配置systemPath元素来显示指定依赖文件的路径,此类依赖不是由maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可移植,因此谨慎使用。systemPath可以引用环境变量 | |
导入 import |
不会对三种classpath产生影响,该依赖范围只能与dependencyManagement元素配合使用 将目标pom文件中dependencyManagement的配置导入合并到当前pom的dependencyManagement中。 |
Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中
第一直接依赖 \ 第二直接依赖 | compile | test | provided | runtime |
---|---|---|---|---|
compile | compile | runtime | ||
test | test | test | ||
provided | provided | provided | provided | |
runtime | runtime | runtime |
可能导致问题:依赖冲突。依赖的版本和实际使用的版本不一致,常见的表现有NoSuchMethodError,ClassNotFoundException 等。
解决方法:依赖排除
元素为true,标识当前依赖为可选依赖,该依赖不会得到传递
理想情况下,是不应该使用可选依赖的,用可选依赖的某一个原因是某一个项目实现了多个特性,这违反了单一职责性原则
元素声明排除依赖,可以包含一个或多个元素,因此可以排除一个或多个传递性依赖。
**声明的时候只需要groupId和artifactId,而不需要version元素。**因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖,即Maven解析后的依赖中,不可能出现groupId和artifactId相同,而version不同的两个依赖。
原因:同一项目下不同依赖的版本相同,避免重复
查看命令:
mvn dependency:list
查看命令:
mvn dependency:tree
查看命令:
mvn dependency:analyze
Used undeclared dependencies:项目中使用到,但没有显示声明的依赖。存在这潜在的危险,当直接依赖升级时,相关传递依赖的版本也可能发生变化,这种变化不易察觉,但是有可能导致当前项目出错
Unused declared dependencies:项目中未使用到,到显示声明的依赖。不能简单的直接删除,需要仔细分析,因为dependency:analyze只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖它就发现不了
排除依赖和可选依赖都能在项目中将间接依赖排除在外,但两者实现机制却完全不一样。
任何一个依赖、插件或者项目构建的输出,都可以称为构建。坐标和依赖时任何一个构件在Maven世界中的逻辑表示方式,而构件的物理表示方式是文件。
Maven仓库是统一存储所有Maven项目共享的构件的位置。实际的Maven项目将不再各自存储其依赖文件,只需要声明这些依赖的坐标,在需要的时候,Maven会自动根据坐标找到仓库中的构件并使用
仓库路径与坐标的大致对应关系为:groupId/artifactId/version/artifactId-version.packaging
仓库只有两类:本地仓库和远程仓库。
Maven寻找构件过程:
特殊的远程仓库:
目录地址:settings.xml中标签设置,默认为~/.m2/repository/
构件下载到本地仓库的方式:①从Maven远程仓库下载,②mvn install将本地项目安装到仓库中
安装Maven后,如果不执行任何Maven命令,本地仓库目录时不存在的。当执行第一条Maven命令后,Maven才会创建本地仓库
每个用户只有一个本地仓库,但是可以配置访问多个远程仓库
由于原始本地仓库是空的,Maven必须知道至少一个可用的远程仓库,才能在执行Maven命令的时候下载到需要的构件
中央仓库就是一个默认的远程仓库,Maven安装文件自带了中央仓库的配置(pom-4.0.0.xml)
一些无法从外部仓库下载到的构件也能从本地上传到私服上供局域网内的Maven用户使用
优势:
pom.xml中元素下,子元素声明一个或多个远程仓库。
元素对仓库进行唯一的标识,Maven自带的中央仓库使用的id是central,如果其他的仓库声明也使用了该id,就会覆盖中央仓库的配置。
元素指向了仓库的地址,一般来说,这个地址是基于http协议,可以直接在浏览器在打开地址浏览构件
和元素用来控制Maven对于发布版构件和快照版构件的下载。子元素为true表示会下载,false表示不会下载。
元素default表示仓库的布局是Maven2和Maven3的默认布局,而不是Maven1的布局
元素,用来配置从远程仓库检查更新的频率:
元素,配置检查 校验和 文件的策略:
settings.xml中配置,因为pom.xml会提交到代码仓库中,而settings.xml一般只会放在本地,比较安全
下子元素配置认证信息,**关键是元素,需要与POM中需要认证的repositore元素的id完全一致。**正是这个id将认证信息与仓库配置联系在了一起
POM中元素下和子元素,前者表示发布版构件的仓库,后者表示快照版的仓库。两个元素都需要配置id、name和url,id为该远程仓库的唯一标识,name是方便阅读,url是仓库地址
命令:
mvn clean deploy
Maven世界中,任何一个项目或构件都必须有自己的版本。
快照版本发布到私服的过程中,Maven会自动为构件打上时间戳,有了时间戳,Maven就能随时找到快照版构件的最新版本。
默认情况下,Maven每天检查一次快照版更新,可使用-U参数强制更新
快照版值应该在组织内部的项目或模块间依赖使用
RELEASE和LATEST分别对应仓库中该构件的最新发布版本和最新版本(含快照),不推荐使用这两种做法
Maven3不再支持在插件配置中使用RELEASE和LATEST,如果不设置插件版本,其效果和RELEASE一样。
maven-metadata.xml的元素包含两个子元素,分别代表了这一快照的时间戳和构建号,基于这两个元素可以得到该仓库中此快照的最新构建版本号。仓库元数据并不是永远正确的
settings.xml中元素下配置镜像。
值为被镜像的仓库id,如果镜像需要认证,则基于镜像id配置仓库认证
配置:
镜像常见做法是结合私服,使用私服代理任何外部的公共仓库
由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或停止服务时,Maven仍将无法访问被镜仓库,因而无法下载构件
Maven生命周期是抽象的,实际行为都由插件来完成。生命周期和插件两者协同工作,密不可分。
Maven声明周期就是为了对搜友的构建过程进行抽象和统一,几乎所有的项目的构建都能映射到一个声明周期上。
生命周期抽象了构建的各个步骤,定义了它们的次序,但是没有提供具体的实现。
每个构建步骤都可以绑定一个或者多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认步骤
Maven 有三个标准的生命周期,三套生命周期是相互独立的:
clean:项目清理的处理
default(或 build):项目部署的处理
site:项目站点文档创建的处理
每个生命周期包好一些阶段,这些阶段是有顺序的,并且后面的阶段依赖于前面的阶段,用户和Maven最直接的交互方式就是调用这些生命周期阶段
clean 生命周期包含3个阶段
这是 Maven 的主要生命周期,被用于构建应用,包括下面的 23 个阶段
生命周期阶段 | 描述 |
---|---|
validate(校验) | 校验项目是否正确并且所有必要的信息可以完成项目的构建过程。 |
initialize(初始化) | 初始化构建状态,比如设置属性值。 |
generate-sources(生成源代码) | 生成包含在编译阶段中的任何源代码。 |
process-sources(处理源代码) | 处理源代码,比如说,过滤任意值。 |
generate-resources(生成资源文件) | 生成将会包含在项目包中的资源文件。 |
process-resources (处理资源文件) | 复制和处理资源到目标目录,为打包阶段最好准备。 |
compile(编译) | 编译项目的源代码。 |
process-classes(处理类文件) | 处理编译生成的文件,比如说对Java class文件做字节码改善优化。 |
generate-test-sources(生成测试源代码) | 生成包含在编译阶段中的任何测试源代码。 |
process-test-sources(处理测试源代码) | 处理测试源代码,比如说,过滤任意值。 |
generate-test-resources(生成测试资源文件) | 为测试创建资源文件。 |
process-test-resources(处理测试资源文件) | 复制和处理测试资源到目标目录。 |
test-compile(编译测试源码) | 编译测试源代码到测试目标目录. |
process-test-classes(处理测试类文件) | 处理测试源码编译生成的文件。 |
test(测试) | 使用合适的单元测试框架运行测试(Juint是其中之一)。 |
prepare-package(准备打包) | 在实际打包之前,执行任何的必要的操作为打包做准备。 |
package(打包) | 将编译后的代码打包成可分发格式的文件,比如JAR、WAR或者EAR文件。 |
pre-integration-test(集成测试前) | 在执行集成测试前进行必要的动作。比如说,搭建需要的环境。 |
integration-test(集成测试) | 处理和部署项目到可以运行集成测试环境中。 |
post-integration-test(集成测试后) | 在执行集成测试完成后进行必要的动作。比如说,清理集成测试环境。 |
verify (验证) | 运行任意的检查来验证项目包有效且达到质量标准。 |
install(安装) | 安装项目包到本地仓库,这样项目包可以用作其他本地项目的依赖。 |
deploy(部署) | 将最终的项目包复制到远程仓库中与其他开发者和项目共享。 |
Maven Site 插件一般用来创建新的报告文档、部署站点等。包含4个阶段
pre-site:执行一些需要在生成站点文档之前完成的工作
site:生成项目的站点文档
post-site: 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy:将生成的站点文档部署到特定的服务器上
用于建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点,方便团队交流和发布项目信息
插件以独立的构件形式存在,Maven只会在需要的时候下载并使用构件。对于构件本身,为了能够复用代码,往往能够完成多个任务。
插件目标使用方式:mvn [plugin-name]:[goal-name]
mvn clean:clean
生命周期的阶段与插件的目标相互绑定,以完成某个具体的构建任务
Maven在核心为一些主要的生命周期阶段绑定了很多插件的目标,当用户通过命令行调用生命周期阶段的时候,对应插件 目标就会执行相应的任务。
生命周期与阶段与插件目标的绑定关系
项目的打包类型会影响构建的具体过程,因此,default生命周期的阶段与插件目标的绑定关系也由项目打包类型所决定。
POM中build元素下的plugins子元素中声明插件的使用。
下每个子元素元素可以用来配置执行一个任务。
**即使不通过phase元素配置生命周期阶段,插件目标也能够绑定到生命周期中去。**因为很多插件的目标在编写时已经定义了默认绑定阶段。
当生命周期绑定到不同的生命周期阶段的时候,执行顺序由生命周期阶段的先后顺序决定。当多个插件目标绑定到同一个阶段时,这些插件声明的先后顺序决定了目标的执行顺序
插件目标都支持从命令行配置,通过参数-D参数键=参数值的形式。参数-D是java自带的,其功能是通过命令行设置一个java系统属性。Maven简单的使用了该参数,在准备插件的时候检查系统属性,便实现了插件参数的配置。
在POM中可以一次性配置全局参数。同时还可以为某个插件任务配置特定的参数,通过对绑定到的多个生命周期阶段配上不同的配置,就可以让Maven在不同的生命周期阶段执行不同的任务。
配置特定任务的配置,而非插件整体的配置。
命令行参数是由插件参数的表达式(Expression)决定的,并不是所有插件目标参数都有表达式,一些插件目标参数只能在POM中配置
通过maven-help-plugin插件能够获取插件的详细信息
# 获取插件信息。结果中最重要的是目标前缀(Goal Prefix),其作用是方便在命令行直接运行插件
mvn help:describe -Dplugin=groupId:artifactId:version
# 可以省略版本信息,自动获取最新版本
mvn help:describe -Dplugin=groupId:artifactId
# 可以使用插件前缀替换坐标
mvn help:describe -Dplugin=goalPrefix
# 添加goal参数,仅获取某个插件目标的信息
mvn help:describe -Dplugin=goalPrefix -Dgoal=goalName
# 添加detail参数,获取更加详细的信息
mvn help:describe -Dplugin=goalPrefix -Ddetail
Maven支持这种方式是因为有些任务不适合绑定到生命周期上。例如maven-help-plugin:describe
为了达到命令间接的目的,Maven引入了目标前缀的概念。通过目标前缀,Maven就能找到对应的artifactId
为了方便用户使用和配置插件,Maven不需要用户提供完整的插件坐标信息,就可以解析得到正确的插件。
POM中元素下元素配置插件仓库信息。
子元素同依赖仓库。
在POM配置插件的时候,如果该插件时Maven的官方插件(即groupId时org.apache.maven.plugins),可以省略groupId的配置。但不推荐这一机制
在用户没有提供插件版本的情况下,Maven会自动解析插件版本。
不推荐依赖Maven解析插件版本
插件前缀与groupId、artifactId时一一对应的,这种匹配关系存储在仓库元数据中。groupId/maven-metadata.xml
Maven在解析插件仓库元数据的时候,会默认使用org.apache.maven.plugin和org.codehaus.mojo两个groupId,可以通过配置settgins.xml()让Maven检查其他groupId上的插件仓库元数据
Maven的聚合特性能够把项目的各个模块聚合在一起构建,Maven的继承特性能帮助抽取各模块相同的依赖和插件等配置,在简化POM的同时,还能促进各个模块配置的一致性
对于聚合模块,其打包方式packaging的值必须为pom,否则就无法构建
元素时聚合的最核心的配置,用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的元素来实现模块的聚合,这里每个model的值都是一个当前POM的相对目录
一般来说,为了方便快速定位内容,模块所处的目录名称应当与其artifactId一致。
通常将聚合模块放在项目目录的最顶层,其他模块则为聚合模块的子目录存在。聚合模块与其他模块的目录结构并非一定要是父子关系
聚合模块仅仅是帮助聚合其他模块构建的工具,它本身并无实质的内容。
Maven首先会解析聚合模块的POM,分析要构建的模块,并计算出一个反应堆构建顺序(Reactor Build Order),然后根据这个顺序一次构建各个模块
Maven构建输出中显示的是各个模块的名字,而不是artifactId,为了让Maven的构建输出更清晰,应该在POM中配置合理的name字段
在父POM中声明一些配置供子类POM继承,以实现“一处声明,多处使用”的目的
父模块POM的打包方式也必须是pom,由于父模块只是为了帮助消除配置的重复,因此它本身不包含除了POM之外的项目文件。
元素声明父模块,元素下指定了父模块的坐标,这三个元素是必须的。表示父模块POM的相对路径
子模块隐式的从父模块继承了groupId和version两个元素
可继承的POM元素:
Maven提供的dependencyManagement元素既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下的依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用
完整的依赖声明在父POM中,子模块只需要配置简单的groupId和artifactId就能获得对应的依赖信息,从而引入正确的依赖
dependencyManagement元素下通过import依赖范围,可以将目标POM中的dependencyManagement配置搭导入并合并到当前POM的dependencyManagement元素中。import范围依赖由于其特殊性,一般都是只想打包类型为pom的模块
pluginManagement元素管理插件
聚合与继承是两个概念,其目的完全不同
为了方便,一个POM往往既是聚合POM,又是父POM
原因:使用约定可以大量减少配置
常见约定:
遵循约定虽然损失了一定的灵活性,但用户不能随意安排目录结构,但是却能大大减少配置
任何一个Maven项目都隐式的继承自该POM,Maven所提倡的约定都是在超级POM中配置
超级POM在maven-model-builder-x.x.x.jar的org/apache/maven/model/pom-4.0.0.xml路径下
Maven设定核心插件的原因是防止由于某些插件版本的变化而造成构建不稳定
反应堆(Reactor)是值所有模块组成的一个构建结构。对于多模块项目来说,反应堆就包含了个模块之间继承与依赖的关系,从而能够自动计算出合理的模块构建顺序。
Maven按顺序读取POM,如果该POM没有依赖模块,那么就构建该模块,否则就先构建其依赖模块,如果该依赖模块还依赖于其他模块,则进一步先构建该依赖的依赖
模块间的依赖关系会将反应堆构成一个有向非循环图(Directed Acyclic Graph,DAG),各个模块是该图的节点,依赖关系构成了有向边。这个图不允许出现循环
Maven提供了很多命令选项支持裁剪反应堆:
灵活运用上述四个参数,可以帮助我们跳过无需构建的模块,从而加速构建。在项目庞大,模块特别多的时候,这种效果就会异常明显。
maven-surefire-plugin的test目标会自动执行测试代码路径(默认为src/main/test)下所有符合一组命名模式的测试类
可通过命令行中加入参数skipTests参数跳过测试。maven.test.skip参数同时控制了maven-compiler-plugin和maven-surefire-plugin插件,跳过了测试代码编译、执行(两个插件的命令行表达式都是maven.test.skip)。
test参数指定要运行的测试用例的类名
test参数的值必须匹配一个或多个测试类,否则会报错并导致构建错误。可以加上failIfNoTest=false参数,告诉maven-surefire-plugin插件即使没有测试也不要报错
maven-surefire-plugin允许用户通过配置自定义包含一些其他测试类,或排除一些符合默认命名模式的测试类。
元素下元素配置要包含的其他测试类
元素下元素配置要包含的其他测试类
maven-surefire-plugin会在项目的target/surefire-reports目录下生成连各种格式的错误报告:
cobertura-maven-plugin插件与Coberture工具集成,用户可以为Maven项目生成测试覆盖率报告。文件生成在target/site/cobertura目录下。
mvn cobertura:cobertura
TestNG在JUnit基础上增加了很多特性。允许用户使用一个名为testng.xml的文件来配置想要运行的测试集合。
TestNG相较于JUnit的一大优势在于它支持测试组的概念
Maven为了支持构建的灵活性,内置了三大特性:属性、Profile、资源过滤
Maven有6类属性
Maven属性默认只有在POM中才会被解析。
资源文件的处理起始时maven-resources-plugin做的事情,它的默认行为只是将项目主资源文件复制到代码编译输出目录中,将测试资源文件复制到测试代码编译输出目录中。通过开启资源过滤,就能够解析资源文件中的Maven属性了
Maven默认的住主资源目录和测试资源目录的定义都是在超级POM中,因此只要在此基础上加上配置即可
为了能让构建在各个环境下方便移植,Maven引入了profile概念。profile能够在构建的时候修改POM的一个子集,或者添加额外的配置元素。
还可以用来分离构建的一些比较耗时或者耗资源的行为,并给予更合适的构建频率
# 查看当前激活的profile
mvn help:active-profiles
# 查看当前所有的profiles
mvn help:all-profiles
与一般资源文件一样,web资源文件默认不会被过滤。开启一般资源文件的过滤也不会影响到web资源文件。
maven-war-plugin插件可对src/main/webapp资源目录开启过滤
maven-site-plugin插件用于站点生成,在target/site目录下能够找到Maven生成的站点文件。
# 通过stage目标,将站点预发布至某个本地临时目录下
mvn site:stage -DstagingDirectory=D:\tmp
默认情况下,Maven生成的站点包含了很多项目信息连接,这是由maven-project-info-reports-plugin插件生成的,该插件内置在maven-site-plugin插件中。该插件会基于POM配置生成以下信息报告:
Maven不会凭空生成信息,只有用户在POM中提供了相关配置后,站点才有可能包含这些信息的报告
Maven3中需要在maven-site-plugin插件的元素下配置
src/site/site.xml中配置站点外观,所有站点使用的本地web资源都必须位于src/site/resources目录下
那么属性配置标题
配置站点头部左侧,默认显示项目名称
配置项目站点头部右侧
最新发布版本
最近发布时间
面包屑导航
自定义站点皮肤:
Maven官方提供了三款皮肤:
默认页面左边的边栏只会显示包含项目信息报告和其他报告的菜单
可通过在下
子元素加入自定义菜单。站点描述符中的Maven属性会被自动解析至对应的值,ref用来引用Maven站点默认生成的页面
Maven支持比较号的两种文档格式为APT(类似基于维基的文档格式,所有APT文档必须位于src/site/apt目录下)和FML(用来创建FAQ页面的XML文档格式,FML文档位于arc/site/fml目录下)
https://maven.apache.org/plugin-developers/index.html
Maven插件项目的POM特殊地方:
每个插件目标类都必须继承AbstractMojo并实现execute()方法,只有这样Maven才能识别该插件目标,并执行execute()方法中的行为。
每个Mojo类都必须使用@goal标注写明自己的目标名称
Mojo和Parameter注解建议查看对应接口类,防止版本变动
@Mojo注解标记当前类为Mojo类
per-lookup
和singleton
),默认per-lookup
once-per-session
or always
. 默认once-per-session
@Parameter将Mojo某个字段标注为可配置的参数,Maven支持种类多样的Mojo参数,包括单值的boolean、int、float、String、Date、File和URL,多值的数组、Collection、Map、Properties等
@Parameter标注提供了一些额外的属性,进一步自定义Mojo参数:
MojoFailureException异常,会显示“BUILD_FAILURE”的错误信息,表示在运行时发现了预期的错误
MojoExecutationException异常,会显示“BUILD_ERROR”的错误信息,表示在运行时发现了未预期的错误
AbstractMojo提供了一个getLog()方法,用户可以使用该方法获得一个Log对象,支持四种日志级别:debug、info、warn、error
Maven社区有一个用来帮助插件继承测试的插件,maven-invoker-plugin
最重要的是archetype-metadata.xml描述符文件:1. 声明那些目录及文件应该包含在Archetype中,2. 这个Archetype使用那些属性参数
gradle基于groovy,灵活。https://docs.gradle.org/current/userguide/userguide.html
maven基于xml,标准