码迷,mamicode.com
首页 > 其他好文 > 详细

Maven中的核心概念

时间:2015-04-14 14:40:14      阅读:268      评论:0      收藏:0      [点我收藏+]

标签:maven核心概念   maven   本地仓库   pom   

上一篇我们已经第一次运行了Maven,是时候介绍一些Maven的核心概念了。在之前的例子中,我们生了一个项目,它包含了一个POM和一些源代码,它们一起组成了Maven的标准目录布局。之后你用生命周期阶段(phase)作为参数来运行Maven,这个阶段会提示Maven运行一系列Maven插件的目标。最后,我们把Maven构件(artifact)安装(install)到了你本地仓库(repository)。等等?什么是生命周期?什么是“本地仓库”?下面的小结

阐述了一些Maven的核心概念。

1:Maven插件和目标 (Plugins and Goals)

上一篇我们用两种类型的命令行参数运行了Maven。第一条命令是一条单个的插件目标,Archetype插件的generate目标。Maven第二次运行是一个生命周期阶段–install。为了运行单个的Maven插件目标,我们使用

mvn archetype:generate 这样的语法,这里archetype是一个插件标识而generate是目标标识。当Maven运行一个插件目标,它向标准输出打印出插件标识和目标标识:
技术分享
一个Maven插件是一个单个或者多个目标的集合。Maven插件的例子有一些简单但核心的插件,像Jar插件,它包含了一组创建JAR文件的目标,Compiler插件,它包含了一组编译源代码和测试代码的目标,或者Surefire插件,它包含一组运行单元测试和生成测试报告的目标。而其它的,更有专门的插件包括:Hibernate3插件,用来集成流行的持久化框架Hibernate,JRuby插件,它让我们能够让运行ruby称为Maven构建的一部分或者用Ruby来编写Maven插件。Maven也提供了自定义插件的能力。一个定制的插件可以用Java编写,或者用一些其它的语言如Ant,Groovy,beanshell和之前提到的Ruby。
技术分享
一个目标是一个明确的任务,它可以作为单独的目标运行,或者作为一个大的构建的一部分和其它目标一起运行。一个目标是Maven中的一个“工作单元(unit ofwork)”。目标的例子包括Compiler插件中的compile目标,它用来编译项目中的所有源文件,或者Surefire插件中的test目标,用来运行单元测试。目标通过配置属性进行配置,以用来定制行为。例如,Compiler插件的compile目标定义了一组配置参数,它们允许你设置目标JDK版本或者选择是否用编译优化。在之前的例子中,我们通过命令行参数-DgroupId=com.gao.test和-DartifactId=HelloWorld向Archetype插件的generate目标传入了groupId和artifactId配置参数。我们没有generate目标传入了packageName参数,它的值为org.sonatype.mavenbook。我们忽略了packageName参数,那么包名的默认值为com.gao.test

注意:

当提到一个插件目标的时候,我们常常用速记符号:pluginId:goalId。

例如,当提到Archetype插件的generate目标的时候,我们写成archetype:generate。

目标定义了一些参数,这些参数可以定义一些明智的默认值。在archetype:generate这个例子中,我们并没有在命令行中指定这个目标创建什么类型的archetype,我们简单的传入一个groupId和一个artifactId。这是我们对于约定优于配置(convention over configuration)的第一笔。这里generate目标的约定,或者默认值,是创建一个简单的项目,叫做Quickstart。generate目标定义了一个配置属性archetypeArtifactId,它有一个默认值为maven-archetype-quickstart。Quickstart archetype生成了一个最小项目的躯壳,包括一个POM和一个类。Archetype插件比第一个例子中的样子强大得多,但是这是一个快速开始新项目的不错的方法。在之后我们将会让看到Archetype插件可以用来生成复杂如web应用的项目,以及如何能够使用Archetype插件来定义我们自己项目的集合。Maven的核心对我们项目构建中特定的任务几乎毫无所知。就它本身来说,Maven不知道如何编译你的代码,它甚至不知道如何制作一个JAR文件,它把所有这些任务代理给了Maven插件,像Compiler插件和Jar插件,它们在需要的时候被下载下来并且定时的从Maven中央仓库更新。当我们下载Maven的时候,得到的是一个包含了基本躯壳的Maven核心,它知道如何解析命令行,管理classpath,解析POM文件,在需要的时候下载Maven插件。通过保持Compiler插件和Maven核心分离,并且提供更新机制,我们很容易能使用编译器最新的版本。通过这种方式,Maven插件提供了通用构建逻辑的全局重用性,有不会在构建周期中定义编译任务,有使用了所有Maven用户共享的Compiler插件。如果有个对Compiler插件的改进,每个使用Maven的项目可以立刻从这种变化中得到好处。(并且,如果我们不喜欢这个Compiler插件,我们可以用你的实现来覆盖它)。

