
When you search for artifacts using http://repository.sonatype.org, the browser is querying the Nexus repository using a REST API. In this post, I’m going to show you some simple Ruby scripts which you can use to search the Maven repository ...
If you are looking for the easiest way to search for artifacts in the Central Maven Repository, go to http://repository.sonatype.org. Sonatype maintains a publi instance of Nexus that can be used to search for artifacts by GAV (groupId, artifactId...
A common question from Apache Maven users is “How do I search the central repository?” or “How do I find out what groupId or artifactId I should use for a specific dependency?” Use Sonatype’s Nexus installation at htt...
One of the users on the m2eclipse user list has some great feedback about a presentation he did at the Stockholm JUG on the best of breed Maven tooling which includes Nexus and m2eclipse. Apparently people are impressed with the Maven support insi...
Maven 3.0 uses a new standalone component that handles inheritance and interpolation of a model in any format. The model needn’t even be XML based. If you can translate your model into a list of property-value pairs, you can use this framewo...
There’s are lots of people who already know that Mark de Visser is the new CEO of Sonatype, but Zack wrote a nice bit about it today in his InfoWorld blog: http://weblog.infoworld.com/openresource/archives/2008/11/marketing_maven.html I am r...
本章我们介绍一个用Maven Archetype插件从空白开始创建的简单项目。 当你跟着这个简单项目的开发过程,你会看到这个简单的应用给我们提供了介绍Maven核心概念的机会。
在你能开始使用Maven做复杂的,多模块的构建之前,我们需要从基础开始。如果你之前用过Maven,你将会注意到这里很好的照顾到了细节。你的构建倾向于“只要能工作”,只有当你需要编写插件来自定义默认行为的时候,才需要深入Maven的细节。另一方面,当你需要深入Maven细节的时候,对Maven核心概念的彻底理解是至关重要的。本章致力于为你介绍一个尽可能简单的Maven项目,然后介绍一些使Maven成为一个可靠的构建平台的核心概念。读完本章后,你将会对构建生命周期 (build lifecycle),Maven仓库 (repositories),依赖管理 (dependency management)和项目对象模型 (Project Object Model)有一个基本的理解。
本章开发了一个十分简单的例子,它将被用来探究Maven的核心概念。如果你跟着本章表述的步骤,你应该不需要下载这些例子来重新创建那些Maven已经生成好的代码。我们将会使用Maven
Archetype插件来创建这个简单的项目,本章不会以任何方式修改这个项目。如果你更喜欢通过最终的例子源代码来阅读本章,本章的例子项目和这本书的例子代码可以从这里下载到:http://www.sonatype.com/book/mvn-examples-1.0.zip或者http://www.sonatype.com/book/mvn-examples-1.0.tar.gz。解压存档文件到任何目录下,然后到ch03/目录。在ch03/目录你将看到一个名字为simple/的目录,它包含了本章的源代码。如果你希望在Web浏览器里看这些例子代码,访问http://www.sonatype.com/book/examples-1.0并且点击ch03/目录。
开始一个新的Maven项目,在命令行使用Maven Archetype插件。
$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 \
-DartifactId=simple \
-DpackageName=org.sonatype.mavenbook
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] artifact org.apache.maven.plugins:maven-archetype-plugin: checking for \
updates from central
[INFO] -----------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO] task-segment: [archetype:create] (aggregator-style)
[INFO] --------------------------------------------------------------------
[INFO] [archetype:create]
[INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: \
checking for updates from central
[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch03
[INFO] Parameter: packageName, Value: org.sonatype.mavenbook
[INFO] Parameter: basedir, Value: /Users/tobrien/svnw/sonatype/examples
[INFO] Parameter: package, Value: org.sonatype.mavenbook
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: simple
[INFO] * End of debug info from resources from generated POM *
[INFO] Archetype created in dir: /Users/tobrien/svnw/sonatype/examples/simple
mvn
是Maven2的命令。archetype:create称为一个Maven目标
(goal)。如果你熟悉Apache Ant,一个Maven目标类似于一个Ant目标 (target);它们都描述了将会在构建中完成的工作单元
(unit of
work)。而像-Dname=value这样的对是将会被传到目标中的参数,它们使用-D属性这样的形式[1],类似于你通过命令行向Java虚拟机传递系统属性。archetype:create这个目标的目的通过archetype快速创建一个项目。在这里,一个archetype被定义为“一个原始的模型或者类型,在它之后其它类似的东西与之匹配;一个原型(prototype)”。Maven有许多可用的archetype,从生成一个简单的Swing应用,到一个复杂的Web应用。本章我们用最基本的archetype来创建一个入门项目的骨架。这个插件的前缀是“archetype”,目标为”create”。[1]
我们已经生成了一个项目,看一下Maven在simple目录下创建的目录结构:
simple/simple/pom.xml
/src/ /src/main/
/main/java /src/test/
/test/java
这个生成的目录遵循Maven标准目录布局,我们之后会去看更多的细节,但是,现在让我们只是尝试了解这些基本的目录。
Maven
Archtype插件生成了一个简单的类org.sonatype.mavenbook.App,它是一个仅有13行代码的Java,所做的只是在main方法中输出一行消息:
package org.sonatype.mavenbook;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
}
}
最简单的Maven archetype生成最简单的Maven项目:一个往标准输出打印“Hello World”的程序。
一旦你遵循Section 3.2, “创建一个简单的项目”使用Maven
Archetype插件创建了一个项目,你会希望构建并打包这个应用。想要构建打包这个应用,在包含pom.xml的目录下运行mvn
install。
$ mvn install
[INFO] Scanning for projects...
[INFO] ----------------------------------------------------------------------------
[INFO] Building simple
[INFO] task-segment: [install]
[INFO] ----------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to /simple/target/classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Compiling 1 source file to /simple/target/test-classes
[INFO] [surefire:test]
[INFO] Surefire report directory: /simple/target/surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.sonatype.mavenbook.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.105 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] [jar:jar]
[INFO] Building jar: /simple/target/simple-1.0-SNAPSHOT.jar
[INFO] [install:install]
[INFO] Installing /simple/target/simple-1.0-SNAPSHOT.jar to \
~/.m2/repository/org/sonatype/mavenbook/ch03/simple/1.0-SNAPSHOT/ \
simple-1.0-SNAPSHOT.jar
你已经创建了,编译了,测试了,打包了,并且安装了(installed)最简单的Maven项目。在命令行运行它以向你自己验证这个程序能工作。
$ java -cp target/simple-1.0-SNAPSHOT.jar org.sonatype.mavenbook.App
Hello World!
当Maven运行的时候它向项目对象模型(POM)查看关于这个项目的信息。POM回答类似这样的问题:这个项目是什么类型的?这个项目的名称是什么?这个项目的构建有自定义么?这里是一个由Maven
Archetype插件的create目标创建的默认的pom.xml文件。
Example 3.1. Simple 项目的 pom.xml 文件
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.sonatype.mavenbook.ch03</groupId> <artifactId>simple</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>simple</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
这个pom.xml文件是你将会面对的Maven项目中最基础的POM,一般来说一个POM文件会复杂得多:定义多个依赖,自定义插件行为。最开始的几个元素——groupId,artifactId,
packaging,
version——是Maven的坐标(coordinates),它们唯一标识了一个项目。name和url是POM提供的描述性元素,它们给人提供了可阅读的名字,将一个项目关联到了项目web站点。最后,dependencies元素定义了一个单独的,测试范围(test-scoped)依赖,依赖于称为JUnit的单元测试框架。这些话题将会Section 3.5, “核心概念”被深入介绍,当前,你所需知道的是,pom.xml是一个让Maven跑起来的文件。
当Maven运行的时候,它是根据项目的pom.xml里设置的组合来运行的,一个最上级的POM定义了Maven的安装目录,在这个目录中全局的默认值被定义了,(可能)还有一些用户定义的设置。想要看这个“有效的
(effective)”POM,或者说Maven真正运行根据的POM,在simple项目的基础目录下跑下面的命令。
$ mvn help:effective-pom一旦你运行了此命令,你应该能看到一个大得多的POM,它暴露了Maven的默认设置
我们已经第一次运行了Maven,是时候介绍一些Maven的核心概念了。在之前的例子中,我们生了一个项目,它包含了一个POM和一些源代码,它们一起组成了Maven的标准目录布局。之后你用生命周期阶段(phase)作为参数来运行Maven,这个阶段会提示Maven运行一系列Maven插件的目标。最后,你把Maven构件(artifact)安装(install)到了你本地仓库(repository)。等等?什么是生命周期?什么是“本地仓库”?下面的小结阐述了一些Maven的核心概念。
在前面一节中,我们用两种类型的命令行参数运行了Maven。第一条命令是一条单个的插件目标,Archetype插件的create目标。Maven第二次运行是一个生命周期阶段
–install。为了运行单个的Maven插件目标,我们使用mvn
archetype:create这样的语法,这里archetype是一个插件标识而create是目标标识。当Maven运行一个插件目标,它向标准输出打印出插件标识和目标标识:
$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch03 \ -DartifactId=simple \ -DpackageName=org.sonatype.mavenbook ... [INFO] [archetype:create] [INFO] artifact org.apache.maven.archetypes:maven-archetype-quickstart: \ checking for updates from central ...
一个Maven插件是一个单个或者多个目标的集合。Maven插件的例子有一些简单但核心的插件,像Jar插件,它包含了一组创建JAR文件的目标,Compiler插件,它包含了一组编译源代码和测试代码的目标,或者Surefire插件,它包含一组运行单元测试和生成测试报告的目标。而其它的,更有专门的插件包括:Hibernate3插件,用来集成流行的持久化框架Hibernate,JRuby插件,它让你能够让运行ruby称为Maven构建的一部分或者用Ruby来编写Maven插件。Maven也提供了自定义插件的能力。一个定制的插件可以用Java编写,或者用一些其它的语言如Ant,Groovy,beanshell和之前提到的Ruby。
一个目标是一个明确的任务,它可以作为单独的目标运行,或者作为一个大的构建的一部分和其它目标一起运行。一个目标是Maven中的一个“工作单元(unit
of
work)”。目标的例子包括Compiler插件中的compile目标,它用来编译项目中的所有源文件,或者Surefire插件中的test目标,用来运行单元测试。目标通过配置属性进行配置,以用来定制行为。例如,Compiler插件的compile目标定义了一组配置参数,它们允许你设置目标JDK版本或者选择是否用编译优化。在之前的例子中,我们通过命令行参数-DgroupId=org.sonatype.mavenbook.ch03和-DartifactId=simple向Archetype插件的create目标传入了groupId和artifactId配置参数。我们也向create目标传入了packageName参数,它的值为org.sonatype.mavenbook。如果我们忽略了packageName参数,那么包名的默认值为org.sonatype.mavenbook.ch03。
当提到一个插件目标的时候,我们常常用速记符号:pluginId:goalId。例如,当提到Archetype插件的create目标的时候,我们写成archetype:create。
目标定义了一些参数,这些参数可以定义一些明智的默认值。在archetype:create这个例子中,我们并没有在命令行中指定这个目标创建什么类型的archetype,我们简单的传入一个groupId和一个artifactId。这是我们对于约定优于配置(convention
over
configuration)的第一笔。这里create目标的约定,或者默认值,是创建一个简单的项目,叫做Quickstart。create目标定义了一个配置属性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插件,你可以用你的实现来覆盖它)。
上一节中,我们运行的第二个命令是mvn package。命令行并没有指定一个插件目标,而是指定了一个Maven生命周期阶段。一个阶段是在被Maven称为“构建生命周期”中的一个步骤。生命周期是包含在一个项目构建中的一系列有序的阶段。Maven可以支持许多不同的生命周期,但是最常用的生命周期是默认的Maven生命周期,这个生命周期中一开始的一个阶段是验证项目的基本完整性,最后的一个阶段是把一个项目发布成产品。生命周期的阶段被特地留得含糊,单独的定义为验证(validation),测试(testing),或者发布(deployment),而他们对不同项目来说意味着不同的事情。例如,打包(package)这个阶段在一个项目里生成一个JAR,它也就意味着“将一个项目打成一个jar”,而在另外一个项目里,打包这个阶段可能生成一个WAR文件。Figure 3.2, “一个生命周期是一些阶段的序列”展示了默认Maven生命周期的简单样子。
插件目标可以附着在生命周期阶段上。随着Maven沿着生命周期的阶段移动,它会执行附着在特定阶段上的目标。每个阶段可能绑定了零个或者多个目标。在之前的小节里,当你运行mvn
package,你可能已经注意到了不止一个目标被执行了。检查运行mvn
package之后的输出,会注意到那些被运行的各种目标。当这个简单例子到达package阶段的时候,它运行了Jar插件的jar目标。既然我们的简单的quickstart项目(默认)是jar包类型,jar:jar目标被就绑定到了打包阶段。
我们知道,在包类型为jar的项目中,打包阶段将会创建一个JAR文件。但是,在它之前的目标做什么呢,像compiler:compile和surefire:test?在Maven经过它生命周期中package之前的阶段的时候,这些目标被运行了;Maven执行一个阶段的时候,它首先会有序的执行前面的所有阶段,到命令行指定的那个阶段为止。每个阶段对应了零个或者多个目标。我们没有进行任何插件配置或者定制,所以这个例子绑定了一组标准插件的目标到默认的生命周期。当Maven经过以package为结尾的默认生命周期的时候,下面的目标按顺序被执行:
resources:resourcesResources插件的resources目标绑定到了resources
阶段。这个目标复制src/main/resources下的所有资源和其它任何配置的资源目录,到输出目录。
compiler:compileCompiler插件的compile目标绑定到了compile
阶段。这个目标编译src/main/java下的所有源代码和其他任何配置的资源目录,到输出目录。
resources:testResourcesResources插件的testResources目标绑定到了test-resources
阶段。这个目标复制src/test/resources下的所有资源和其它任何的配置的测试资源目录,到测试输出目录。
compiler:testCompileCompiler插件的testCompile目标绑定到了test-compile
阶段。这个目标编译src/test/java下的测试用例和其它任何的配置的测试资源目录,到测试输出目录。
surefire:testSurefire插件的test目标绑定到了test
阶段。这个目标运行所有的测试并且创建那些捕捉详细测试结果的输出文件。默认情况下,如果有测试失败,这个目标会终止。
jar:jarJar插件的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项目。
Archetype插件通过名字为pom.xml的文件创建了一个项目。这就是项目对象模型(POM),一个项目的声明性描述。当Maven运行一个目标的时候,每个目标都会访问定义在项目POM里的信息。当jar:jar目标需要创建一个JAR文件的时候,它通过观察POM来找出这个Jar文件的名字。当compiler:compile任务编译Java源代码为字节码的时候,它通过观察POM来看是否有编译目标的参数。目标在POM的上下文中运行。目标是我们希望针对项目运行的动作,而项目是通过POM定义的。POM为项目命名,提供了项目的一组唯一标识符(坐标),并且通过依赖
(dependencies) ,父 (parents) 和先决条件 (prerequisite)
来定义和其它项目的关系。POM也可以自定义插件行为,提供项目相关的社区和开发人员的信息。
Maven坐标定义了一组标识,它们可以用来唯一标识一个项目,一个依赖,或者Maven POM里的一个插件。看一下下面的POM。
我们加亮了这个项目的坐标:groupId,
artifactId,
version和packaging。这些组合的标识符拼成了一个项目的坐标[2]。[2]就像任何其它的坐标系统,一个Maven坐标是一个地址,即“空间”里的某个点:从一般到特殊。当一个项目通过依赖,插件或者父项目引用和另外一个项目关联的时候,Maven通过坐标来精确定位一个项目。Maven坐标通常用冒号来作为分隔符来书写,像这样的格式:groupId:artifactId:packaging:version。在上面的pom.xml中,它的坐标可以表示为mavenbook:my-app:jar:1.0-SNAPSHOT.这个符号也适用于项目依赖,我们的项目依赖JUnit的3.8.1版本,它包含了一个对junit:junit:jar:3.8.1的依赖。
groupIdd 团体,公司,小组,组织,项目,或者其它团体。团体标识的约定是,它以创建这个项目的组织名称的逆向域名(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的唯一坐标来加入对这个特定构件的依赖。
当你第一次运行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仓库的标准是按照下面的目录格式来存储构件,相对于仓库的根目录:
/<groupId>/<artifactId>/<version>/<artifactId>-<version>.<packaging>
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会把我们项目的构件安装到本地仓库。试一下。
$ mvn install
...
[INFO] [install:install]
[INFO] Installing .../simple-1.0-SNAPSHOT.jar to \
~/.m2/repository/org/sonatype/mavenbook/simple/1.0-SNAPSHOT/ \
simple-1.0-SNAPSHOT.jar
...就像你能从这个命令的输出看到的,Maven把我们项目的JAR文件安装到了我们的本地Maven仓库。Maven在本地项目中通过本地仓库来共享依赖。如果你开发了两个项目——项目A和项目B——项目B依赖于项目A产生的构件。当构建项目B的时候,Maven会从本地仓库取得项目A的构件。Maven仓库既是一个从远程仓库下载的构件的缓存,也允许你的项目相互依赖。
在本章的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),才使传递性依赖成为可能。下图展现了一个传递性依赖的可能场景。
在上图中,项目A依赖于项目B和C,项目B依赖于项目D,项目C依赖于项目E,但是项目A所需要做的只是定义对B和C的依赖。当你的项目依赖于其它的项目,而这些项目又有一些小的依赖时(向Hibernate, Apache Struts 或者 Spring Framework),传递性依赖使之变得相当的方便。Maven同时也提供了一种机制,能让你排除一些你不想要的传递性依赖。
Maven也提供了不同的依赖范围(dependency
scope)。Simple项目的pom.xml包含了一个依赖——junit:junit:jar:3.8.1——范围是test。当一个依赖的范围是test的时候,说明它在Compiler插件运行compile目标的时候是不可用的。它只有在运行compiler:testCompile和surefire:test目标的时候才会被加入到classpath中。
当为项目创建JAR文件的时候,它的依赖不会被捆绑在生成的构件中,他们只是用来编译。当用Maven来创建WAR或者EAR,你可以配置Maven让它在生成的构件中捆绑依赖,你也可以配置Maven,使用provided范围,让它排除WAR文件中特定的依赖。provided范围告诉Maven一个依赖在编译的时候需要,但是它不应该被捆绑在构建的输出中。当你开发web应用的时候provided范围变得十分有用,你需要通过Servlet
API来编译你的代码,但是你不希望Servlet
API的JAR文件包含在你web应用的WEB-INF/lib目录中。
另外一个Maven的重要特征是,它能生成文档和报告。在simple项目的目录下,运行以下命令:
$ mvn site
这将会运行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中配置那些报告被包含在构建中,站点报告就可以被定制了。
我们创建了一个simple项目,将其打包为一个Jar,安装到了Maven仓库使之能被其它项目使用,最后生成了带有文档的站点。不写一行代码,不碰一个配置文件,我们就做到了这些。我们花了一些时间来研究Maven核心概念的定义。在下一章,我们将会自定义并修改我们项目的pom.xml文件,加入一些依赖,配置一些单元测试。

