Maven最早始于Jarkarta Turbine项目,目的是为了简化构建过程。这个项目下面有 几个子项目,每个子项目都有自己的Ant文件,但是区别很小,而且生成的JAR 文件都要check in到CVS中。我们希望有一种标准的方式来构建这些项目,一种可以 清晰描述项目的方式,一种易于发布项目信息的方式,一种在多个项目之间共享JARs 的方式。
这个结果就是产生了一个可以用于构建、管理任何基于java的项目。我们希望我们创造 的这个工具可以让Java开发者的日常工作更加轻松,并有助于理解基于java的项目.
项目对象模型
Maven是基于项目对象模型(POM)的概念而创建的。在这个模型中,所有由Maven产生的 artifact都是清晰定义的项目模型的结果。构建,文档,源码度量,源码交叉引用和其他 任何由Maven plug-in提供的东西都是由POM来控制的。
POM 处理机制
这篇文档简单的描述了Maven执行过程中是如何处理POM的。这里有一些简单的POM例子 来展示继承机制和插值机制。
POM 插值机制
POM(通常以project.xml的名字出现)现在已经被当作Jelly脚本来处理了。大部分时 候,用户无须关心project.xml文件是不是真正的Jelly脚本,但是,如果需要的话, 也可以使用内置值。我也不愿看到逻辑控制语句出现在project.xml中,但是由于 project.xml实际上已经是一个隐含的jelly的脚本,所以它会有足够的灵活性:-)。 下面是一个简单的例子。
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<pomVersion>3</pomVersion>
<groupId>maven</groupId>
<artifactId>maven</artifactId>
<name>Maven</name>
<currentVersion>1.0-b5-dev</currentVersion>
<organization>
<name>Apache Software Foundation</name>
<url>http://jakarta.apache.org/</url>
<logo>/images/jakarta-logo-blue.gif</logo>
</organization>
<inceptionYear>2001</inceptionYear>
<package>org.apache.${pom.artifactId}</package>
<logo>/images/${pom.artifactId}.jpg</logo>
<description>Maven is a project that was created in ${pom.inceptionYear}.</description>
<shortDescription>${pom.name} is a Java Project Management Tool</shortDescription>
</project>
POM 继承机制
现在有一种简单方式可以用于在POM中进行继承,下面是一个简单的例子:
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>project.xml</extend>
<groupId>super-extendo</groupId>
<artifactId>super-extendo<artifactId>
<name>Super Extendo</name>
<build>
<unitTest>
<includes>
<include>**/*Test*.java</include>
</includes>
<excludes>
<exclude>**/TestAll.java</exclude>
<exclude>**/*Abstract*.java</exclude>
</excludes>
</unitTest>
</build>
</project>
目前对POM父对象的解析还相对较弱,现在对一层以上的继承还没有做过任何测试。尽管如此, 单层继承加上插值机制已经能够给带来很多好处。这些机制的意图在于简化构建的共用问题。
你可以这样定义主模板:
<project>
<pomVersion>3</pomVersion>
<groupId>commons</groupId>
<artifactId>commons-master</artifactId>
<name>Commons Master Maven POM</name>
<organization>
<name>Apache Software Foundation</name>
<url>http://www.apache.org</url>
</organization>
<gumpRepositoryId>jakarta</gumpRepositoryId>
<url>http://jakarta.apache.org/commons/${pom.artifactId}.html</url>
<issueTrackingUrl>http://nagoya.apache.org/</issueTrackingUrl>
<siteAddress>jakarta.apache.org</siteAddress>
<siteDirectory>/www/jakarta.apache.org/commons/${pom.artifactId}/</siteDirectory>
<distributionDirectory>
/www/jakarta.apache.org/builds/jakarta-commons/${pom.artifactId}/
</distributionDirectory>
<repository>
<connection>
scm:cvsserver:anoncvs@cvs.apache.org:/home/cvspublic:jakarta-commons/${pom.artifactId}
</connection>
<url>http://cvs.apache.org/viewcvs/jakarta-commons/${pom.artifactId}/</url>
</repository>
...
</project>
子POM对象可以这样定义:
<project>
<groupId>commons-betwixt</groupId>
<artifactId>commons-betwixt</artifactId>
<name>Betwixt</name>
...
</project>
这样你就可以在父POM对象中,将子POM对象的${pom.artifactId}替换进去。有许多项目的 构建都以相似的方式进行构建,这样一来,对于项目的公共部分,你就可以使用一个主模板, 然后在子POM对象project.xml中定义有区别的部分,而这部分内容通常很少。
这种机制确实还可以简化那些需要产生多个JAR包的项目。由于project.xml和标准Ant构建 不会相互干扰,我计划在公共部分测试继承机制。
如果你对使用这种机制,DVSL报告会变成什么样感到疑惑,我要说,你很上路。我已经修改 了DVSL报告来适应POM本身,这就是说DVSL转换是基于java对象的。在使用继承和插值机制的 时候,为了正确的产生DVSL报告,这是很有必要的。象上面列出的子模板是无法工作的,我们 需要全面的解析POM。我能说的是,它可以工作了!我所使用的处理方式可能不是最有效率的 方式,但仍有提升的空间。因为POM只会被处理一次(不管怎么说,这就它的原理,我可能漏了 某些东西),然后到处使用,至少这就是我以前试图做的事情,所以我们很有可能会取得平衡。
如果你不使用继承和插值,那么一切照常工作。maven站点本身一切ok,有几个刚部署的站点 已经使用了我昨晚提交的东西了。
使用插件
Maven是一个很紧凑的内核,围绕着它的是许许多多的插件。Maven所有的功能都是由插件来提供 的。
maven.xml文件
项目中的maven.xml文件是Maven在执行过程中要使用的“定制”文件。
在这个文件中,你可以加入Maven构建中需要的额外处理。或者在Maven的“目标”前后附加自己 的代码,如jar 或 test。
Maven使用Jelly 脚本语言, 任何合法的jelly标签都可以在maven.xml中使用。
Maven所采用的goal功能是由werkz标签库提供。更多的信息请看 wiki页面.
简单的maven.xml例子
注意由于Maven并未缺省的定义'compile'目标,下面这个例子没法运行。
这是一个简单的maven.xml例子
<project
default="nightly-build"
xmlns:j="jelly:core"
xmlns:u="jelly:util">
<goal name="nightly-build">
<!-- Any ant task, or jelly tags can go here thanks to jeez -->
<j:set var="goals" value="compile,test" />
<mkdir dir="${maven.build.dir}" />
<u:tokenize var="goals" delim=",">${goals}</u:tokenize>
<j:forEach items="${goals}" var="goal" indexVar="goalNumber">
Now attaining goal number ${goalNumber}, which is ${goal}
<attainGoal name="${goal}" />
</j:forEach>
</goal>
</project>
你可能会注意到这里一些有意思的东西,下面我们将逐一解释。
project节点
project节点, <project>, 是任何 maven.xml 文件的根节点。
项目节点有一个缺省的属性:default="nightly-build",如果用户只是简单键入 没有参数的maven命令,Maven就会用nightly-build 目标作为缺省目标。
接下来是几个名字空间的声明,如:
xmlns:j="jelly:core"
所有以j:作为前缀的节点,Jelly都会把它视为在core标识下 预定义的标签库。
xmlns:u="jelly:util"
所有以u:作为前缀的节点,Jelly都会把它视为在标识下 预定义的标签库。
所有在maven.xml文件使用的Jelly标签库,都必须在project节点中定义,并且 分配一个名称空间前缀。
Maven已经预先包含了jeez标签库作为空前缀。这个标签库在一个名称空间内包含了 ant 和 werkz 标签库。这样,任何werkz或ant标签都无须名称空间 即可使用,同时也简化了ant的迁移过程。
目标
goal是一个 werkz 标签,类似于Ant的target;它是包含了一系列可执行标签的容器。
由于jeez 标签库已经由maven预先注册了,一个目标(goal)可以包含任何合法的 Ant 标签。
为了执行在maven.xml中定义的目标,你只需要在命令行中为Maven指定目标名即可。要执行例子中定 义的nightly-build你只需执行命令:
maven nightly-build
Maven插件定义的目标需要在目标前加上插件名,这样一来,就不会和你自己的goal冲突,如 jar:jar就是 jar 插件定义的目标,用于创建项目的jar包。
Jelly编程
在每个目标里,由Jelly标签提供功能,让我们来看看例子里的这些代码。
set
<j:set var="goals" value="compile,test" />这行就是一个jelly的core标签set,它使用了project节点中定义的前缀 j:
set标签设置了一个由var属性定义的Jelly变量,值由 value 指定。和Ant的proerties不一样,Jelly变量在被赋值后仍可以改变。
mkdir
<mkdir dir="${maven.build.dir}" />等同于Ant任务 mkdir, 用于创建目录,目录名由变量 ${maven.build.dir}指定。
tokenize
<u:tokenize var="goals" delim=",">${goals}</u:tokenize>这行执行的是Jelly tokenize 标签。这是Jelly util 标签库中标签, 这个标签库已经在项目节点中预先定义:u:
tokenize标签在将节点中的内容分离成一个list,用于后续的处理。
var 属性就是将被于新list的变量。
delim 属性是用于分割字符串中的分隔符。
在这个例子中,tokenize 标签中节点值是一个变量:goals, 在前几行中,这是一个由逗号分隔、compile 与 test 的字符串。
forEach
<j:forEach items="${goals}" var="goal" indexVar="goalNumber">
Now attaining goal number ${goalNumber}, which is ${goal}
<attainGoal name="${goal}" />
</j:forEach>forEach标签提供简单循环功能,节点值就是循环体。
items 属性是一个表达式,是在循环过程中需要遍历的值集合。
集合中的值被逐个取出,存放在var 属性指定的变量中。你可以在 forEach 循环体访问这个变量。
indexVar 属性指定了一个计数器(起始基数为0)变量,用于在处理 过程中计数。
forEach 标签的节点值输出了一些在处理过程中的关于目标的文本,并使用 attainGoal werkz 标签来获得(执行?)这些目标。
这个结果就是产生了一个可以用于构建、管理任何基于java的项目。我们希望我们创造 的这个工具可以让Java开发者的日常工作更加轻松,并有助于理解基于java的项目.
项目对象模型
Maven是基于项目对象模型(POM)的概念而创建的。在这个模型中,所有由Maven产生的 artifact都是清晰定义的项目模型的结果。构建,文档,源码度量,源码交叉引用和其他 任何由Maven plug-in提供的东西都是由POM来控制的。
POM 处理机制
这篇文档简单的描述了Maven执行过程中是如何处理POM的。这里有一些简单的POM例子 来展示继承机制和插值机制。
POM 插值机制
POM(通常以project.xml的名字出现)现在已经被当作Jelly脚本来处理了。大部分时 候,用户无须关心project.xml文件是不是真正的Jelly脚本,但是,如果需要的话, 也可以使用内置值。我也不愿看到逻辑控制语句出现在project.xml中,但是由于 project.xml实际上已经是一个隐含的jelly的脚本,所以它会有足够的灵活性:-)。 下面是一个简单的例子。
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<pomVersion>3</pomVersion>
<groupId>maven</groupId>
<artifactId>maven</artifactId>
<name>Maven</name>
<currentVersion>1.0-b5-dev</currentVersion>
<organization>
<name>Apache Software Foundation</name>
<url>http://jakarta.apache.org/</url>
<logo>/images/jakarta-logo-blue.gif</logo>
</organization>
<inceptionYear>2001</inceptionYear>
<package>org.apache.${pom.artifactId}</package>
<logo>/images/${pom.artifactId}.jpg</logo>
<description>Maven is a project that was created in ${pom.inceptionYear}.</description>
<shortDescription>${pom.name} is a Java Project Management Tool</shortDescription>
</project>
POM 继承机制
现在有一种简单方式可以用于在POM中进行继承,下面是一个简单的例子:
<?xml version="1.0" encoding="ISO-8859-1"?>
<project>
<extend>project.xml</extend>
<groupId>super-extendo</groupId>
<artifactId>super-extendo<artifactId>
<name>Super Extendo</name>
<build>
<unitTest>
<includes>
<include>**/*Test*.java</include>
</includes>
<excludes>
<exclude>**/TestAll.java</exclude>
<exclude>**/*Abstract*.java</exclude>
</excludes>
</unitTest>
</build>
</project>
目前对POM父对象的解析还相对较弱,现在对一层以上的继承还没有做过任何测试。尽管如此, 单层继承加上插值机制已经能够给带来很多好处。这些机制的意图在于简化构建的共用问题。
你可以这样定义主模板:
<project>
<pomVersion>3</pomVersion>
<groupId>commons</groupId>
<artifactId>commons-master</artifactId>
<name>Commons Master Maven POM</name>
<organization>
<name>Apache Software Foundation</name>
<url>http://www.apache.org</url>
</organization>
<gumpRepositoryId>jakarta</gumpRepositoryId>
<url>http://jakarta.apache.org/commons/${pom.artifactId}.html</url>
<issueTrackingUrl>http://nagoya.apache.org/</issueTrackingUrl>
<siteAddress>jakarta.apache.org</siteAddress>
<siteDirectory>/www/jakarta.apache.org/commons/${pom.artifactId}/</siteDirectory>
<distributionDirectory>
/www/jakarta.apache.org/builds/jakarta-commons/${pom.artifactId}/
</distributionDirectory>
<repository>
<connection>
scm:cvsserver:anoncvs@cvs.apache.org:/home/cvspublic:jakarta-commons/${pom.artifactId}
</connection>
<url>http://cvs.apache.org/viewcvs/jakarta-commons/${pom.artifactId}/</url>
</repository>
...
</project>
子POM对象可以这样定义:
<project>
<groupId>commons-betwixt</groupId>
<artifactId>commons-betwixt</artifactId>
<name>Betwixt</name>
...
</project>
这样你就可以在父POM对象中,将子POM对象的${pom.artifactId}替换进去。有许多项目的 构建都以相似的方式进行构建,这样一来,对于项目的公共部分,你就可以使用一个主模板, 然后在子POM对象project.xml中定义有区别的部分,而这部分内容通常很少。
这种机制确实还可以简化那些需要产生多个JAR包的项目。由于project.xml和标准Ant构建 不会相互干扰,我计划在公共部分测试继承机制。
如果你对使用这种机制,DVSL报告会变成什么样感到疑惑,我要说,你很上路。我已经修改 了DVSL报告来适应POM本身,这就是说DVSL转换是基于java对象的。在使用继承和插值机制的 时候,为了正确的产生DVSL报告,这是很有必要的。象上面列出的子模板是无法工作的,我们 需要全面的解析POM。我能说的是,它可以工作了!我所使用的处理方式可能不是最有效率的 方式,但仍有提升的空间。因为POM只会被处理一次(不管怎么说,这就它的原理,我可能漏了 某些东西),然后到处使用,至少这就是我以前试图做的事情,所以我们很有可能会取得平衡。
如果你不使用继承和插值,那么一切照常工作。maven站点本身一切ok,有几个刚部署的站点 已经使用了我昨晚提交的东西了。
使用插件
Maven是一个很紧凑的内核,围绕着它的是许许多多的插件。Maven所有的功能都是由插件来提供 的。
maven.xml文件
项目中的maven.xml文件是Maven在执行过程中要使用的“定制”文件。
在这个文件中,你可以加入Maven构建中需要的额外处理。或者在Maven的“目标”前后附加自己 的代码,如jar 或 test。
Maven使用Jelly 脚本语言, 任何合法的jelly标签都可以在maven.xml中使用。
Maven所采用的goal功能是由werkz标签库提供。更多的信息请看 wiki页面.
简单的maven.xml例子
注意由于Maven并未缺省的定义'compile'目标,下面这个例子没法运行。
这是一个简单的maven.xml例子
<project
default="nightly-build"
xmlns:j="jelly:core"
xmlns:u="jelly:util">
<goal name="nightly-build">
<!-- Any ant task, or jelly tags can go here thanks to jeez -->
<j:set var="goals" value="compile,test" />
<mkdir dir="${maven.build.dir}" />
<u:tokenize var="goals" delim=",">${goals}</u:tokenize>
<j:forEach items="${goals}" var="goal" indexVar="goalNumber">
Now attaining goal number ${goalNumber}, which is ${goal}
<attainGoal name="${goal}" />
</j:forEach>
</goal>
</project>
你可能会注意到这里一些有意思的东西,下面我们将逐一解释。
project节点
project节点, <project>, 是任何 maven.xml 文件的根节点。
项目节点有一个缺省的属性:default="nightly-build",如果用户只是简单键入 没有参数的maven命令,Maven就会用nightly-build 目标作为缺省目标。
接下来是几个名字空间的声明,如:
xmlns:j="jelly:core"
所有以j:作为前缀的节点,Jelly都会把它视为在core标识下 预定义的标签库。
xmlns:u="jelly:util"
所有以u:作为前缀的节点,Jelly都会把它视为在标识下 预定义的标签库。
所有在maven.xml文件使用的Jelly标签库,都必须在project节点中定义,并且 分配一个名称空间前缀。
Maven已经预先包含了jeez标签库作为空前缀。这个标签库在一个名称空间内包含了 ant 和 werkz 标签库。这样,任何werkz或ant标签都无须名称空间 即可使用,同时也简化了ant的迁移过程。
目标
goal是一个 werkz 标签,类似于Ant的target;它是包含了一系列可执行标签的容器。
由于jeez 标签库已经由maven预先注册了,一个目标(goal)可以包含任何合法的 Ant 标签。
为了执行在maven.xml中定义的目标,你只需要在命令行中为Maven指定目标名即可。要执行例子中定 义的nightly-build你只需执行命令:
maven nightly-build
Maven插件定义的目标需要在目标前加上插件名,这样一来,就不会和你自己的goal冲突,如 jar:jar就是 jar 插件定义的目标,用于创建项目的jar包。
Jelly编程
在每个目标里,由Jelly标签提供功能,让我们来看看例子里的这些代码。
set
<j:set var="goals" value="compile,test" />这行就是一个jelly的core标签set,它使用了project节点中定义的前缀 j:
set标签设置了一个由var属性定义的Jelly变量,值由 value 指定。和Ant的proerties不一样,Jelly变量在被赋值后仍可以改变。
mkdir
<mkdir dir="${maven.build.dir}" />等同于Ant任务 mkdir, 用于创建目录,目录名由变量 ${maven.build.dir}指定。
tokenize
<u:tokenize var="goals" delim=",">${goals}</u:tokenize>这行执行的是Jelly tokenize 标签。这是Jelly util 标签库中标签, 这个标签库已经在项目节点中预先定义:u:
tokenize标签在将节点中的内容分离成一个list,用于后续的处理。
var 属性就是将被于新list的变量。
delim 属性是用于分割字符串中的分隔符。
在这个例子中,tokenize 标签中节点值是一个变量:goals, 在前几行中,这是一个由逗号分隔、compile 与 test 的字符串。
forEach
<j:forEach items="${goals}" var="goal" indexVar="goalNumber">
Now attaining goal number ${goalNumber}, which is ${goal}
<attainGoal name="${goal}" />
</j:forEach>forEach标签提供简单循环功能,节点值就是循环体。
items 属性是一个表达式,是在循环过程中需要遍历的值集合。
集合中的值被逐个取出,存放在var 属性指定的变量中。你可以在 forEach 循环体访问这个变量。
indexVar 属性指定了一个计数器(起始基数为0)变量,用于在处理 过程中计数。
forEach 标签的节点值输出了一些在处理过程中的关于目标的文本,并使用 attainGoal werkz 标签来获得(执行?)这些目标。