首页 > 编程语言 > Maven技术解析
2016
04-01

Maven技术解析

Maven简单介绍

Maven是一个项目管理工具。强大,但是很容易使用。

它包含了:

  • 一个项目对象模型 (Project Object Model);
  • 一组标准集合;
  • 一个项目生命周期(Project Lifecycle);
  • 一个依赖管理系统(Dependency Management System);
  • 用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。

当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后 Maven 可以应用横切的逻辑, 这些逻辑来自一组共享的(或者自定义的)插件。

如果看完简介,你还迷糊的,那么我想说:这很正常!请心无旁骛地往下看,学习完这个入门教程,该懂的你就都懂了。

温馨提示:本文的最后一个主题是个动手示例,如果大段的文字定义让你非常没有效率,那么,你可以在任何时候先来动手完成这个示例。找找感觉,然后再来看这些枯燥的定义。

扫清障碍

学习新技能的时候,有太多的内容需要去探索,快速有效的获取信息非常重要。掌握下边两个方法,可以助你快速掌握maven。

help 插件的使用方法

tags:maven help

目的:使用help插件,配合不同的参数,查看maven插件的相关信息。

  • 命令:mvn help:describe -Dplugin=插件名称或mvn 插件名称:help
    作用:查看插件的描述信息。

  • 命令:mvn help:describe -Dplugin=插件名称 -Dmojo=目标名称
    作用:查看插件的某个【目标】的简介信息。

  • 命令:mvn help:describe -Dplugin=插件名称 -Dmojo=目标名称 -Ddetail
    或者这么写mvn 插件名称:help -Ddetail=true -Dgoal=目标名称
    作用:查看help插件的某个【目标】的详细信息。

maven自有参数

使用mvn --help命令查看maven自有参数信息,比如-D、-X、-e

Maven核心概念

约定大于配置

在必要的情况下才进行自定义,否则使用maven默认设置。

插件(plugin),目标(goal),参数,生命周期,生命周期阶段

插件

是一组【目标】的集合。maven使用插件来完成特定的任务。

比如:创建项目使用archetype插件,编译项目使用compiler插件,使用install插件编译、测试、打包项目并安装到本地库中等。
注意install插件,他同时也是一个maven生命周期阶段,因为在执行install时,它使用了多个插件,完成了多个目标。

先记下,后边再说。

目标(goal)

  • 被用来完成明确的任务。
  • 它可以作为单独的目标运行,也可以作为一个大的构建的一部分和其它目标一起运行。
  • 一个目标是Maven中的一个“工作单元(unit of work)“。

你会在maven的输出中经常看到一个词:Mojo。mojo就是goal。A Maven plain Old Java Object.

示例:

help是一个maven插件。它包括9个目标。

help:active-profiles help:all-profiles help:describe help:effective-pom help:effective-settings help:evaluate help:expressions help:help help:system
  • 使用命令mvn 插件名称:help查看插件的目标。

参数

目标定义了一些参数,可以在执行目标时,向目标传递参数值来达成个性化的结果。

  • 使用命令mvn help:describe -Dplugin=插件名称 -Dmojo=目标名称 -Ddetail查看目标的参数。

生命周期

生命周期就是一个项目从无到有的整个过程,这个过程里包含了多个阶段(比如创建、编译、测试、打包/构建、部署),每个阶段就叫做生命周期阶段。Maven的生命周期是抽象的。
这意味着生命周期本身不做任何实际的工作。在Maven的设计中,实际的任务(如编译源代码)都交由插件来完成。maven命令行的输入往往就对应了生命周期阶段,如mvn package就表示执行默认生命周期阶段package(构建)。

  • 生命周期和插件两者协同工作,密不可分。
  • 生命周期是一系列有序的生命周期阶段的集合,对所有的构建过程进行了抽象和统一。
  • Maven可以支持许多不同的生命周期,最常用的是Maven生命周期。

包括:

清理
初始化
编译
测试
打包(package)
集成测试
验证
部署
站点生成

以上的每个生命周期阶段都可以绑定一个或者多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认插件。
比如package这个生命周期阶段的编译任务可能就会调用maven-jar-plugin来完成,测试任务就会调用maven-surefire-plugin来完成。

坐标

Maven坐标定义了一组标识,它们可以用来唯一标识一个项目,一个依赖,或者Maven POM里的一个插件。

Maven项目坐标的构成元素:groupId, artifactId, version和packaging。这些组合的标识符拼成了一个项目的坐标。

但是packaging不是一个项目唯一标识符的必须部分。

Maven坐标通常用冒号来作为分隔符来书写,格式:groupId:artifactId:packaging:version。
比如:mavenbook:my-app:jar:1.0-SNAPSHOT.
这也适用于项目依赖,比如项目包含了一个对junit:junit:jar:3.8.1的依赖。