2:Maven生命周期 (Lifecycle)

我们运行命令mvn package。命令行并没有指定一个插件目标,而是指定了一个Maven生命周期阶段。一个阶段是在被Maven称为“构建生命周期”中的一个步骤。生命周期是包含在一个项目构建中的一系列有序的阶段。Maven可以支持许多不同的生命周期,但是最常用的生命周期是默认的Maven生命周期,这个生命周期中一开始的一个阶段是验证项目的基本完整性,最后的一个阶段是把一个项目发布成产品。生命周期的阶段被特地留得含糊,单独的定义为验证(validation),测试(testing),或者发布(deployment),而他们对不同项目来说意味着不同的事情。

例如,打包(package)这个阶段在一个项目里生成一个JAR,它也就意味着“将一个项目打成一个jar”,而在另外一个项目里,打包这个阶段可能生成一个WAR文件。下图 中“一个生命周期是一些阶段的序列”展示了默认Maven生命周期的简单样子。
技术分享

插件目标可以附着在生命周期阶段上。随着Maven沿着生命周期的阶段移动,它会执行附着在特定阶段上的目标。每个阶段可能绑定了零个或者多个目标。在之前的小节里,当我们运行mvn package,可能已经注意到了不止一个目标被执行了。检查运行mvn package之后的输出,会注意到那些被运行的各种目标。当这个简单例子到达package阶段的时候,它运行了Jar插件的jar目标。既然我们的简单的quickstart项目(默认)包类型,jar:jar目标被就绑定到了打包阶段。
技术分享
我们知道,在包类型为jar的项目中,打包阶段将会创建一个JAR文件。但是,在它之前的目标做什么呢,像compiler:compile和surefire:test?经过它生命周期经过以为结尾的默认生命周期的时候,下面的目标按顺序被执行:

resources:resources

Resources插件的resources目标绑定到了resources 阶段。这个目标复制src/main/resources下的所有资源和其它任何配置的资源目录,到输出目录。

compiler:compile

Compiler插件的compile目标绑定到了compile 阶段。这个目标编译src/main/java下的所有源代码和其他任何配置的资源目录,到输出目录。

resources:testResources

Resources插件的testResources目标绑定到了test-resources 阶段。这个目标复制src/test/resources下的所有资源和其它任何的配置的测试资源目录,到测试输出目录。

compiler:testCompile

Compiler插件的testCompile目标绑定到了test-compile 阶段。这个目标编译src/test/java下的测试用例和其它任何的配置的测试资源目录,到测试输出目录。

surefire:test

Surefire插件的test目标绑定到了test 阶段。这个目标运行所有的测试并且创建那些捕捉详细测试结果的输出文件。默认情况下,如果有测试失败,这个目标会终止。

jar:jar

Jar插件的jar目标绑定到了package 阶段。这个目标把输出目录打包成JAR文件。
技术分享
总结得来说,当我们运行mvn package,Maven运行到打包为止的所有阶段,在Maven沿着生命周期一步步向前的过程中,它运行绑定在每个阶段上的所有目标。你也可以像下面这样显式的指定一系列插件目标,以得到同样的结果:

