Maven 翻译为"专家"、“内行”,是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。 Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。 Maven 也可被用于构建和管理各种项目,例如 C#,Ruby,Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目,现为由 Apache 软件基金会主持的独立 Apache 项目。
Maven
Maven POM
POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。
执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。
POM 中可以指定以下配置:
- 项目依赖
- 插件
- 执行目标
- 项目构建 profile
- 项目版本
- 项目开发者列表
- 相关邮件列表信息
构建配置文件
构建配置文件是一系列的配置项的值,可以用来设置或者覆盖 Maven 构建默认值。
使用构建配置文件,你可以为不同的环境,比如说生产环境(Production)和开发(Development)环境,定制构建方式。
配置文件在 pom.xml 文件中使用 activeProfiles 或者 profiles 元素指定,并且可以通过各种方式触发。配置文件在构建时修改 POM,并且用来给参数设定不同的目标环境(比如说,开发(Development)、测试(Testing)和生产环境(Production)中数据库服务器的地址)。
构建配置文件的类型
构建配置文件大体上有三种类型:
类型 | 在哪定义 |
---|---|
项目级(Per Project) | 定义在项目的POM文件pom.xml中 |
用户级 (Per User) | 定义在Maven的设置xml文件中 (%USER_HOME%/.m2/settings.xml) |
全局(Global) | 定义在 Maven 全局的设置 xml 文件中 (%M2_HOME%/conf/settings.xml) |
Maven构建生命周期
Maven 构建生命周期定义了一个项目构建跟发布的过程。
一个典型的 Maven 构建(build)生命周期是由以下几个阶段的序列组成的:
阶段 | 处理 | 描述 |
---|---|---|
验证 validate | 验证项目 | 验证项目是否正确且所有必须信息是可用的 |
编译 compile | 执行编译 | 源代码编译在此阶段完成 |
测试 Test | 测试 | 使用适当的单元测试框架(例如JUnit)运行测试。 |
包装 package | 打包 | 创建JAR/WAR包如在 pom.xml 中定义提及的包 |
检查 verify | 检查 | 对集成测试的结果进行检查,以保证质量达标 |
安装 install | 安装 | 安装打包的项目到本地仓库,以供其他项目使用 |
部署 deploy | 部署 | 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程 |
为了完成 default 生命周期,这些阶段(包括其他未在上面罗列的生命周期阶段)将被按顺序地执行。
Maven 有以下三个标准的生命周期:
- clean:项目清理的处理
- default(或 build):项目部署的处理
- site:项目站点文档创建的处理
Maven插件
每个生命周期中都包含着一系列的阶段(phase)。这些 phase 就相当于 Maven 提供的统一的接口,然后这些 phase 的实现由 Maven 的插件来完成。
我们在输入 mvn 命令的时候 比如 mvn clean,clean 对应的就是 Clean 生命周期中的 clean 阶段。但是 clean 的具体操作是由 maven-clean-plugin 来实现的。
所以说 Maven 生命周期的每一个阶段的具体实现都是由 Maven 插件实现的。
Maven 实际上是一个依赖插件执行的框架,每个任务实际上是由插件完成。Maven 插件通常被用来:
- 创建 jar 文件
- 创建 war 文件
- 编译代码文件
- 代码单元测试
- 创建工程文档
- 创建工程报告
插件通常提供了一个目标的集合,并且可以使用下面的语法执行:
|
|
例如,一个 Java 工程可以使用 maven-compiler-plugin 的 compile-goal 编译,使用以下命令:
|
|
插件类型
Maven 提供了下面两种类型的插件:
类型 | 描述 |
---|---|
Build plugins | 在构建时执行,并在 pom.xml 的 元素中配置。 |
Reporting plugins | 在网站生成过程中执行,并在 pom.xml 的 元素中配置。 |
下面是一些常用插件的列表:
插件 | 描述 |
---|---|
clean | 构建之后清理目标文件。删除目标目录。 |
compiler | 编译 Java 源文件。 |
surefile | 运行 JUnit 单元测试。创建测试报告。 |
jar | 从当前工程中构建 JAR 文件。 |
war | 从当前工程中构建 WAR 文件。 |
javadoc | 为工程生成 Javadoc。 |
antrun | 从构建过程的任意一个阶段中运行一个 ant 任务的集合。 |
Maven仓库
在 Maven 的术语中,仓库是一个位置(place)。
Maven 仓库是项目中依赖的第三方库,这个库所在的位置叫做仓库。
在 Maven 中,任何一个依赖、插件或者项目构建的输出,都可以称之为构件。
Maven 仓库能帮助我们管理构件(主要是JAR),它就是放置所有JAR文件(WAR,ZIP,POM等等)的地方。
Maven 仓库有三种类型:
-
本地(local)
-
中央(central)
-
远程(remote)
Maven 依赖搜索顺序
当我们执行 Maven 构建命令时,Maven 开始按照以下顺序查找依赖的库:
- 步骤 1 - 在本地仓库中搜索,如果找不到,执行步骤 2,如果找到了则执行其他操作。
- 步骤 2 - 在中央仓库中搜索,如果找不到,并且有一个或多个远程仓库已经设置,则执行步骤 4,如果找到了则下载到本地仓库中以备将来引用。
- 步骤 3 - 如果远程仓库没有被设置,Maven 将简单的停滞处理并抛出错误(无法找到依赖的文件)。
- 步骤 4 - 在一个或多个远程仓库中搜索依赖的文件,如果找到则下载到本地仓库以备将来引用,否则 Maven 将停止处理并抛出错误(无法找到依赖的文件)。
Maven构建Java项目&Web 应用
Maven 使用原型 archetype 插件创建项目。要创建一个简单的 Java 应用,我们将使用 maven-archetype-quickstart 插件。
在下面的例子中,我们将在 C:\MVN 文件夹下创建一个基于 maven 的 java 应用项目。
命令格式如下:
|
|
参数说明:
-
-DgroupId: 组织名,公司网址的反写 + 项目名称
-
-DartifactId: 项目名-模块名
-
-DarchetypeArtifactId: 指定 ArchetypeId,maven-archetype-quickstart,创建一个简单的 Java 应用
-
-DinteractiveMode: 是否使用交互模式
我们可以使用 maven-archetype-webapp 插件来创建一个简单的 Java web 应用。
打开命令控制台,进入到 C:\MVN 文件夹,然后执行以下的 mvn 命令:
|
|
命令太长,记不住,只输入基本命令mvn archetype:generate
用默认模板即可,在交互状态下第一步选模板,直接回车默认模板即可,接着输入groupId和artifactId(项目名)、版本号即可,如果需要其它,例如web项目,根据文章末尾的约定文件夹,手动创建即可,扔掉笨拙的Ide吧(当然不可能)。
Maven项目测试
maven本身没有单元测试框架,但是maven的default生命周期的test阶段绑定了maven-surefire-plugin插件,该插件可以调用Junit3、Junit4、TestNG等Java流行测试框架完成单元测试。在pom中默认加入Junit依赖。
约定优于配置
maven的default生命周期的test阶段与maven-surefire-plugin插件的test插件目标内置绑定。默认情况下,maven-surefire-plugin的test目标会自动执行测试源码路径(默认:src/test/java)下的所有符合一组命名规则的测试类。该命名规则如下:
|
|
动态指定要执行的测试用例
使用test参数可以在命令行指定要执行的测试用例的类名。
|
|
注意:当test参数没有值时,默认构建会失败。此时加上failIfNoTests=false,即使没有测试也不报错:
|
|
上述命令不会执行任何测试,构建也能顺利完成。这也是一种跳过所有测试完成构建的方法。
包含与排除测试用例
pom中可以配置includes和excludes参数来包含和排除指定的测试类:
|
|
跳过测试
pom中配置
- skipTests配置,跳过测试执行阶段
|
|
- skip配置,跳过测试执行阶段
|
|
mvn命令行中加入参数
- skipTests
|
|
- maven.test.skip(需要注意的是,maven-compiler-plugin插件的testCompile插件目标和maven-resources-plugin插件的testResources插件目标也有这个参数,如果使用-Dmaven.skip.test=true,则跳过了测试资源文件处理、测试代码编译和执行三个阶段。关于maven的生命周期和插件的详细描述,请参考我的另两篇博客:maven生命周期详解和Maven插件详解)
|
|
说明:此处的“maven.test.skip”为maven-surefire-plugin插件的test插件目标的参数的表达式,其对应的插件目标参数为上面pom中配置的skip参数。并不是所有插件目标参数都有表达式,也就是说,一些插件目标参数只能在pom中配置。关于插件目标参数和其表达式的详细信息,可以去maven官网的对应插件章查询,地址为:maven官网插件
Maven项目文档
运行 mvn site
可以生成项目文档,位置在**\target\site** 文件夹下,点击 index.html 就可以看到文档了。
令时出现 java.lang.NoClassDefFoundError: org/apache/maven/doxia/siterenderer/DocumentContent 的问题, 这是由于 maven-site-plugin 版本过低,升级到 3.3+ 即可。
Maven 快照(SNAPSHOT)
快照是一种特殊的版本,指定了某个当前的开发进度的副本。不同于常规的版本,Maven 每次构建都会在远程仓库中检查新的快照。 现在 data-service 团队会每次发布更新代码的快照到仓库中,比如说 data-service:1.0-SNAPSHOT 来替代旧的快照 jar 包。
对于版本,如果 Maven 以前下载过指定的版本文件,比如说 data-service:1.0,Maven 将不会再从仓库下载新的可用的 1.0 文件。若要下载更新的代码,data-service 的版本需要升到1.1。
快照的情况下,每次 app-ui 团队构建他们的项目时,Maven 将自动获取最新的快照(data-service:1.0-SNAPSHOT)。
虽然,快照的情况下,Maven 在日常工作中会自动获取最新的快照, 你也可以在任何 maven 命令中使用 -U 参数强制 maven 下载最新的快照构建。
|
|
Maven 自动化构建
在一个项目成功构建完成后,其相关的依赖工程即开始构建,这样可以保证其依赖项目的稳定。比如一个团队正在开发一个项目 bus-core-api, 并且有其他两个项目 app-web-ui 和 app-desktop-ui 依赖于这个项目。
现在 app-web-ui 和 app-desktop-ui 项目的团队要求不管 bus-core-api 项目何时变化,他们的构建过程都应当可以启动。使用快照可以确保最新的 bus-core-api 项目被使用,但要达到上面的要求,我们还需要做一些额外的工作。
可以使用两种方式:
- 在 bus-core-api 项目的 pom 文件中添加一个 post-build 目标操作来启动 app-web-ui 和 app-desktop-ui 项目的构建。
- 使用持续集成(CI) 服务器,比如 Hudson,来自行管理构建自动化。
Maven 依赖管理
Maven 一个核心的特性就是依赖管理。当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难。针对此种情形,Maven 提供了一种高度控制的方法。
可传递性依赖发现
一种相当常见的情况,比如说 A 依赖于其他库 B。如果,另外一个项目 C 想要使用 A ,那么 C 项目也需要使用库 B。
Maven 可以避免去搜索所有所需库的需求。Maven 通过读取项目文件(pom.xml),找出它们项目之间的依赖关系。
我们需要做的只是在每个项目的 pom 中定义好直接的依赖关系。其他的事情 Maven 会帮我们搞定。
通过可传递性的依赖,所有被包含的库的图形会快速的增长。当有重复库时,可能出现的情形将会持续上升。Maven 提供一些功能来控制可传递的依赖的程度。
功能 | 功能描述 |
---|---|
依赖调节 | 决定当多个手动创建的版本同时出现时,哪个依赖版本将会被使用。 如果两个依赖版本在依赖树里的深度是一样的时候,第一个被声明的依赖将会被使用。 |
依赖管理 | 直接的指定手动创建的某个版本被使用。例如当一个工程 C 在自己的依赖管理模块包含工程 B,即 B 依赖于 A, 那么 A 即可指定在 B 被引用时所使用的版本。 |
依赖范围 | 包含在构建过程每个阶段的依赖。 |
依赖排除 | 任何可传递的依赖都可以通过 “exclusion” 元素被排除在外。举例说明,A 依赖 B, B 依赖 C,因此 A 可以标记 C 为 “被排除的”。 |
依赖可选 | 任何可传递的依赖可以被标记为可选的,通过使用 “optional” 元素。例如:A 依赖 B, B 依赖 C。因此,B 可以标记 C 为可选的, 这样 A 就可以不再使用 C。 |
依赖范围
传递依赖发现可以通过使用如下的依赖范围来得到限制:
范围 | 描述 |
---|---|
编译阶段 | 该范围表明相关依赖是只在项目的类路径下有效。默认取值。 |
供应阶段 | 该范围表明相关依赖是由运行时的 JDK 或者 网络服务器提供的。 |
运行阶段 | 该范围表明相关依赖在编译阶段不是必须的,但是在执行阶段是必须的。 |
测试阶段 | 该范围表明相关依赖只在测试编译阶段和执行阶段。 |
系统阶段 | 该范围表明你需要提供一个系统路径。 |
导入阶段 | 该范围只在依赖是一个 pom 里定义的依赖时使用。同时,当前项目的POM 文件的 部分定义的依赖关系可以取代某特定的 POM。 |
依赖管理
通常情况下,在一个共通的项目下,有一系列的项目。在这种情况下,我们可以创建一个公共依赖的 pom 文件,该 pom 包含所有的公共的依赖关系,我们称其为其他子项目 pom 的 pom 父。
Maven 自动化部署
通常情况下上面的提到开发过程中会涉及到多个团队。一个团队可能负责提交代码,另一个团队负责构建等等。很有可能由于涉及的人为操作和多团队环境的原因,任何一个步骤都可能出错。比如,较旧的版本没有在网络机器上更新,然后部署团队又重新部署了较早的构建版本。
解决方案
通过结合以下方案来实现自动化部署:
- 使用 Maven 构建和发布项目
- 使用 SubVersion, 源码仓库来管理源代码
- 使用远程仓库管理软件(Jfrog或者Nexus) 来管理项目二进制文件。
Maven资料
Maven 主要目录结构
Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构。如下所示:
目录 | 目的 |
---|---|
${basedir} | 存放pom.xml和所有的子目录 |
${basedir}/src/main/java | 项目的java源代码 |
${basedir}/src/main/resources | 项目的资源,比如说property文件,springmvc.xml |
${basedir}/src/test/java | 项目的测试类,比如说Junit代码 |
${basedir}/src/test/resources | 测试用的资源 |
${basedir}/src/main/webapp/WEB-INF | web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面 |
${basedir}/target | 打包输出目录 |
${basedir}/target/classes | 编译输出目录 |
${basedir}/target/test-classes | 测试编译输出目录 |
Test.java | Maven只会自动运行符合该命名规则的测试类 |
~/.m2/repository | Maven默认的本地仓库目录位置 |
Maven 特点
- 项目设置遵循统一的规则。
- 任意工程中共享。
- 依赖管理包括自动更新。
- 一个庞大且不断增长的库。
- 可扩展,能够轻松编写 Java 或脚本语言的插件。
- 只需很少或不需要额外配置即可即时访问新功能。
- 基于模型的构建 − Maven能够将任意数量的项目构建到预定义的输出类型中,如 JAR,WAR 或基于项目元数据的分发,而不需要在大多数情况下执行任何脚本。
- 项目信息的一致性站点 − 使用与构建过程相同的元数据,Maven 能够生成一个网站或PDF,包括您要添加的任何文档,并添加到关于项目开发状态的标准报告中。
- 发布管理和发布单独的输出 − Maven 将不需要额外的配置,就可以与源代码管理系统(如 Subversion 或 Git)集成,并可以基于某个标签管理项目的发布。它也可以将其发布到分发位置供其他项目使用。Maven 能够发布单独的输出,如 JAR,包含其他依赖和文档的归档,或者作为源代码发布。
- 向后兼容性 − 您可以很轻松的从旧版本 Maven 的多个模块移植到 Maven 3 中。
- 子项目使用父项目依赖时,正常情况子项目应该继承父项目依赖,无需使用版本号,
- 并行构建 − 编译的速度能普遍提高20 - 50 %。
- 更好的错误报告 − Maven 改进了错误报告,它为您提供了 Maven wiki 页面的链接,您可以点击链接查看错误的完整描述。