标识解释

1 groupId:

团体,公司,小组,组织,项目,或者其它团体的逆向域名。比如Apache Software的项目经常以org.apache作为groupId。

2 artifactId

在groupId下表示一个单独项目的唯一标识符。

3 version:

一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。正在开发中的项目用一个特殊的标识“SNAPSHOT”标记。正式发布版是“release”或“stable”标记。

4 packaging:

项目的类型,默认是jar,描述了项目打包后的输出。类型为jar的项目产生一个JAR文件,类型为war的项目产生一个web应用。

仓库(Repositories)

当你第一次运行Maven的时候,你会注意到Maven从一个远程的Maven仓库下载了许多文件。如果这个简单的项目是你第一次运行Maven,那么当触发resources:resource目标的时候,它首先会做的事情是去下载最新版本的Resources插件。在Maven中,构件和插件是在它们被需要的时候从远程的仓库取来的。初始的Maven下载包的大小相当的小(1.8兆),其中一个原因是这个初始Maven不包括多余的插件。它只包含了几近赤裸的最少值,而在需要的时候再从远程仓库去取。Maven自带了一个用来下载Maven核心插件和依赖的远程仓库地址/ http://repo1.maven.org/maven2 。你常常会写这样一个项目,这个项目依赖于一些既不免费也不公开的包。在这种情况下,你需要要么在你组织的网络里安装一个定制的仓库,要么手动的安装这些依赖。默认的远程仓库可以被替换,或者增加一个你组织维护的自定义Maven仓库的引用。有许多现成的项目允许组织管理和维护公共Maven仓库的镜像。

是什么让Maven仓库成为一个Maven仓库的呢?

Maven仓库是通过结构来定义的,一个Maven仓库是项目构件的一个集合,这些构件存储在一个目录结构下面,它们的格式能很容易的被Maven所理解。在一个Maven仓库中,所有的东西存储在一个与Maven项目坐标十分匹配的目录结构中。你可以打开浏览器,然后浏览中央Maven仓库http://repo1.maven.org/maven2/ 来看这样的结构。你会看到坐标为org.apache.commons:commons-email:1.1的构件能在目录/org/apache/commons/commons-email/1.1/下找到,文件名为commons-email-1.1.jar。Maven仓库使用约定的标准目录格式来存储构件。

Maven从远程仓库下载构件和插件到你本机上,存储在你的本地Maven仓库里。一旦Maven已经从远程仓库下载了一个构件,它将永远不需要再下载一次,因为maven会首先在本地仓库查找插件,然后才是其它地方。在Windows XP上,你的本地仓库很可能在C:\Documents and Settings\USERNAME.m2\repository,在Windows Vista上,会是C:
\Users\USERNAME.m2\repository。在Unix系统上,你的本地仓库在~/.m2/repository。

如果你运行mvn install命令,Maven会把本地项目的构件安装到本地仓库。你能从这个命令的输出看到,Maven把本地项目的JAR文件安装到了本地Maven仓库。Maven在本地项目中通过本地仓库来共享依赖。如果你开发了两个项目—— 项目A和项目B——项目B依赖于项目A产生的构件。当构建项目B的时候,Maven会从本地仓库取得项目A的构件。

Maven仓库既是一个从远程仓库下载的构件的缓存,也允许你的项目相互依赖。

依赖管理 (Dependency Management)

创建一个项目所需要的资源引用称为依赖。

一个复杂的项目将会包含很多依赖,也有可能包含依赖于其它构件的依赖。这是Maven最强大的特征之一,它支持了传递性依赖(transitive dependencies)。假如你的项目依赖于一个库,而这个库又依赖于五个或者十个其它的库(就像Spring或者Hibernate那样)。你不必找出所有这些依赖然后把它们写在你的pom.xml里,你只需要加上你直接依赖的那些库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。Maven也会处理这些依赖中的冲突,同时能让你自定义默认行为,或者排除一些特定的传递性依赖。

在Maven中的一个依赖不仅仅是一个JAR。它还包括了一个POM文件,这个POM可能也声明了对其它构件的依赖。这些依赖的依赖叫做传递性依赖,Maven仓库不仅仅存贮二进制文件,也存储了这些构建的元数据(metadata),才使传递性依赖成为可能。

让我们看一下这个目录:~/.m2/repository/junit/junit/3.8.1/。这里会有文件junit-3.8.1.jar 和junit-3.8.1.pom,还有Maven用来验证已下载构件准确性的校验和文件。需要注意的是Maven不只是下载JUnit的JAR文件,它同时为这个JUnit依赖下载了一个POM文件。Maven同时下载构件和POM文件的这种行为,对Maven支持传递性依赖来说非常重要。
当Maven通过一组Maven坐标来处理依赖构件的时候,它也会获取依赖构建的POM,通过依赖的POM来寻找传递性依赖。那些传递性依赖就会被添加到当前项目的依赖列表中。