mvn resources:resources \

compiler:compile \

resources:testResources \

compiler:testCompile \

surefire:test \

jar:jar

运行package阶段能很好的跟踪一个特定的构建中包含的所有目标,它也允许每个项目使用Maven来遵循一组定义明确的标准。而这个生命周期能让我们从一个Maven项目跳到另外一个Maven项目,而不用知道太多每个项目构建的细节。如果我们能够构建一个Maven项目,那么我们就能构建所有的Maven项目。

3:Maven坐标 (Coordinates)

Archetype插件通过名字为pom.xml的文件创建了一个项目。这就是项目对象模型(POM),一个项目的声明性描述。当Maven运行一个目标的时候,每个目标都会访问定义在项目POM里的信息。当jar:jar目标需要创建一个JAR文件的时候,它通过观察POM来找出这个Jar文件的名字。当compiler:compile任务编译Java源代码为字节码的时候,它通过观察POM来看是否有编译目标的参数。目标在POM的上下文中运行。目标是我们希望针对项目运行的动作,而项目是通过POM定义的。POM为项目命名,提供了项目的一组唯一标识符(坐标),并且通过依赖 (dependencies) ,父 (parents) 和先决条件(prerequisite) 来定义和其它项目的关系。POM也可以自定义插件行为,提供项目相关的社区和开发人员的信息。

MavenPOM里的一个插件。看一下下面的POM。

HelloWorld项目的坐标
技术分享
我们用框圈起了这个项目的坐标:groupId, artifactId, version和packaging。这些组合的标识符拼成了一个项目的坐标。就像任何其它的坐标系统,一个Maven坐标是一个地址,即“空间”里的某个点:从一般到特殊。当一个项目通过依赖,插件或者父项目引用和另外一个项目关联的时候,Maven通过坐标来精确定位一个项目。Maven坐标通常用冒号来作为分隔符来书写,像这样的格式:groupId:artifactId:packaging:version。在上面的pom.xml中,它的坐标可以表示为com.gao.test:HelloWorld:jar1.0-SNAPSHOT.版本,它包含了一个对junit:junit:jar:3.8.1的依赖。

groupId

d 团体,公司,小组,组织,项目,或者其它团体。团体标识的约定是,它以创建这个项目的组织名称的逆向域名(reverse domain name)开头。来自Sonatype的项目有一个以com.sonatype开头的groupId,而Apache Software的项目有以org.apache开头的groupId。

artifactId

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

version

一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识给版本加上一个“SNAPSHOT”的标记。项目的打包格式也是Maven坐标的重要组成部分,但是它不是项目唯一标识符的一个部分。一个项目的groupId:artifactId:version使之成为一个独一无二的项目;你不能同时有一个拥有同样的groupId, artifactId和version标识的项目。

packaging

项目的类型,默认是jar,描述了项目打包后的输出。类型为jar的项目产生一个JAR文件,类型为war的项目产生一个web应用。在其它“Maven化”项目构成的巨大空间中,的这四个元素是定位和使用某个特定项目的关键因素。Maven仓库(repositories)(公共的,私有的,和本地的)是通过这些标符来组织的。当一个项目被安装到本地的Maven仓库,它立刻能被任何其它的项目所使用。而我们所需要做的只是,在其它项目用使用Maven的唯一坐标来加入对这个特定构件的依赖。
技术分享