Maven同时也提供了一种机制,能让你排除一些你不想要的传递性依赖。

Maven也提供了不同的依赖范围(dependency scope)。当一个依赖的范围是test的时候,说明它在Compiler插件运行compile 目标的时候是不可用的。它只有在运行compiler:testCompile和surefire:test目标的时候才会被加入到classpath中。当用Maven来创建WAR或者EAR,你可以配置Maven让它在生成的构件中捆绑依赖,你也可以配置Maven,使用provided范围,让它排除WAR文件中特定的依赖。provided范围告诉Maven一个依赖在编译的时候需要,但是它不应该被捆绑在构建的输出中。当你开发web应用的时候provided范围变得十分有用,你需要通过Servlet API来编译你的代码,但是你不希望Servlet API的JAR文件包含在你web应用的WEB-INF/lib 目录中。

站点生成和报告 (Site Generation and Reporting)

另外一个Maven的重要特征是,它能生成文档和报告。在项目的目录下,运行以下命令:

mvn site

这将会运行site生命周期阶段。它不像默认生命周期那样,管理代码生成,操作资源,编译,打包等等。Site生命周期只关心处理在src/site目录下的site内容,还有生成报告。在这个命令运行过之后,你将会在target/site 目录下看到一个项目web站点。载入target/site/index.html你会看到项目站点的基本外貌。它包含了一些报告,它们在左手边的导航目录的“项目报告”下面。它也包含了项目相关的信息,依赖和相关开发人员信息。

在这个站点上,你会注意到一些默认的报告已经可以访问了,有一个报告详细描述了测试的结果。这个单元测试报告描述了项目中所有单元测试的成功和失败信息。另外一个报告生成了项目API的JavaDoc。Maven提供了很完整的可配置的报告,像Clover报告检查单元测试覆盖率,JXR报告生成HTML源代码相互间引用,这在代码审查的时候非常有用,PMD报告针对各种编码问题来分析源代码,JDepend报告分析源代码中各个包之间的依赖。通过在pom.xml中配置那些报告被包含在构建中,站点报告就可以被定制了。

自己动手体会一下

通过一个经典的控制台 Hello world! 程序来体会maven的使用过程,包括创建、编译、测试、打包 ,并运行程序。

第一步:定位目录

在命令窗口中定位到将要创建项目的目录下。比如我的是:D:\Project\mavenTest\quickstart>。

第二步:创建项目

使用archetype插件创建项目,输入以下命令:mvn archetype:generate,回车,等。。。 然后,不出意外的话,你会看到屏幕输出了上千种archetype支持的项目类型。在末尾你会看到要求你输入的地方,就是这个:Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 756:不翻译了,你一定要能看懂。

因为我们要创建的是一个简单的示例程序,所以什么都不输入,直接回车。
屏幕输出maven-archetype-quickstart项目类型的几个不同版本.
输入对应的序号(我输入的是6),然后回车。

按照屏幕输出,依次输入:groupId,artifactId,version,package. 

屏幕会输出你填写的项目信息,要求你确认,输入 Y ,回车,确认。

maven会根据你的设置开始下载依赖文件并创建项目。 当你看到Build Success时,项目就被创建好了,打开你的项目位置查看你的项目。maven在项目根目录下生成了一个POM.xml文件,这个是项目对象模型描述文件,maven就是根据这个文件的描述来构建你的项目的,打开看一下。

第三步:编译项目

使用compiler插件创建项目,输入以下命令:mvn compiler:compile,回车,等。。。
maven开始下载依赖文件并编译项目。 当你看到Build Success时,项目就被编译好了,你还可以看到详细的编译结果信息。

第四步:打包项目 / 构建项目

使用maven默认的生命周期阶段命令package来打包项目,输入以下命令:mvn package,回车,等。。。
maven开始下载依赖文件并编译项目。 当你看到Build Success时,项目就被打包好了,你还可以看到详细的编译结果信息。项目的根目录会出现一个target目录,存放了项目打包后的文件。

当然,你也可以不使用maven生命周期的默认package命令,而是自己动手使用多个插件来完成这个任务。

第五步:运行项目

如果前边的都成功完成的话,现在就可以运行一下项目看看结果了。

命令行输入:java -cp target/quickstart-1.1.jar maven.archetype.quickstart.App回车。
应该输出了Hello world!。

到这里为止,你已经了解了maven最核心的概念,明白了maven的结构,而且亲自使用maven成功构建了一个项目。

所以,本文到此就结束了。

接下来,到实际的工作中去使用maven,不断磨练自己的技艺吧!


文/驭风(简书作者)
原文链接:http://www.jianshu.com/p/6fc7b4aaf5f9
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

编程技巧