4:Maven仓库(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。当你创建像前一节创建的简单项目时,install阶段执行一个目标,把你项目的构件安装到我们的本地仓库。在你的本地仓库,我们应该可以看到我们的简单项目创建出来的构件。如果我们运行mvn install命令,Maven会把我们项目的构件安装到本地仓库。试一下。

由于上一篇最后我们已经用过这个命令了这里就不再重复演示了。就像我们能从这个命令的输出看到的,Maven把我们项目的JAR文件安装到了我们的本地Maven仓库。Maven在本地项目中通过本地仓库来共享依赖。如果我们开发了两个项目——项目A和项目B——项目B依赖于项目A产生的构件。当构建项目B的时候,Maven会从本地仓库取得项目A的构件。Maven仓库既是一个从远程仓库下载的构件的缓存,也允许你的项目相互依赖。

5:Maven依赖管理 (Dependency Management)

在本章的simple样例中,Maven处理了JUnit依赖的坐标——junit:junit-3.8.1,指向本地Maven仓库中的/junit/junit/3.8.1/junit-3.8.1.jar。这种基于Maven坐标的定位构件的能力能让我们在项目的POM中定义依赖。如果我们检查simple项目的pom.xml文件,会看到有一个文件中有一个段专门处理dependencies,那里面包含了一个单独的依赖——JUnit。一个复杂的项目将会包含很多依赖,也有可能包含依赖于其它构件的依赖。这是Maven最强大的特征之一,它支持了传递性依赖(transitive dependencies)。假如我们的项目依赖于一个库,而这个库又依赖于五个或者十个其它的库(就像Spring或者Hibernate那样)。我们不必找出所有这些依赖然后把它们写在你的pom.xml里,只需要加上你直接依赖的那些库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。Maven也会处理这些依赖中的冲突,同时能让你自定义默认行为,或者排除一些特定的传递性依赖。让我们看一下你运行前面的样例的时候那些下载到你本地仓库的依赖。

看一下这个目录:~/.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支持传递性依赖来说非常重要。当我们把项目的构件安装到本地仓库时,会发现在和JAR文件同一目录下,Maven发布了一个稍微修改过的pom.xml的版本。存储POM文件在仓库里提供给其它项目了该项目的信息,其中最重要的就是它有哪些依赖。如果项目B依赖于项目A,那么它也依赖于项目A的依赖。当Maven通过一组Maven坐标来处理依赖构件的时候,它也会获取POM,通依赖的POM来寻找传递性依赖。那些传递性依赖就会被添加到当前项目的依赖列表中。在Maven中一个依赖不仅仅是一个JAR。它是一个POM文件,这个POM可能也声明了对其它构件的依赖。这些依赖的依赖叫做传递性依赖,Maven仓库不仅仅存贮二进制文件,也存储了这些构建的元数据(metadata),才使传递性依赖成为可能。下图展现了一个传递性依赖的可能场景。
技术分享

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

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

mvn site

生命周期阶段。它不像默认生命周期那样,管理代码生成,操作资源,编译,打包等等。Site生命周期只关心处理在src/site目录下的site内容,还有生成报告。在这个命令运行过之后,你将会在target/site目录下看到一个项目web站点。载入target/site/index.html你会看到项目站点的基本外貌。它包含了一些报告,它们在左手边的导航目录的“项目报告”下面。它也包含了项目相关的信息,依赖和相关开发人员信息,在“项目信息”下面。Simple项目的web站点大部分是空的,因为POM只包含了比较少的信息,只有项目坐标,名称,URL和一个test依赖。在这个站点上,我们会注意到一些默认的报告已经可以访问了,有一个报告详细描述了测试的结果。这个单元测试报告描述了项目中所有单元测试的成功和失败信息。另外一个报告生成了项目API的JavaDoc。Maven提供了很完整的可配置的报告,像Clover报告检查单元测试覆盖率,JXR报告生成HTML源代码相互间引用,这在代码审查的时候非常有用,PMD报告针对各种编码问题来分析源代码,JDepend报告分析源代码中各个包之间的依赖。通过在pom.xml中配置那些报告被包含在构建中,站点报告就可以被定制了。最后我们在项目HelloWord\target\site目录下会发现如下文件
技术分享
打开index.html

如下图
技术分享
到此Maven 的一些核心概念就介绍完了。

Maven中的核心概念

标签:maven核心概念   maven   本地仓库   pom   

原文地址:http://blog.csdn.net/gao36951/article/details/45041247

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!