Maven实战

我本来已经决定停止更新该博客了,但是为了推广下我的新书《Maven实战》,在此破例一下。该书包含了我不少的经验总结和心血劳动,希望能让更多的人了解。

 

 简介
你是否早已厌倦了日复一日的手工构建工作?你是否对各个项目风格迥异的构建系统感到恐惧?Maven——这一Java社区事实标准的项目管理工具,能帮你从琐碎的手工劳动中解脱出来,帮你规范整个组织的构建系统。不仅如此,它还有依赖管理、自动生成项目站点等超酷的特性。已经有无数的开源项目使用它来构建项目,促进团队交流。每天都由数以万计的开发者在访问中央仓库以获取他们需要的依赖。

由国内的Maven权威亲自执笔,《Maven实战》告诉你Maven的本质是什么,深入阐述Maven的依赖管理、仓库、生命周期、插件等核心概念。你还能看到实际的案例演变,以及诸如多模块组织、建立Nexus私服、使用Hudson进行持续集成等企业开发的最佳实践。
 

 

前3章免费阅读:http://www.iteye.com/wiki/hzbook/2872-Maven-in-action

InfoQ免费迷你版本(包含第5章和第12章的内容):http://www.infoq.com/cn/minibooks/maven-in-action

本书的豆瓣页面:http://book.douban.com/subject/5345682/

本书的官方页面:http://www.juvenxu.com/mvn-in-action/

 

嗯,重要的是这个链接,去china-pub购买(你懂的):http://www.china-pub.com/197177

 

支持下我吧,具体的目录内容在上述各个链接中都有,这里我就不再赘述了。

最后,希望本书能给你带来帮助。

 

 

 

本文译自 http://www.sonatype.com/people/2010/02/now-available-m2eclipse-0-10-0/

 

这是一年多来m2eclipse的第一个生产适用版本,这已经是最快的速度了。在该版本中,你会看到我们分离了更新站点。现在有一个核心更新站点,和另外一个包含可选组件的额外更新站点。要了解详细的安装信息,请阅读m2eclipse站点上的安装指令

 

关于0.10.0有有点要注意:你无法从0.9.8或者0.9.9-dev升级到0.10.0。你必须卸载之前的版本,或者在一个新的eclipse上安装m2eclipse。推荐的Eclipse版本是3.5.1。你可以从http://www.eclipse.org/downloads下载eclipse分发包。本文解析来详细介绍m2eclipse 0.10.0的重要的新特性。

 

该版本重要的新特性

稳定性

过去的一年我们的工作主要在改进稳定性和性能。如果你曾经使用过m2eclipse 0.9.8,你会注意到显著的性能提升。

与Maven 3.0集成

该版本的m2eclipse包含了Maven 3.0-alpha-6+。Maven 3.0的重要目之一就是重新实现一些Maven的“内脏”,以便更容易的与其它框架集成如Eclipse IDE。使用Maven 3.0需要对项目做什么更改么?答案是什么都不需要变动。Maven 3.0是一次革命性的升级,它会支持下一代的开发工具,但是你不用对自己的项目做什么更改。它直接就能工作。

  • 与Maven 3.0命令行行为兼容
  • 教0.9.8有很大的性能提升
  • 完全支持settings.xml中的proxy/mirror/auth配置
  • Maven2用户要注意:如果你需要让m2eclipse使用Maven 2,你可以在m2eclipse选项中进行配置。

Maven项目生命周期匹配框架

该框架能让你自定义开发周期中的Maven插件与插件目标。如果你需要配置Maven Resources插件,使其在每次Eclipse项目构建的时候更新资源,那么你可以使用POM编辑器中一个新的选项卡来完成这件事情。

  • 使用新的Project Configurator API开发
  • 每种类型和每个单独项目的Eclipse项目配置和构建可以完全定制。
  • 实现了plexus-build-api,允许mojo参与到eclipse的增量/完全构建中。
  • 支持modello,plexus metadata,antlr3,build-helper,resources(来自额外更新站点)。

重新实现了nexus-indexer集成与仓库视图

m2eclipse与nexus-indexer集成非常紧密,用它来快速定位依赖和构件。该版本加入了一个新的仓库视图,能让你使用简单的界面查看、修改、和管理Maven仓库(包括你的本地Maven仓库)。

  • m2eclipse现在跟踪settings.xml和项目pom.xml中定义的仓库
  • 每个仓库都有新的选项用来关闭、最小化、和最大化索引细节。
  • 支持新的增量索引标准
  • 远程索引文件可以缓存在本地仓库,并共享给整个工作区,因此m2eclipse中的工作区初始化会更快。

预备性的eclipse 3.6支持

虽然我们还不支持使用eclipse 3.6,但是该版本开始为其添加了预备性的支持。

 

m2eclipse更新站点

核心组件:http://m2eclipse.sonatype.org/sites/m2e

可选组件:http://m2eclipse.sonatype.org/sites/m2e-extras

 

 

在多模块Maven项目中,反应堆(Reactor)是一个包含了所有需要构建模块的抽象概念,对于Maven用户来说,主要关心的是两点:

 

  1. 哪些模块会被包含到反应堆中?
  2. 反应堆中所有模块的构建顺序是什么?

 

例如有这样的一个多模块项目:

 

 

plexus-security包含了8个子模块,其中security-realms还包括了更进一层的两个子模块。

运行mvn clean install可以看到这样的输出:

 

Java代码
  1. [INFO] Scanning for projects...  
  2. [INFO] ------------------------------------------------------------------------  
  3. [INFO] Reactor Build Order:  
  4. [INFO]   
  5. [INFO] Plexus Security Aggregator  
  6. [INFO] Security: Parent  
  7. [INFO] Security: Model  
  8. [INFO] Security: XML Model  
  9. [INFO] Security: Configuration  
  10. [INFO] Security: System  
  11. [INFO] Security: Realms Parent  
  12. [INFO] Plexus XML Realm  
  13. [INFO] Security: REST API  
  14. [INFO] Plexus URL Realm  
  15. [INFO] Security Legacy Adapter  
  16. [INFO]                                                                           
  17. [INFO] ------------------------------------------------------------------------  

 

在默认情况下,Maven会根据多模块配置构建所有的模块,Maven还会根据模块间的依赖关系自动计算构建顺序,以确保被依赖的模块会先得以构建。值得一提的是,在这种情形下,Maven会将父模块看成是其子模块的依赖,因此该例中Security Parent会较先得以构建。

 

一般来说,我们要么构建整个项目,要么构建单个模块,但是有些时候,我们会想要仅仅构建这个完整的反应堆中的某些模块,换句话说,我们会需要裁剪反应堆。

例如说,我对模块security-configuration做了一些更改,而我知道在完整反应堆顺序中,security-model-xml和security-model在其之前,不会依赖它,因此就没必要构建他们。我们只需要构建security-configuration及其之后的项目。

 

Maven提供了很多命令行选项让我们自定义反应堆,输入mvn -h可以看到这样一些选项:

 

Java代码
  1. Options:  
  2.  -am,--also-make                        If project list is specified, also  
  3.                                         build projects required by the  
  4.                                         list  
  5.  -amd,--also-make-dependents            If project list is specified, also  
  6.  -pl,--projects <arg>                   Build specified reactor projects  
  7.                                         instead of all projects. A project  
  8.                                         can be specified by  
  9.                                         [groupId]:artifactId or by its  
  10.                                         relative path.  
  11.  -rf,--resume-from <arg>                Resume reactor from specified  
  12.                                         project  
 

 

--resume-from 表示从该模块恢复,也就是说选择从哪里开始构建,等于剪掉了完整反应堆的前面一部分。

例如我运行 mvn clean install -rf security-configuration/ ,就会得到这样的一个反应堆:

 

Java代码
  1. [INFO] ------------------------------------------------------------------------  
  2. [INFO] Reactor Build Order:  
  3. [INFO]   
  4. [INFO] Security: Configuration  
  5. [INFO] Security: System  
  6. [INFO] Security: Realms Parent  
  7. [INFO] Plexus XML Realm  
  8. [INFO] Security: REST API  
  9. [INFO] Plexus URL Realm  
  10. [INFO] Security Legacy Adapter  
  11. [INFO]                                                                           
  12. [INFO] ------------------------------------------------------------------------  
  

与完整反应堆相比,前面的四个模块不见了,只保留了security-system及其之后的模块。

 

--projects 表示手动选择需要构建的项目,项目间以逗号分隔。

例如我运行 mvn clean install -pl security-configuration/,security-realms/security-xml-realm/,会得到如下反应堆:

 

Java代码
  1. [INFO] ------------------------------------------------------------------------  
  2. [INFO] Reactor Build Order:  
  3. [INFO]   
  4. [INFO] Security: Configuration  
  5. [INFO] Plexus XML Realm  
  6. [INFO]                                                                           
  7. [INFO] ------------------------------------------------------------------------  

 

--also-make 的前提是--projects参数,表示同时构建所列模块依赖的其他模块。

例如我运行 mvn clean install -pl security-model-xml/ -am,会得到如下反应堆:

 

Java代码
  1. [INFO] ------------------------------------------------------------------------  
  2. [INFO] Reactor Build Order:  
  3. [INFO]   
  4. [INFO] Security: Parent  
  5. [INFO] Security: Model  
  6. [INFO] Security: XML Model  
  7. [INFO]                                                                           
  8. [INFO] ------------------------------------------------------------------------  

 

 

这里security-model-xml依赖于security-model,而security-parent是security-model-xml的父项目,因此这两个模块都会得以构建。

 

--also-make-dependents 的前提是--projects参数,表示同时构建那些依赖于所列模块的模块。

例如我运行 mvn clean install -pl security-model-xml/ -amd,会得到如下反应堆:

 

Java代码
  1. [INFO] ------------------------------------------------------------------------  
  2. [INFO] Reactor Build Order:  
  3. [INFO]   
  4. [INFO] Security: XML Model  
  5. [INFO] Plexus XML Realm  
  6. [INFO] Security: REST API  
  7. [INFO] Plexus URL Realm  
  8. [INFO] Security Legacy Adapter  
  9. [INFO]                                                                           
  10. [INFO] ------------------------------------------------------------------------  
 

 

除了security-model-xml本身,所有其他依赖于该模块的模块也会被加入到反应堆中。

 

除此之外,在-pl、-am或者-pl、-amd的基础上,还能应用-rf参数,紧接着上面的例子,再加上参数-rf -rf security-rest-api/,

如:mvn clean install -pl security-model-xml/ -amd -rf security-rest-api/,便可以得到如下的反应堆:

 

Java代码
  1. [INFO] ------------------------------------------------------------------------  
  2. [INFO] Reactor Build Order:  
  3. [INFO]   
  4. [INFO] Security: REST API  
  5. [INFO] Plexus URL Realm  
  6. [INFO] Security Legacy Adapter  
  7. [INFO]                                                                           
  8. [INFO] ------------------------------------------------------------------------  

 

 

这个反应堆表示:计算所有security-model-xml及依赖于它的模块,在次基础上,从security-rest-api模块开始构建。

 

在开发过程中,灵活应用上述4个参数,可以帮助我们跳过那些无须构建的项目模块,从而加速构建,当项目庞大,模块特别多的时候,这种效果就异常明显。

 

最后提一下,Maven从2.1版本才加入了此功能。 :)

注:本文节选自我正在编写的《Maven实战》

任何一个构件都有其唯一的坐标,根据这个坐标可以定义其在仓库中的唯一存储路径,这便是Maven的仓库布局方式。例如log4j:log4j:1.2.15这一依赖,其对应的仓库路径为log4j/log4j/1.2.15/log4j-1.2.15.jar,细心的读者可以观察到,该路径与坐标的大致对应关系为groupId/artifactId/version/artifactId-version.packaging。下面看一段Maven的源码并结合具体的实例来理解Maven仓库的布局方式:

Java代码
  1.     private static final char PATH_SEPARATOR = '/';  
  2.   
  3.     private static final char GROUP_SEPARATOR = '.';  
  4.   
  5.     private static final char ARTIFACT_SEPARATOR = '-';  
  6.   
  7.     public String pathOf( Artifact artifact )  
  8.     {  
  9.         ArtifactHandler artifactHandler = artifact.getArtifactHandler();  
  10.   
  11.         StringBuilder path = new StringBuilder( 128 );  
  12.   
  13.         path.append( formatAsDirectory( artifact.getGroupId() ) ).append( PATH_SEPARATOR );  
  14.         path.append( artifact.getArtifactId() ).append( PATH_SEPARATOR );  
  15.         path.append( artifact.getBaseVersion() ).append( PATH_SEPARATOR );  
  16.         path.append( artifact.getArtifactId() ).append( ARTIFACT_SEPARATOR ).append( artifact.getVersion() );  
  17.   
  18.         if ( artifact.hasClassifier() )  
  19.         {  
  20.             path.append( ARTIFACT_SEPARATOR ).append( artifact.getClassifier() );  
  21.         }  
  22.   
  23.         if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )  
  24.         {  
  25.             path.append( GROUP_SEPARATOR ).append( artifactHandler.getExtension() );  
  26.         }  
  27.   
  28.         return path.toString();  
  29. }  
  30.   
  31.     private String formatAsDirectory( String directory )  
  32.     {  
  33.         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );  
  34.     }  

 

该pathOf()方法的目的是根据构件信息生成其在仓库中的路径。在阅读本段代码之前,读者可以先回顾一下上一章Maven坐标的相关内容。这里,我们根据一个实际的例子来分析路径的生成,考虑这样一个构件:groupId=org.testng、artifactId=testng、version=5.8、classifier=jdk15、packaging=jar,其对应的路径按如下步骤生成:

  1. 首先基于构件的groupId准备路径,formatAsDirectory()将groupId中的句点分隔符转换成路径分隔符,该例中,groupId org.testng就会被转换成org/testng,之后再加一个路径分隔符斜杠,那么org.testng就成为了org/testng/。
  2. 基于构件的artifactId准备路径,也就是在前面的基础上加上artifactId以及一个路径分隔符,该例中的artifactId为testng,那么在这一步过后路径就成为了org/testng/testng/。
  3. 接着使用版本信息,在前面的基础上加上version和路径分隔符,该例中版本是5.8,那么路径就成为了org/testng/tesgng/5.8/。
  4. 这一步再依次加上artifactId,构件分隔符连字号,以及version,于是构建的路径就变成了org/testng/testng/5.8/testng-5.8。读者可能会注意到这里使用了artifactId.getVersion(),而上一步用的是artifactId.getBaseVersion(),version和baseVersion的区别在本章讨论SNAPSHOT的时候会具体阐述。
  5. 紧接着如果构件有classifier,就加上构件分隔符和classifier,该例中构件的classifier是jdk15,那么路径就变成org/testng/testng/5.8/testng-5.8-jdk5。
  6. 最后第检查构件的extension,若extension存在,则加上句点分隔符和extension,从代码中可以看到,extension是从artifactHandler而非artifact获取,artifactHandler是由项目的packaging决定的,因此可以说,packaging决定了构件的扩展名,该例的packaging是的jar,因此最终的路径为org/testng/testng/5.8/testng-5.8-jdk5.jar。

到这里笔者(包括读者你)都应该感谢Maven开源社区,正是由于Maven的所有源代码都是开放的,我们才能仔细得深入到其内部工作的所有细节。
由于Maven仓库是基于简单文件系统存储的,现在我们又理解了其存储方式,因此当遇到一些与仓库相关的问题时,可以很方便的查找相关文件,方便定位问题。例如当Maven无法获得项目声明的依赖时,可以简单该依赖对应的文件在仓库中是否存在,如果不存在,是否有其它版本可用,等等。

2009 - 11 - 10

Maven3初窥

Maven3的开发已经完成大半,本周末将发布alpha-3(Twitter @jvanzyl),而Nexus已经开始使用Maven3进行构建,于是我也开始使用Maven3。

 

http://svn.apache.org/repos/asf/maven/maven-3/trunk签出最新的maven3代码,然后使用maven2进行build,build完毕之后,便能得到Maven安装文件apache-maven/target/apache-maven-3.0-SNAPSHOT-bin.zip,接着升级本地的maven2至maven3,检查安装如下:

控制台输出
juven@juven-ubuntu:~$ mvn -v
Apache Maven 3.0-SNAPSHOT (r833360; 2009-11-06 19:53:14+0800)
Java version: 1.6.0_11
Java home: /usr/local/jdk1.6.0_11/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux" version: "2.6.27-7-generic" arch: "i386" Family: "unix"
 

Maven本身提倡的一个原则是,不要使用任何外部的SNAPSHOT依赖,这里我却在使用SNAPSHOT的Maven,未免有些讽刺的意味,因此难免有些担心。不过,build完Nexus之后,我的担心就消除了,使用maven3 build Nexus没有出现任何问题。而且从build输出我就发现maven3的一大优点,build输出更加明了:

Maven3输出
[INFO] Building Nexus (API) 1.4.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.2:clean (default-clean) @ nexus-api ---
[INFO] Deleting directory /home/juven/workspaces/ws-nexus/nexus/nexus-api/target
[INFO]
[INFO] --- maven-resources-plugin:2.4:resources (default-resources) @ nexus-api ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/juven/workspaces/ws-nexus/nexus/nexus-api/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ nexus-api ---
[INFO] Compiling 191 source files to /home/juven/workspaces/ws-nexus/nexus/nexus-api/target/classes
[INFO]
[INFO] --- plexus-component-metadata:1.2.1:generate-metadata (process-classes) @ nexus-api ---
[INFO] Discovered 1 component descriptors(s)
[INFO]
[INFO] --- maven-resources-plugin:2.4:testResources (default-testResources) @ nexus-api ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/juven/workspaces/ws-nexus/nexus/nexus-api/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:testCompile (default-testCompile) @ nexus-api ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- plexus-component-metadata:1.2.1:generate-test-metadata (process-test-classes) @ nexus-api ---

比较下Maven2的输出:

Maven2输出
[INFO] Building Spice Timeline
[INFO] task-segment: [clean, install]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting directory D:\demo\spice-timeline\target
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 14 source files to D:\demo\spice-timeline\target\classes
[INFO] [plexus-component-metadata:generate-metadata {execution: process-classes}]
[INFO] Discovered 3 component descriptors(s)
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Compiling 4 source files to D:\demo\spice-timeline\target\test-classes
[INFO] [plexus-component-metadata:generate-test-metadata {execution: process-test-classes}]
[INFO] [surefire:test]
[INFO] Surefire report directory: D:\demo\spice-timeline\target\surefire-reports

Maven3的输出中,正在运行的插件,以及build所处的项目信息更加清晰。使用缩进来标示插件,更容易找到各个插件的运行情况,而增加@ nexus-api这样的信息,就能在一个多模块构建中更方便的看到当前所build的项目。

 

目前,几乎没有什么关于Maven3的文档,不过有两篇博客和一个wiki提供了很多有用的信息:

Maven 3.0 technology preview: Interview with Benjamin Bentmann

Maven 3.x: Paving the desire lines — Part One

Maven 3.x Compatibility Notes

 

基于以上的信息以及我自己简单的使用经验,这里有一个简单的总结,归纳Maven3主要的目的和特性:

  • 重构和清理代码库
    Maven3减少了代码中模块的数量,并基于大量的集成测试,对Maven核心进行重大的重构。一方面方便修复一些之前由于Maven2架构问题无法修复的bug,另一方面,更简单优雅的代码也容易吸引开源爱好者的加入及贡献。
  • 完全兼容Maven2
    Maven3不会破坏现存的Maven2构建,为此Maven团队已经开发了500多个集成测试以覆盖各种情况,到GA版本的时候,这个数字会达到600。
  • 改进多模块构建
    Maven2的多模块构建中,模块A必须install之后才能被模块B使用,Maven3消除了这一限制,比如说模块A编译之后,模块B就能直接使用其输出class。
  • 改进插件版本解析机制
    Maven2的一大问题是用户可以不指定插件版本就能直接使用插件,而Maven会自动解析最新的插件版本(可能是SNAPSHOT),这造成了build的不可预知性,有时会因为某个插件版本的变化而导致build失败。自maven 2.0.9开始所有核心插件明确指定了版本,但这还不够,在Maven3中,如果发现有未指定版本的插件,Maven会发出警告,此外,如果你忽略警告,Maven也只会解析最新的RELEASE版本,而不会再使用SNAPSHOT。这样,既保持了对Maven2的兼容,也在最大程度上避免因插件版本不稳定引起的build失败。
  • 从核心分离site和reporting模块
    Maven2的核心生命周期包含了site,而site又与Doxia绑定,maven3将site以及reporting分离,通过插件,当前的maven2 site仍然可以使用,而且,使用非Doxia格式的site也成为了可能。
  • 更好的支持嵌入环境如CI和IDE
    Maven2缺乏对嵌入环境的考虑,maven3基于一些集成的经验如m2eclipse, hudson,对这一点进行了改进,因此,将来在IDE和CI服务器,或者Nexus中,集成Maven会更加方便。

最后,我将持续对Maven3进行关注,并在博客中更新相关信息。

  • 16:28
  • 评论 / 浏览 (4 / 9079)

新完成的用户故事:

  • [NEXUS-463] - 从搜索结果下载构件
  • [NEXUS-840] - SMTP服务器配置测试界面
  • [NEXUS-982] - 添加一个“新部署的发布版构件”RSS源
  • [NEXUS-1504] - 仓库信息面板
  • [NEXUS-1510] - 集成自动问题报告
  • [NEXUS-1551] - 添加一个RSS源以显示异常和错误
  • [NEXUS-1666] - 重做了构件上传
  • [NEXUS-1668] - 在服务器配置面板添加一个“发送测试email”按钮,以测试SMTP配置
  • [NEXUS-1765] - 增强安全以控制view访问
  • [NEXUS-1767] - 在权限/角色页面中,支持shift及ctrl多选
  • [NEXUS-1771] - 提升了JS性能
  • [NEXUS-1977] - 集成Nexus特定的增量索引
  • [NEXUS-2127] - 将仓库浏览分隔成两个tab,一个为本地存储内容,另外一个为索引内容
  • [NEXUS-2168] - 优化ehcache的内存使用
  • [NEXUS-2371] - 支持可插拔的索引管理器
  • [NEXUS-2381] - 支持可扩展的搜索
  • [NEXUS-2399] - 当scheduled task失败的时候发送警告邮件
  • [NEXUS-2541] - 在用户和角色界面添加更多的细节

完整的relese notes可以查看:http://nexus.sonatype.org/change-history.html

 

此外,1.4.0 对商业版本Nexus Pro做了很多增强,详细可以看这篇博客:http://www.sonatype.com/people/2009/10/new-features-in-nexus-and-nexus-professional-14/

 

可以从这里下载Nexushttp://nexus.sonatype.org/download-nexus.html

 

Nexus还以电子书的形式提供了完善的文档:http://www.sonatype.com/books/nexus-book/reference/

 

初次接触Nexus可以参考:Nexus入门指南(图文)

  • 17:22
  • 评论 / 浏览 (2 / 1748)

我们常常会使用mvn archetype:generate来创建一个项目的骨架,这本身是Maven一个非常有趣的功能,你甚至可以定义自己的项目骨架,但是,我听到不止一次有用户说,这个命令没法用,不妨试试:

 

GMT +8:00,2009年10月12日零点整,我运行mvn archetype:generate,得到的是一堆出错信息:

 

[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Internal error in the plugin manager executing goal 'org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-5-SNAPSHOT:generate': Unable to
load the mojo 'org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-5-SNAPSHOT:generate' in the plugin 'org.apache.maven.plugins:maven-archetype
plugin'. A required class is missing: org/codehaus/plexus/util/xml/XmlStreamReader
org.codehaus.plexus.util.xml.XmlStreamReader
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12 seconds
[INFO] Finished at: Mon Oct 12 00:00:15 CST 2009
[INFO] Final Memory: 8M/15M
[INFO] ------------------------------------------------------------------------

 

一条标准得不能再标准的命令,得到的结果却是一对看不懂的出错信息,实在令人沮丧。其实出错的原因很简单,maven-archetype-plugin没有被认为是Maven的核心插件,也就没有在超级POM中为其设定版本,于是,我们运行archetype命令的时候,maven就去中央仓库下载最新的SNAPSHOT,而恰恰这个SNAPSHOT是有问题的,是完全无法工作的,于是我们看到了上面的结果。

 

解决方案很简单,就是有点烦,我们需要在运行archetype命令的时候指定其版本,命令如下:

 

mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:generate

 

指定groupId, artifactId, version,2.0-alpha-4是目前的最新版本,试试吧,现在archetype又能正常工作了,他会提示你一大堆可选的archetype类型,我看到了41个之多,个人还是最喜欢默认的15: internal -> maven-archetype-quickstart (),在需要一个简单的Maven项目进行测试的时候,非常有用。

 

 

更多Maven相关讨论:加入Maven中文讨论组:http://groups.google.com/group/maven-zh

最近Eclipse社区进行了一项调查,关注社区如何使用Eclipse和其它开源软件,目前调查的结果已经发布,详细的结果涵盖了IDE,操作系统,版本控制工具等等,具体可以查看这个链接:

 

http://www.eclipse.org/org/press-release/20090527_survey09.php

 

作为Maven的忠实用户,我自然比较关注构建工具这一块,这是我看到的报告:

 

 

这个图表本身有就有一些问题,持续集成工具如CruiseControl和Hudson与构建工具Maven和Ant本身是没有冲突的,它们是互补的。抛开这个不谈,我们可以看到Ant以33.3%的得票率远超得票率为18.0%的Maven。这当然是可以理解的,Maven和Ant相比还比较年轻。此外,该图中有一个Other(specify)项,我点击show replies后看到,其中有一大部分make,shell之类的纯脚本。当然,我们也看到21.7%的None,也就是说还有很大部分人完全不用构建工具。

 

这是Eclipse社区的调查,这里我可以简单的总结下,约1/3的人在使用Ant构建项目,约1/5的人在使用Maven构建项目,还有一小部分不到1/10的人在使用make和shell,约5/1的人不用任何工具构建项目。当然这还不完整,由于统计本身的缺陷我无法得知其它人用什么工具。不过总这个简单的结果我们已经能够清楚的看到一些现状,Ant作为成熟流行的java构建构建工具仍是主流,Maven作为新一代构建工具也得到了广泛的认可,还有一些古董在用Make/shell。而不借助工具执行纯手工构建还大有人在。

 

这份调查是不分国界的,其中中国参与人数为3.1%。我相信在中国,统计出来的数据结果肯定比这个数据差很多,可能不用任何构建工具的人会超过一半。虽然我很想了解,但我根本无从得知有百分之多少的人在用Maven。5%?呵呵,希望我的一些工作能帮助增加一两个百分点。

 

此外再扯一下持续集成工具,这里主要有CruiseControl,Hudson,和Bamboo。前两者是开源的,而Bamboo对于商业使用是要收费的,占用率低一点也可以理解。Hudson最为流行,用户为CruiseControl的两倍,这也能理解,毕竟易用性方面Hudson强很多。在自动化构建的基础上进行持续集成,是重要的敏捷实践之一。

 

总之,好的工具能助你事半功倍,有那么多优秀的开源工具,我们应该衷心感谢这些工具背后的程序员们。

  • 15:03
  • 评论 / 浏览 (6 / 3430)

2010-08-27更新

关注本书请访问我为此专门创建的页面:http://www.juvenxu.com/mvn-in-action/

 

 

由于《Maven权威指南》由于一些原因暂时无法在国内出版,我决心使用业余时间编写一本《Maven实战》,以方便在国内更好的推广Maven技术。为了使本书能更好的满足国内Maven用户的需要,我希望能在编写过程中得到大家的反馈并不断改进。这里是初步的一个目录,欢迎任何意见,谢谢。

 

 

《Maven实战》

前言
为何写这本书
谁该阅读本书
如何阅读本书

第一部分 入门篇

第1章 Maven简介
1.1 何为Maven
1.2 为什么需要Maven
1.2.1 组装PC和品牌PC
1.2.2 IDE不是万能的
1.2.3 Make
1.2.4 Ant
1.2.5 现状,困惑
1.3 Maven与极限编程
1.4 被误解的Maven

第2章 安装Maven
2.1 下载Maven
2.2 安装Maven
2.2.1 在Windows上安装Maven
2.2.2 在Linux上安装Maven
2.3 安装目录及文件
2.3.1 安装文件目录
2.3.2 ~/.m2目录
2.4 安装m2eclipse
2.5 安装NetBeans Maven插件

第3章 Hello World
3.1 编写POM
3.2 编写主代码
3.3 编写测试代码
3.4 使用Archetype生成骨架
3.5 使用m2eclipse
3.6 使用 NetBeans Maven插件

第二部分 核心概念篇

第4章 背景案例
4.1 Amazon图书查询服务
4.2 Amazon-book背景需求
4.3 Amazon-book领域模型

第5章 Maven坐标和依赖
5.1 Maven坐标系统
5.1.1 坐标的组成元素
5.1.2 如何定义坐标
5.2 配置简单依赖
5.3 依赖范围(scope)
5.4 传递性依赖
5.5 Maven的依赖解析机制
5.6 最佳实践
5.6.1 依赖归类
5.6.2 依赖分析
5.6.3 排除传递性依赖

第6章 Maven仓库
6.1 本地仓库
6.1.1 仓库的布局
6.2 远程仓库
6.2.1 Maven中央仓库
6.2.2 搜索远程仓库
6.3 仓库元数据
6.3 在POM中配置远程仓库
6.4 在Settings中配置远程仓库
6.5 部署构件至远程仓库

第7章 Maven生命周期
7.1 生命周期模型
7.2 三套独立的生命周期
7.3 生命周期阶段
7.4 使用插件绑定任务
7.5 内置打包类型绑定任务
7.6 命令行视角

第8章 Maven插件
8.1 插件模型
8.2 配置简单插件
8.3 常用插件
8.3.1 核心插件
8.4 获取插件信息
8.4.1 在线资源
8.4.2 使用maven-help-plugin
8.5 最佳实践
8.5.1 设定插件版本

第三部分 进阶篇

第9章 遵循约定
9.1 为什么要遵循约定
9.2 Maven中的约定
9.3 超级POM

第10章 创建仓库服务——使用Nexus
10.1 Nexus简介
10.2 下载和安装
10.3 代理外部仓库
10.4 管理本地仓库
10.5 仓库组
10.6 构件搜索
10.7 配置Maven使用Nexus
10.8 部署构件至Nexus
10.9 小结

第11章 使用Maven执行单元测试
11.1编写测试用例
11.2执行测试
11.3 查看测试报告
11.4 自定义测试
11.4.1 跳过单元测试
11.4.2 运行单个测试
11.4.3 包含和排除测试
11.6 调试测试
11.5 执行TestNG测试
11.6 重用其它模块的测试类

第12章 使用Maven构建Web应用
12.1 Web应用的目录结构
12.2 Amazon-book的前端实现
12.3 自动化部署
12.3.1 自动化部署至Jetty
12.3.2自动化部署至Tomcat
12.4 添加和过滤web资源文件

第13章 划分模块
13.1 为什么要划分模块
13.2 如何划分模块
13.2.1多模块
13.2.2继承
12.3 模块化amazon-book
12.4 amazon-book持久层模块
12.5 amazon-book命令行模块
12.6 在父模块管理依赖
12.7 在父模块管理插件

第14章 生成项目报告
14.1 生成项目站点
14.2 项目信息
14.3 项目报告
14.3.1 javadoc报告
14.3.2 源码报告
14.3.3 测试覆盖率报告
14.4.4 checkstyle报告

第15章 隔离构建环境
15.1 资源过滤
15.2 使用profile
15.2.1 在POM中定义profile
15.2.2 在Settings中定义profile
15.3 隔离amazon-book的数据库配置

第16章 项目版本管理
16.1 Maven的版本机制
16.2 发布版vs.快照版
16.3 maven-release-plugin介绍
16.4 amazon-book版本发布
16.5 amazon-book版本分支

第17章 充分利用IDE
17.1 m2eclipse最佳实践
17.1.1 集成subversion
17.1.2 图形化依赖分析
17.1.3 Nexus索引浏览
17.2 NetBeans Maven插件最佳实践
17.2.1 集成subversion
17.2.2 高效POM编辑
17.2.3 Nexus索引浏览

第18章 持续集成——使用Hudson
18.1 持续集成概述
18.2 Hudson概述
18.3 下载和安装
18.4 创建Maven持续集成任务
18.5 持续集成报告
18.6 协作模式

第19章 编写Maven插件
19.1 什么时候编写插件
19.2 Plexus介绍
19.3 插件描述符
19.4 开始编写插件
19.5 暴露配置点
19.6 使用该插件

附录
POM详解
Settings详解
索引

2010/04/29更新:我已经在个人网站提供了PDF下载 http://www.juvenxu.com/mvn-def-guide/

 

最近抽了两天时间,把该书的序啊,前言啊,附录啊,零零碎碎的部分都翻译完毕了,也算是一次完整的发布,我称之为Beta 0.2。中文版不可能与英文版完全同步,于是中文版有自己的版本号,和英文版的Beta 0.2不尽一致。

 

目前最新的英文版本是0.6-SNAPHSOT,较目前中文版本的主要变化在于增加了“Using Maven Archetypes”和"Developing with Flexmojos"两章,此外,关于Nexus和M2Eclipse的章节被移到了单独的书中,并进行了很大更新。详情见:http://www.sonatype.com/books/maven-book/reference/public-book.html。这本Maven书一直在不停更新,也没说什么时候是个头(这是好事,不是么?),所以中文版也会持续更新。

 

这次发布最好的消息应该是修复了PDF中文字体的问题,我要十分十分感谢docbx-maven-plugin,在其2.0.8版本中,添加了对于自定义字体的支持,我经过一阵折腾终于将中文PDF构建成功。这不是之前流传的山寨版本,该版本包含所有章节(不再中英混杂),此外,由于maven一书的构建是基于Maven的,因此以后PDF生成都是自动的事情,可以随时和docbook源码一起更新,并发布。

 

猛击这个地址吧:http://www.sonatype.com/books/maven-book/reference_zh/public-book.html

 

如图点击便可下载了,注意Sonatype会建议你填写一些基本信息,填一下吧,享受了资源也要稍微回报下 :)

 

记不清是去年什么时候开始着手翻译本书了,我也不废话说什么锻炼英语啥的,其实像这样的技术书英语没啥难度。在这个过程中最大的感受是这一文档制作的流程,从docbook源码,到maven构建书籍,到git管理源码,这是很值得大家学习和参与的,因此,以后我不会再花大力气在图书的翻译更新上,我会写文档介绍这一流程,并帮助有兴趣的人去体验这个流程,为开源社区尽上一点力。

 

哦,如果你还不知道,我强调一下,《Maven权威指南》是开源的。你可以阅读下本书的”版权“部分。

 

希望Maven的明天会更好,特别希望Maven在中国的明天会更好。

 

2009 - 05 - 14

Nexus Indexer 2.0:增量下载

原文:http://www.sonatype.com/people/2009/05/nexus-indexer-20-incremental-downloading/

 

Nexus Indexer已经变得十分流行,它已经成为了索引Maven仓库的事实标准(包括大个子,中央仓库)。随着仓库变得越来越大,索引的大小也随之增长。从最初几百kb的文件,渐渐得增长至20-30mb的文件。大家看到索引是仓库内容的一个很好的入口(不仅有Maven使用仓库,用户也直接使用仓库),于是索引文件成了被下载得最多的文件,中央仓库中这个20mb的文件每天被数千的用户下载,带宽消耗十分巨大。为了解决这个问题,我们为Nexus Indexer引入了增量索引处理机制。这包括两个部分,生产者构建增量索引为消费者提供下载,以及消费者从生产者处获取增量索引。

 

构建增量索引

当中央仓库的每日执行任务创建索引的时候,最近的内容被存储到nexus-maven-repository-index.gz文件中(所有全部内容)。这个文件只是作为一个后备,为了防止万一:1,消费者没有正确的处理增量索引;2,用户落后太久了,而生产者不再拥有用户需要的增量部分。除了这个文件,每个增量索引的生成包含了自上次索引生成的所有变化(添加/更新/删除)。增量文件很小,与完整索引相比,大部分情况基于天的增量索引大约就10kb。增量文件在文件nexus-maven-repository-index.properties中列出,伴随有一个chain-id。这个chain-id是用来“重置”增量链,用来判断是否由于某些原因需要下载完整索引。

 

获取增量索引

如果消费者程序和Nexus Indexer集成了(至少要2.0版),那么就没什么可担心了,Nexus Indexer会管理下载其缺失的增量索引片段,如果索引混乱了(请求一个生产者不再支持的增量片段,或者chain-id不同),indexer就会下载完整索引文件,然后在下次更新的时候开始检查增量变化。

 

这是所有文件the nexus-maven-repository-index.properties处理的内容:

  • nexus.index.chain-id:这是当前所有增量条目的chain-id。如果什么时候该值变化了,造成与消费者本地properties文件不一致,那么消费者就需要触发一次完整的.gz索引下载(当然同时更新properties文件)
  • nexus.index.last-incremental:这是可用的最新增量条目,只是简单的一个插入到下载文件名中的整数。如果消费者本地properties文件中拥有同样的值,就不需要下载任何增量。
  • nexus.index.incremential-X:这是为每个可用增量条目罗列的值。第一个条目(X=0)是生产者仍然维护的最旧的增量片段。如果消费者本地properties文件的last-incremental的值小于这里第一个条目的值,就需要下载完整.gz索引(以及properties文件)。否则,就只需要下载生产者提供的每一个nexus-maven-repository-index.X.gz文件(X大于消费者本地的last-incremental值,小于等于远程的last-incremental值)。

支持遗留索引应用

当然我们不希望将所有遗留应用拒之门外,因此旧的基于时间戳的properties同样可用:

  • nexus.index.time:遗留.zip索引上次创建的时间戳。如果该时间戳和你本地属性文件的不同,就需要下载完整.zip索引。
  • nexus.index.timestamp:遗留.gz索引上次创建的时间戳。如果该时间戳和你本地属性文件中的不同,就需要下载完整.gz索引。


因此为了简单明了的用好增量索引这个特性,如果你的引用已经和Nexus Indexer集成了,那么你绝对应该更新到版本2.0.0,以节省巨大的带宽消耗。最新的m2eclipse 0.9.8已经完成了这一步,Nexus 1.4也会相应的升级。

什么是版本管理

首先,这里说的版本管理(version management)不是指版本控制(version control),但是本文假设你拥有基本的版本控制的知识,了解subversion的基本用法。版本管理中说得版本是指构件(artifact)的版本,而非源码的版本(如subversion中常见的rXXX,或者git中一次提交都有个sha1的commit号)。

比如我有一个项目,其artifactId为myapp,随着项目的进展,我们会生成这样一些jar:myapp-1.0-SNAPSHOT.jar,myapp-1.0.jar,myapp-1.1-SNAPSHOT.jar,myapp-1.0.1.jar等等。你可能会说,这很简单啊,我在POM中改个version,mvn clean install不就完了?但这只是表面,本文我将讲述,snapshot和release版本的区别,如何自动化版本发布(如果你的项目有几十个module,你就会觉得手工改POM来升级版本是很痛苦的事情),结合自动化发布的过程,这里还会介绍maven-release-plugin。此外,一些scm概念也会被涉及到,比如tag和branch。

 

前提:版本控制

不管怎样,我们都需要建立一个项目并提交到SCM中,这里我以subversion为例。你得有一个配置好的subversion repository,这里我建立了一个空的svn仓库,其地址为:https://192.168.1.100:8443/svn/myapp/ 现在,该目录下只有三个空的典型的子目录:/trunk/, branches/, tags/。分别用来存放主干,分支,以及标签。

接着将项目导入到svn仓库中,到项目根目录,运行如下命令:

svn import -m 'project initialization' https://192.168.1.100:8443/svn/myapp/trunk
(注意,这么做你会将目录下所有文件导入到svn库中,但是这其中某些目录和文件是不应该被导入的,如/target目录,以及eclipse相关的项目文件)

目前,我们将项目的版本设置为1.0-SNAPSHOT。

 

为什么用SNAPSHOT?

我先说说如果没有SNAPSHOT会是什么样子。假设你的项目有2个模块,A,B,其中A依赖B。这三个模块分别由甲,乙两个个人负责开发。在开发过程中,因为A是依赖于B的,因此乙每次做一个改动都会影响到甲,于是,乙提交了一些更改后,需要让甲看到。这个时候,怎么做呢?乙对甲说,“你签出我的代码,build一下就OK了”,甲有点不情愿,但还是照做了,签出代码,svn clean install,然后,发现build出错了,有个测试没有pass。甲郁闷了,对乙说,“你的代码根本不能用,我不想build,你build好了给我”,乙看了看确实自己的代码build不过,于是回去解决了,然后打了个jar包,扔给甲,甲对了对groupId,artifactId,放到了自己的.m2/repository/目录下,OK,能用了。

于是乙每次更新都这样做,打包,复制,然后甲粘贴,使用……渐渐的,大家发现这是个很笨的办法,这是纯手工劳动阿,程序员最BS的就是重复劳动。一天,甲对乙说,“你知道nexus么?你把你的jar发布到nexus上就可以了,我要用就自动去下载,这多棒!”乙说“哦?有这好东西,我去看看”于是乙发现了nexus这块新大陆,并成功的发布了B到nexus上。(见,Nexus入门指南,(图文))。

但是,请注意,我们这里的一切都假设没有SNAPSHOT,因此如果乙不更改版本,甲下载一次如B-1.0.jar之后,maven认为它已经有了正确的B的版本,就不会再重新下载。甲发现了这个问题,对乙说“你的更新我看不到,你更新了么?”乙说“不可能!我看看”,于是检查一下甲下载的C-1.0.jar,发现那是几天前的。乙一拍脑袋,说“这简单,我更新一下我的版本就好了,我发布个B-1.1.jar上去,你更新下依赖版本”,甲照做了,似乎这么做是可行的。

这里有一个问题,一次提交就更新一个版本,这明显不是正确的管理办法,此外,乙得不停的通知甲更新对B的依赖版本,累不累阿?1.0,或者说1.1,2.0,都代表了稳定,这样随随便便的改版本,能稳定么?

所以Maven有SNAPSHOT版本的概念,它与release版本对应,后者是指1.0,1.1,2.0这样稳定的发布版本。

现在乙可以将B的版本设置成1.0-SNAPSHOT,每次更改后,都mvn deploy到nexus中,每次deploy,maven都会将SNAPSHOT改成一个当前时间的timestamp,比如B-1.0-SNAPSHOT.jar到nexus中后,会成为这个样子:B-1.0-20081017-020325-13.jar。Maven在处理A中对于B的SNAPSHOT依赖时,会根据这样的timestamp下载最新的jar,默认Maven每天更新一次,如果你想让Maven强制更新,可以使用-U参数,如:mvn clean install -U

现在事情简化成了这个样子:乙做更改,然后mvn deploy,甲用最简单的maven命令就能得到最新的B。

 

从1.0-SNAPSHOT到1.0到1.1-SNAPSHOT

SNAPSHOT是快照的意思,项目到一个阶段后,就需要发布一个正式的版本(release版本)。一次正式的发布需要这样一些工作:

  1. 在trunk中,更新pom版本从1.0-SNAPSHOT到1.0
  2. 对1.0打一个svn tag
  3. 针对tag进行mvn deploy,发布正式版本
  4. 更新trunk从1.0到1.1-SNAPSHOT

你可以手工一步步的做这些事情,无非就是一些svn操作,一些pom编辑,还有一些mvn操作。但是你应该明白,手工做这些事情,一来繁琐,而来容易出错。因此这里我介绍使用maven插件来自动化这一系列动作。

SCM

首先我们需要在POM中加入scm信息,这样Maven才能够替你完成svn操作,这里我的配置如下:

Xml代码
  1. <scm>  
  2.   <connection>scm:svn:http://192.168.1.100:8443/svn/myapp/trunk/</connection>  
  3.   <developerConnection>scm:svn:https://192.168.1.100:8443/svn/myapp/trunk/</developerConnection>  
  4. </scm>  

需要注意的是,很多windows使用的tortoiseSVN客户端,而没有svn命令行客户端,这会导致Maven所有svn相关的工作失败,因此,你首先确保svn --version能够运行。

分发仓库

想要让Maven帮我们自动发布,首先我们需要配置好分发仓库。关于这一点,见Maven最佳实践:Maven仓库——分发构件至远程仓库。

maven-release-plugin

紧接着,我们需要配置maven-release-plugin,这个插件会帮助我们升级pom版本,提交,打tag,然后再升级版本,再提交,等等。基本配置如下:

Xml代码
  1. <plugin>  
  2.   <groupId>org.apache.maven.plugins</groupId>  
  3.   <artifactId>maven-release-plugin</artifactId>  
  4.   <version>2.0-beta-7</version>  
  5.   <configuration>  
  6.     <tagBase>https://192.168.1.100:8443/svn/myapp/tags/</tagBase>  
  7.   </configuration>  
  8. </plugin>  

GAV我就不多解释了,这里我们需要注意的是configuration元素下的tagBase元素,它代表了我们svn中的tag目录,也就是说,maven-release-plugin帮我们打tag的时候,其基础目录是什么。这里,我填写了svn仓库中的标准的tags目录。

提交代码

接着,确保你的所有代码都提交了,如果你有未提交代码,release插件会报错,既然你要发布版本了,就表示代码是稳定的,所以要么要么把代码提交了,要么把本地的更改抛弃了。

开始工作

现在,屏住呼吸,执行:

mvn release:prepare

执行过程中,你会遇到这样的提示:

What is the release version for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) 1.0: :

——“你想将1.0-SNAPSHOT发布为什么版本?默认是1.0。”我要的就是1.0,直接回车。

What is SCM release tag or label for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) myapp-1.0: :

——“发布的tag标签名称是什么?默认为myapp-1.0。”我还是要默认值,直接回车。

What is the new development version for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) 1.1-SNAPSHOT: :

——“主干上新的版本是什么?默认为1.1-SNAPSHOT。”哈,release插件会自动帮我更新版本到1.1-SNAPSHOT,很好,直接回车。

然后屏幕刷阿刷,maven在build我们的项目,并进行了一些svn操作,你可以仔细查看下日志。

那么结果是什么呢?你可以浏览下svn仓库:

  • 我们多了一个tag:https://192.168.1.100:8443/svn/myapp/tags/myapp-1.0/,这就是需要发布的版本1.0。
  • 再看看trunk中的POM,其版本自动升级成了1.1-SNAPSHOT。

这不正是我们想要的么?等等,好像缺了点什么,对了,1.0还没有发布到仓库中呢。

再一次屏住呼吸,执行:

mvn release:perform

maven-release-plugin会自动帮我们签出刚才打的tag,然后打包,分发到远程Maven仓库中,至此,整个版本的升级,打标签,发布等工作全部完成。我们可以在远程Maven仓库中看到正式发布的1.0版本。

这可是自动化的正式的版本发布!

 

Maven的版本规则

前面我们提到了SNAPSHOT和Release版本的区别,现在看一下,为什么要有1.0,1.1,1.1.1这样的版本,这里的规则是什么。

Maven主要是这样定义版本规则的:

<主版本>.<次版本>.<增量版本>

比如说1.2.3,主版本是1,次版本是2,增量版本是3。

主版本一般来说代表了项目的重大的架构变更,比如说Maven 1和Maven 2,在架构上已经两样了,将来的Maven 3和Maven 2也会有很大的变化。次版本一般代表了一些功能的增加或变化,但没有架构的变化,比如说Nexus 1.3较之于Nexus 1.2来说,增加了一系列新的或者改进的功能(仓库镜像支持,改进的仓库管理界面等等),但从大的架构上来说,1.3和1.2没什么区别。至于增量版本,一般是一些小的bug fix,不会有重大的功能变化。

一般来说,在我们发布一次重要的版本之后,随之会开发新的版本,比如说,myapp-1.1发布之后,就着手开发myapp-1.2了。由于myapp-1.2有新的主要功能的添加和变化,在发布测试前,它会变得不稳定,而myapp-1.1是一个比较稳定的版本,现在的问题是,我们在myapp-1.1中发现了一些bug(当然在1.2中也存在),为了能够在段时间内修复bug并仍然发布稳定的版本,我们就会用到分支(branch),我们基于1.1开启一个分支1.1.1,在这个分支中修复bug,并快速发布。这既保证了版本的稳定,也能够使bug得到快速修复,也不同停止1.2的开发。只是,每次修复分支1.1.1中的bug后,需要merge代码到1.2(主干)中。

上面讲的就是我们为什么要用增量版本。

 

实战分支

目前我们trunk的版本是1.1-SNAPSHOT,其实按照前面解释的版本规则,应该是1.1.0-SNAPSHOT。

现在我们想要发布1.1.0,然后将主干升级为1.2.0-SNAPSHOT,同时开启一个1.1.x的分支,用来修复1.1.0中的bug。

首先,在发布1.1.0之前,我们创建1.1.x分支,运行如下命令:

mvn release:branch -DbranchName=1.1.x -DupdateBranchVersions=true -DupdateWorkingCopyVersions=false

这是maven-release-plugin的branch目标,我们指定branch的名称为1.1.x,表示这里会有版本1.1.1, 1.1.2等等。updateBranchVersions=true的意思是在分支中更新版本,而updateWorkingCopyVersions=false是指不更改当前工作目录(这里是trunk)的版本。

在运行该命令后,我们会遇到这样的提示:

What is the branch version for "Unnamed - org.myorg:myapp:jar:1.1-SNAPSHOT"? (org.myorg:myapp) 1.1-SNAPSHOT: :

——"分支中的版本号是多少?默认为1.1-SNAPSHOT" 这时我们想要的版本是1.1.1-SNAPSHOT,因此输入1.1.1-SNAPSHOT,回车,maven继续执行直至结束。

接着,我们浏览svn仓库,会看到这样的目录:https://192.168.1.100:8443/svn/myapp/branches/1.1.x/,打开其中的POM文件,其版本已经是1.1.1-SNAPSHOT。

分支创建好了,就可以使用release:prepare和release:perform为1.1.0打标签,升级trunk至1.2.0-SNAPSHOT,然后分发1.1.0。

至此,一切OK。

 

小结

本文讲述了如何使用Maven结合svn进行版本管理。解释了Maven中SNAPSHOT版本的来由,以及Maven管理版本的规则。并结合SCM的tag和branch概念展示了如何使用maven-release-plugin发布版本,以及创建分支。本文涉及的内容比较多,且略显复杂,不过掌握版本管理的技巧对于项目的正规化管理来说十分重要。Maven为我们提供了一些一套比较成熟的机制,值得掌握。

什么是Maven仓库

在不用Maven的时候,比如说以前我们用Ant构建项目,在项目目录下,往往会看到一个名为/lib的子目录,那里存放着各类第三方依赖jar文件,如log4j.jar,junit.jar等等。每建立一个项目,你都需要建立这样的一个/lib目录,然后复制一对jar文件,这是很明显的重复。重复永远是噩梦的起点,多个项目不共用相同的jar文件,不仅会造成磁盘资源的浪费,也使得版本的一致性管理变得困难。此外,如果你使用版本管理工具,如SVN(你没有使用版本管理工具?马上试试SVN吧,它能帮你解决很多头疼的问题),你需要将大量的jar文件提交到代码库里,可是版本管理工具在处理二进制文件方面并不出色。

Maven仓库就是放置所有JAR文件(WAR,ZIP,POM等等)的地方,所有Maven项目可以从同一个Maven仓库中获取自己所需要的依赖JAR,这节省了磁盘资源。此外,由于Maven仓库中所有的JAR都有其自己的坐标,该坐标告诉Maven它的组ID,构件ID,版本,打包方式等等,因此Maven项目可以方便的进行依赖版本管理。你也不在需要提交JAR文件到SCM仓库中,你可以建立一个组织层次的Maven仓库,供所有成员使用。

简言之,Maven仓库能帮助我们管理构件(主要是JAR)。

 

本地仓库 vs. 远程仓库

运行Maven的时候,Maven所需要的任何构件都是直接从本地仓库获取的。如果本地仓库没有,它会首先尝试从远程仓库下载构件至本地仓库,然后再使用本地仓库的构件。

比如说,你的项目配置了junit-3.8的依赖,在你运行mvn test的时候,Maven需要使用junit-3.8的jar文件,它首先根据坐标查找本地仓库,如果找到,就直接使用。如果没有,Maven会检查可用的远程仓库配置,然后逐个尝试这些远程仓库去下载junit-3.8的jar文件,如果远程仓库存在该文件,Maven会将其下载到本地仓库中,继而使用。如果尝试过所有远程仓库之后,Maven还是没能够下载到该文件,它就会报错。

Maven缺省的本地仓库地址为${user.home}/.m2/repository。也就是说,一个用户会对应的拥有一个本地仓库。

你也可以自定义本地仓库的位置,修改${user.home}/.m2/settings.xml

Xml代码
  1. <settings>  
  2.   ...  
  3.   <localRepository>D:\java\repository</localRepository>  
  4.   ...  
  5. </settings>  

你还可以在运行时指定本地仓库位置:

mvn clean install -Dmaven.repo.local=/home/juven/myrepo/

还有一点需要理解的是,当我们运行install的时候,Maven实际上是将项目生成的构件安装到了本地仓库,也就是说,只有install了之后,其它项目才能使用此项目生成的构件。

了解了本地仓库,接着了解一下Maven缺省的远程仓库,即Maven中央仓库。

安装好Maven之后,我们可以建立一个简单的项目,配置一些简单的依赖,然后运行mvn clean install,项目就构建好了。我们没有手工的去下载任何jar文件,这一切都是因为Maven中央仓库的存在,当Maven在本地仓库找不到需要的jar文件时,它会查找远程仓库,而一个原始的Maven安装就自带了一个远程仓库——Maven中央仓库。

这个Maven中央仓库是在哪里定义的呢?在我的机器上,我安装了maven-2.0.10,我可以找到这个文件:${M2_HOME}/lib/maven-2.0.10-uber.jar,打开该文件,能找到超级POM:\org\apache\maven\project\pom-4.0.0.xml,它是所有Maven POM的父POM,所有Maven项目继承该配置,你可以在这个POM中发现如下配置:

Xml代码
  1. <repositories>  
  2.   <repository>  
  3.     <id>central</id>  
  4.     <name>Maven Repository Switchboard</name>  
  5.     <layout>default</layout>  
  6.     <url>http://repo1.maven.org/maven2</url>  
  7.     <snapshots>  
  8.       <enabled>false</enabled>  
  9.     </snapshots>  
  10.   </repository>  
  11. </repositories>  

关于远程仓库的配置,下面的小节我会详细解释,这里我们只要知道,中央仓库的id为central,远程url地址为http://repo1.maven.org/maven2,它关闭了snapshot版本构件下载的支持。

 

在POM中配置远程仓库

前面我们看到超级POM配置了ID为central的远程仓库,我们可以在POM中配置其它的远程仓库。这样做的原因有很多,比如你有一个局域网的远程仓库,使用该仓库能大大提高下载速度,继而提高构建速度,也有可能你依赖的一个jar在central中找不到,它只存在于某个特定的公共仓库,这样你也不得不添加那个远程仓库的配置。

这里我配置一个远程仓库指向中央仓库的中国镜像:

Xml代码
  1. <project>  
  2. ...  
  3.   <repositories>  
  4.     <repository>  
  5.       <id>maven-net-cn</id>  
  6.       <name>Maven China Mirror</name>  
  7.       <url>http://maven.net.cn/content/groups/public/</url>  
  8.       <releases>  
  9.         <enabled>true</enabled>  
  10.       </releases>  
  11.       <snapshots>  
  12.         <enabled>false</enabled>  
  13.       </snapshots>  
  14.     </repository>  
  15.   </repositories>  
  16.   <pluginRepositories>  
  17.     <pluginRepository>  
  18.       <id>maven-net-cn</id>  
  19.       <name>Maven China Mirror</name>  
  20.       <url>http://maven.net.cn/content/groups/public/</url>  
  21.       <releases>  
  22.         <enabled>true</enabled>  
  23.       </releases>  
  24.       <snapshots>  
  25.         <enabled>false</enabled>  
  26.       </snapshots>      
  27.     </pluginRepository>  
  28.   </pluginRepositories>  
  29. ...  
  30. </project>  

我们先看一下<repositories>的配置,你可以在它下面添加多个<repository> ,每个<repository>都有它唯一的ID,一个描述性的name,以及最重要的,远程仓库的url。此外,<releases><enabled>true</enabled></releases>告诉Maven可以从这个仓库下载releases版本的构件,而<snapshots><enabled>false</enabled></snapshots>告诉Maven不要从这个仓库下载snapshot版本的构件。禁止从公共仓库下载snapshot构件是推荐的做法,因为这些构件不稳定,且不受你控制,你应该避免使用。当然,如果你想使用局域网内组织内部的仓库,你可以激活snapshot的支持。

关于<repositories>的更详细的配置及相关解释,请参考:http://www.sonatype.com/books/maven-book/reference_zh/apas02s08.html。

至于<pluginRepositories>,这是配置Maven从什么地方下载插件构件(Maven的所有实际行为都由其插件完成)。该元素的内部配置和<repository>完全一样,不再解释。

 

在settings.xml中配置远程仓库

我们知道了如何在POM中配置远程仓库,但考虑这样的情况:在一个公司内部,同时进行这3个项目,而且以后随着这几个项目的结束,越来越多的项目会开始;同时,公司内部建立一个Maven仓库。我们统一为所有这些项目配置该仓库,于是不得不为每个项目提供同样的配置。问题出现了,这是重复

其实我们可以做到只配置一次,在哪里配置呢?就是settings.xml。

不过事情没有那么简单,不是简单的将POM中的<repositories>及<pluginRepositories>元素复制到settings.xml中就可以,setting.xml不直接支持这两个元素。但我们还是有一个并不复杂的解决方案,就是利用profile,如下:

Xml代码
  1. <settings>  
  2.   ...  
  3.   <profiles>  
  4.     <profile>  
  5.       <id>dev</id>  
  6.       <!-- repositories and pluginRepositories here-->  
  7.     </profile>  
  8.   </profiles>  
  9.   <activeProfiles>  
  10.     <activeProfile>dev</activeProfile>  
  11.   </activeProfiles>  
  12.   ...  
  13. </settings>  

这里我们定义一个id为dev的profile,将所有repositories以及pluginRepositories元素放到这个profile中,然后,使用<activeProfiles>元素自动激活该profile。这样,你就不用再为每个POM重复配置仓库。

使用profile为settings.xml添加仓库提供了一种用户全局范围的仓库配置。

 

镜像

如果你的地理位置附近有一个速度更快的central镜像,或者你想覆盖central仓库配置,或者你想为所有POM使用唯一的一个远程仓库(这个远程仓库代理的所有必要的其它仓库),你可以使用settings.xml中的mirror配置。

以下的mirror配置用maven.net.cn覆盖了Maven自带的central:

Xml代码
  1. <settings>  
  2. ...  
  3.   <mirrors>  
  4.     <mirror>  
  5.       <id>maven-net-cn</id>  
  6.       <name>Maven China Mirror</name>  
  7.       <url>http://maven.net.cn/content/groups/public/</url>  
  8.       <mirrorOf>central</mirrorOf>  
  9.     </mirror>  
  10.   </mirrors>  
  11. ...  
  12. </settings>  

 

这里唯一需要解释的是<mirrorOf>,这里我们配置central的镜像,我们也可以配置一个所有仓库的镜像,以保证该镜像是Maven唯一使用的仓库:

Xml代码
  1. <settings>  
  2. ...  
  3.   <mirrors>  
  4.     <mirror>  
  5.       <id>my-org-repo</id>  
  6.       <name>Repository in My Orgnization</name>  
  7.       <url>http://192.168.1.100/maven2</url>  
  8.       <mirrorOf>*</mirrorOf>  
  9.     </mirror>  
  10.   </mirrors>  
  11. ...  
  12. </settings>  

关于更加高级的镜像配置,可以参考:http://maven.apache.org/guides/mini/guide-mirror-settings.html。

 

分发构件至远程仓库

mvn install会将项目生成的构件安装到本地Maven仓库,mvn deploy用来将项目生成的构件分发到远程Maven仓库。本地Maven仓库的构件只能供当前用户使用,在分发到远程Maven仓库之后,所有能访问该仓库的用户都能使用你的构件。

我们需要配置POM的distributionManagement来指定Maven分发构件的位置,如下:

Xml代码
  1. <project>    
  2.   ...    
  3.   <distributionManagement>    
  4.     <repository>    
  5.       <id>nexus-releases</id>    
  6.       <name>Nexus Release Repository</name>    
  7.       <url>http://127.0.0.1:8080/nexus/content/repositories/releases/</url>    
  8.     </repository>    
  9.     <snapshotRepository>    
  10.       <id>nexus-snapshots</id>    
  11.       <name>Nexus Snapshot Repository</name>    
  12.       <url>http://127.0.0.1:8080/nexus/content/repositories/snapshots/</url>    
  13.     </snapshotRepository>    
  14.   </distributionManagement>    
  15.   ...    
  16. </project>    

Maven区别对待release版本的构件和snapshot版本的构件,snapshot为开发过程中的版本,实时,但不稳定,release版本则比较稳定。Maven会根据你项目的版本来判断将构件分发到哪个仓库。

一般来说,分发构件到远程仓库需要认证,如果你没有配置任何认证信息,你往往会得到401错误。这个时候,如下在settings.xml中配置认证信息:

Xml代码
  1. <settings>    
  2.   ...    
  3.   <servers>    
  4.     <server>    
  5.       <id>nexus-releases</id>    
  6.       <username>admin</username>    
  7.       <password>admin123</password>    
  8.     </server>    
  9.     <server>    
  10.       <id>nexus-snapshots</id>    
  11.       <username>admin</username>    
  12.       <password>admin123</password>    
  13.     </server>      
  14.   </servers>    
  15.   ...    
  16. </settings>  

需要注意的是,settings.xml中server元素下id的值必须与POM中repository或snapshotRepository下id的值完全一致。将认证信息放到settings下而非POM中,是因为POM往往是它人可见的,而settings.xml是本地的。

 

小结

本文介绍了Maven仓库,它是什么?本地仓库,远程仓库,中央仓库具体是指什么?并介绍了如何在POM中配置项目层次的仓库,在settings中配置用户层次的仓库,以及mirror。本文还介绍了如何安装构件到本地仓库,如何分发构件至仓库。

  • 17:42
  • 评论 / 浏览 (5 / 9445)

(2011-01-27更新,由于私人原因,该镜像已关闭,在此致歉)

 

感谢Shuqun的无私,他帮助建立了一个Maven中央仓库的中国镜像。

 

该镜像的地址为:http://maven.net.cn/content/groups/public/

 

当你访问中央仓库速度很慢的时候,或许你可以试试这个镜像。需要注意的是,无论你使用中央仓库也好,该镜像也好,任何其它公共仓库也好,请不要重复下载,浪费资源。我的意思是,如果你的组织中有不止一个人使用Maven,请建立自己的仓库,你可以使用Nexus,见:Nexus入门指南

 

由于该镜像使用Nexus建立,你可以通过Nexus的UI搜索artifact,访问http://maven.net.cn/,搜索你感兴趣的artifact,或者使用ExtJS的树状UI进行浏览。

 

同时我也寻找任何有意向为开源世界做贡献的个人或组织,如果您愿意提供资源建立Maven镜像,我将提供力所能及的技术帮助。

Nexus介绍

Nexus是Maven仓库管理器,如果你使用Maven,你可以从Maven中央仓库下载所需要的构件(artifact),但这通常不是一个好的做法,你应该在本地架设一个Maven仓库服务器,在代理远程仓库的同时维护本地仓库,以节省带宽和时间,Nexus就可以满足这样的需要。此外,他还提供了强大的仓库管理功能,构件搜索功能,它基于REST,友好的UI是一个extjs的REST客户端,它占用较少的内存,基于简单文件系统而非数据库。这些优点使其日趋成为最流行的Maven仓库管理器。

 

下载和安装

你可以从http://nexus.sonatype.org/downloads/下载最新版本的Nexus,笔者使用的是1.3.0版本。

Nexus提供了两种安装方式,一种是内嵌Jetty的bundle,只要你有JRE就能直接运行。第二种方式是WAR,你只须简单的将其发布到web容器中即可使用。

 

Bundle方式安装

解压nexus-webapp-1.3.0-bundle.zip至任意目录,如D:\dev_tools,然后打开CMD,cd至目录D:\dev_tools\nexus-webapp-1.3.0\bin\jsw\windows-x86-32,运行Nexus.bat。你会看到Nexus的启动日志,当你看到“Started SelectChannelConnector@0.0.0.0:8081”之后,说明Nexus启动成功了,然后打开浏览器,访问http://127.0.0.1:8081/nexus,你会看到如下的页面:

要停止Nexus,Ctrl+C即可,此外InstallNexus.bat可以用来将Nexus安装成一个windows服务,其余的脚本则对应了启动,停止,暂停,恢复,卸载Nexus服务。

 

WAR方式安装

你需要有一个能运行的web容器,这里以Tomcat为例,加入Tomcat的安装目录位于D:\dev_tools\apache-tomcat-6.0.18,首先我们将下载的nexus-webapp-1.3.0.war重命名为nexus.war,然后复制到D:\dev_tools\apache-tomcat-6.0.18\webapps\nexus.war,然后启动CMD,cd到D:\dev_tools\apache-tomcat-6.0.18\bin\目录,运行startup.bat。一切OK,现在可以打开浏览器访问http://127.0.0.1:8080/nexus,你会得到和上图一样的界面。

 

代理外部Maven仓库

登陆

要管理Nexus,你首先需要以管理员身份登陆,点击界面右上角的login,输入默认的登录名和密码:admin/admin123,登陆成功后,你会看到左边的导航栏增加了很多内容:

这里,可以管理仓库,配置Nexus系统,管理任务,管理用户,角色,权限,查看系统的RSS源,管理及查看系统日志,等等。你会看到Nexus的功能十分丰富和强大,本文,笔者只介绍一些最基本的管理和操作。

 

代理Maven中央仓库

点击左边导航栏的Repositories,界面的主面板会显示所有一个所有仓库及仓库组的列表,你会看到它们的Type字段的值有group,hosted,proxy,virtual。这里我们不关心virtual,只介绍下另外三种类型:

  • hosted,本地仓库,通常我们会部署自己的构件到这一类型的仓库。
  • proxy,代理仓库,它们被用来代理远程的公共仓库,如maven中央仓库。
  • group,仓库组,用来合并多个hosted/proxy仓库,通常我们配置maven依赖仓库组。

由此我们知道,我们需要配置一个Maven中央仓库的proxy,其实Nexus已经内置了Maven Central,但我们需要做一些配置。点击仓库列表中的Maven Central,你会注意到它的Policy是release,这说明它不会代理远程仓库的snapshot构件,这是有原因的,远程仓库的snapshot版本构件不稳定且不受你控制,使用这样的构件含有潜在的风险。然后我们发现主面板下方有三个Tab,分别为Browse,Configuration和Mirrors,我们点击Configuration进行配置,你现在需要关心的是两个配置项:“Remote Storage Location”为远程仓库的地址,对于Maven Central来说是http://repo1.maven.org/maven2/;“Download Remote Indexes”顾名思义是指是否下载远程索引文件,Maven Central的该字段默认为False,这是为了防止大量Nexus无意识的去消耗中央仓库的带宽(中央仓库有大量的构件,其索引文件也很大)。这里我们需要将其设置为True,然后点击Save。在Nexus下载的中央仓库索引文件之后,我们就可以在本地搜索中央仓库的所有构件。下图展示了我们刚才所涉及的配置:

 

添加一个代理仓库

这里我们再举一个例子,我们想要代理Sonatype的公共仓库,其地址为:http://repository.sonatype.org/content/groups/public/。步骤如下,在Repositories面板的上方,点击Add,然后选择Proxy Repository,在下方的配置部分,我们填写如下的信息:Repository ID - sonatype;Repository Name - Sonatype Repository;Remote Storage Location - http://repository.sonatype.org/content/groups/public/。其余的保持默认值,需要注意的是Repository Policy,我们不想代理snapshot构件,原因前面已经描述。然后点击Save。配置页面如下:

 

管理本地Maven仓库

Nexus预定义了3个本地仓库,分别为Releases,Snapshots,和3rd Party。这三个仓库都有各自明确的目的。Releases用于部署我们自己的release构件,Snapshots用于部署我们自己的snapshot构件,而3rd Party用于部署第三方构件,有些构件如Oracle的JDBC驱动,我们不能从公共仓库下载到,我们就需要将其部署到自己的仓库中。

当然你也可以创建自己的本地仓库,步骤和创建代理仓库类似,点击Repository面板上方的Add按钮,然后选择Hosted Repository,然后在下方的配置面板中输入id和name,注意这里我们不再需要填写远程仓库地址,Repository Type则为不可修改的hosted,而关于Repository Policy,你可以根据自己的需要选择Release或者Snapshot,如图:

 

管理Maven仓库组

Nexus中仓库组的概念是Maven没有的,在Maven看来,不管你是hosted也好,proxy也好,或者group也好,对我都是一样的,我只管根据groupId,artifactId,version等信息向你要构件。为了方便Maven的配置,Nexus能够将多个仓库,hosted或者proxy合并成一个group,这样,Maven只需要依赖于一个group,便能使用所有该group包含的仓库的内容。

Nexus预定义了“Public Repositories”和“Public Snapshot Repositories”两个仓库组,前者默认合并所有预定义的Release仓库,后者默认合并所有预定义的Snapshot仓库。我们在本文前面的部分创建了一个名为“Sonatype Repository”的仓库,现在将其合并到“Public Repositories”中。

点击仓库列表中的“Public Repositories”,然后选择下方的"Configuration" Tab,在配置面板中,将右边“Avaiable Repositories”中的“Sonatype Repository”拖拽到左边的“Ordered Group Repository”中,如图:

创建仓库组和创建proxy及hosted仓库类似,这里不再赘述。需要注意的是format字段需要填写“maven2”,添加你感兴趣的仓库即可。

 

搜索构件

在浩大的Maven仓库中一下下点击链接,浏览路径以寻找感兴趣的构件是一件很郁闷的事情。Nexus基于nexus-indexer提供构件搜索功能,要想对仓库进行搜索,无论是hosted,proxy,或者group,你都必须确认索引文件存在。这一点对于代理仓库尤其重要,有些远程仓库可能根本就没有索引,所以你无法搜索这些代理仓库。有些远程仓库的远程索引非常大,如中央仓库达到了70M左右,那么第一次下载索引需要花很多时间,所以要期望得到搜索结果,确保看到如下的文件:

一旦你的Nexus拥有了本地或者远程仓库的索引文件,你就可以享受Nexus的构件搜索功能了。不论登陆与否,你都可以使用关键字进行模糊搜索,比如我在左边导航栏上部的搜索框内输入junit,然后点击搜索按钮,右边立刻会分页显示500多条的junit相关构件信息。如果你了解更多的信息,你也可以通过限定groupId,artifactId,version进行搜索,点击导航栏中的“Advanced Search”,点击右边所有页面左上角的下拉框,选择“GAV Search”。笔者这里输入junit:junit:4.4,然后回车:

选择一项搜索结果,在页面下方会显示“Artifact Information”的面板,你可以点击"artifact"或者"pom"下载对应文件,而该面板右边更显示了一个Maven依赖配置,你可以直接复制该配置到Maven POM中,这是个十分方便的特性。

此外,值得一提的是,Nexus还支持基于classname的搜索,你只需点击搜索页面右上角的下拉框,选择“Classname Search”,然后输入类名即可,这里我不再赘述。

 

配置Maven使用Nexus

默认情况下,Maven依赖于中央仓库,这是为了能让Maven开箱即用,但仅仅这么做明显是错误的,这会造成大量的时间及带宽的浪费。既然文章的前面已经介绍了如何安装和配置Nexus,现在我们就要配置Maven来使用本地的Nexus,以节省时间和带宽资源。

我们可以将Repository配置到POM中,但一般来说这不是很好的做法,原因很简单,你需要为所有的Maven项目重复该配置。因此,这里我将Repository的配置放到$user_home/.m2/settings.xml中:

Xml代码
  1. <settings>  
  2. ...  
  3. <profiles>  
  4.   <profile>  
  5.     <id>dev</id>  
  6.     <repositories>  
  7.       <repository>  
  8.         <id>local-nexus</id>  
  9.         <url>http://127.0.0.1:8080/nexus/content/groups/public/</url>  
  10.         <releases>  
  11.           <enabled>true</enabled>  
  12.         </releases>  
  13.         <snapshots>  
  14.           <enabled>true</enabled>  
  15.         </snapshots>  
  16.       </repository>  
  17.     </repositories>  
  18.   </profile>  
  19. </profiles>  
  20. <activeProfiles>  
  21.   <activeProfile>dev</activeProfile>  
  22. </activeProfiles>  
  23. ...  
  24. </settings>  

由于我们不能直接在settings.xml中插入<repositories>元素,这里我们编写了一个profile,并添加了一个profile并使用<activeProfile>元素自动将这个profile激活。这里的local-nexus仓库指向了刚才我们配置的Nexus中“Public Repositories”仓库组,也就是说,所有该仓库组包含的仓库都能供我们使用。此外,我们通过<releases>和<snapshots>元素激活了Maven对于仓库所有类型构件下载的支持,当然你也可以调节该配置,比如说禁止Maven从Nexus下载snapshot构件。

使用该配置,Maven就会从你的Nexus服务器下载构件了,速度和从Central下载可不是一个数量级的。

 

部署构件至Nexus

Nexus提供了两种方式来部署构件,你可以从UI直接上传,也可以配置Maven部署构件。

 

通过Nexus UI部署

有时候有个jar文件你无法从公共Maven仓库找到,但是你能从其它得到这个jar文件(甚至是POM),那么你完全可以将这个文件部署到Nexus中,使其成为标准流程的一部分。步骤如下:

点击左边导航栏的"Repository",在右边的仓库列表中选择一个仓库,如“3rd Party”,然后会看到页面下方有四个tab,选择最后一个“Upload”,你会看到构件上传界面。选择你要上传的构件,并指定POM,(或者手工编写GAV等信息),最后点击Upload,该构件就直接被部署到了Nexus的"3rd Party"仓库中。如图:


通过Maven部署

更常见的用例是:团队在开发一个项目的各个模块,为了让自己开发的模块能够快速让其他人使用,你会想要将snapshot版本的构件部署到Maven仓库中,其他人只需要在POM添加一个对于你开发模块的依赖,就能随时拿到最新的snapshot。

以下的pom.xml配置和settings.xml能让你通过Maven自动化部署构件:

pom.xml

Xml代码
  1. <project>  
  2. ...  
  3. <distributionManagement>  
  4.   <repository>  
  5.     <id>nexus-releases</id>  
  6.       <name>Nexus Release Repository</name>  
  7.       <url>http://127.0.0.1:8080/nexus/content/repositories/releases/</url>  
  8.   </repository>  
  9.   <snapshotRepository>  
  10.     <id>nexus-snapshots</id>  
  11.     <name>Nexus Snapshot Repository</name>  
  12.     <url>http://127.0.0.1:8080/nexus/content/repositories/snapshots/</url>  
  13.   </snapshotRepository>  
  14. </distributionManagement>  
  15. ...  
  16. </project>  

settings.xml

Xml代码
  1. <settings>  
  2. ...  
  3. <servers>  
  4.   <server>  
  5.     <id>nexus-releases</id>  
  6.     <username>admin</username>  
  7.     <password>admin123</password>  
  8.   </server>  
  9.   <server>  
  10.     <id>nexus-snapshots</id>  
  11.     <username>admin</username>  
  12.     <password>admin123</password>  
  13.   </server>    
  14. </servers>  
  15. ...  
  16. </settings>  

这里我们配置所有的snapshot版本构件部署到Nexus的Snapshots仓库中, 所有的release构件部署到Nexus的Releases仓库中。由于部署需要登陆,因为我们在settings.xml中配置对应Repository id的用户名和密码。

然后,在项目目录中执行mvn deploy,你会看到maven将项目构件部署到Nexus中,浏览Nexus对应的仓库,就可以看到刚才部署的构件。当其他人构建其项目时,Maven就会从Nexus寻找依赖并下载。

 

总结

本文介绍强大的仓库管理器——Nexus,包括如何下载安装Nexus,配置Nexus代理中央仓库,管理Nexus的代理仓库,本地仓库,以及仓库组。并帮助你了解如何通过Nexus搜索构件。最后,如何在Maven中配置Nexus仓库,以及如何部署构件到Nexus仓库中。这些都是Nexus中最基本也是最常用的功能。随着使用的深入,你会发现Nexus还有很多其它的特性,如用户管理,角色权限管理等等。

Nexus的OSS版本是完全开源的,如果你有兴趣,你可以学习其源码,甚至自己实现一个REST客户端。

马上拥抱Nexus吧,它是免费的!

 

  • 23:07
  • 评论 / 浏览 (33 / 20405)

又是漫长的一章内容,但如同本书的任何一章一样,由于原作者都是Maven社区的committer,其内容非常精彩,虽然之前编写过Maven插件,但在翻译的过程中,我还是受益匪浅。昨天还和一位朋友谈到,翻译一本书,相当于读这本书三遍,译前浏览一遍,译时字字句句研读,译后还有review。因此,翻译是一个很好的学习过程,无论是英语还是技术本身。(当然,如果自己对领域一窍不通,只求学习,那就还是别害人了)

 

“编写插件”一章概要:

  • 什么是IoC?IoC这么好,Maven当然也用了,这里就当普及一下知识。
  • Plexus,也许你用过Spring,但Maven用的IoC容器可是Plexus。
  • 插件描述符,所有Maven插件都是由该描述符定义的,虽然你不需要编写该文件,但你需要能读懂它。
  • 如果编写插件?插件由Mojo构成,Mojo有一系列Mojo注解,以及Mojo参数与注解所描述。
  • Maven根据注解(非Java5注解,原因请看本章内容)生成插件描述符。
  • 插件和生命周期的关系,学会在插件中配置生命周期。
  • 在插件中使用特定的异常和日志。

 

由于Maven的核心基本不干什么事情,所有实际的任务都是由插件来完成的,因此理解插件对于理解Maven来说十分重要,在有需要的时候,你也会想自己编写插件,来自定义Maven的行为,你会发现这件事情其实不难,而且会十分有趣。

 

本章在线浏览地址:

http://www.sonatype.com/books/maven-book/reference_zh/writing-plugins.html

 

"If I have seen further it is by standing on the shoulders of Giants" —— Isaac Newton (1642-1727)

 

有人认为Maven是一个依赖管理工具,当然这种想法是错误的(确切的说Maven是一个项目管理工具,贯穿了整个项目生命周期,编译,测试,打包,发布...),但Maven给人造成这种错误的印象也是有原因的,因为Maven的依赖管理十分强大,用好了Maven,你不再需要面对一大堆jar感到头大,依赖冲突,无用依赖等问题也能够得到有效的防止和解决。本节介绍如何用好Maven的依赖管理。

 

最简单的依赖

依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成。因此,使用任何一个依赖之间,你都需要知道它的Maven坐标,关于如何寻找Maven坐标,《搜索Maven仓库》 一文可以帮助你。

最简单的依赖如:

Xml代码
  1. <dependency>  
  2.   <groupId>junit</groupId>  
  3.   <artifactId>junit</artifactId>  
  4.   <version>4.4</version>  
  5. </dependency>  

上例中我们声明了一个对junit的依赖,它的groupId是junit, artifactId是junit, version是4.4。这一组GAV构成了一个Maven坐标,基于此,Maven就能在本地或者远程仓库中找到对应的junit-4.4.jar文件。

 

依赖归类

随着项目的增大,你的依赖越来越多,比如说你依赖了一堆spring的jar,有org.spring.framework:spring-core, org.spring.framework:beans, org.spring.framework:spring-web, org.spring.framework:spring-mock。它们的groupId是相同的,artifactId不同。为了管理其版本,你对它们进行过统一的升级,逐个的将version改成了最新版。但是,显然,当POM很大的时候你说不定会犯错误,而当版本不一致的时候,一些诡异的兼容性问题就可能出现。

对此,Maven有它的解决方案:

Xml代码
  1. <dependencies>  
  2.   <dependency>  
  3.     <groupId>org.spring.framework</groupId>  
  4.     <artifactId>spring-core</artifactId>  
  5.     <version>${spring.version}</version>  
  6.   </dependency>  
  7.   <dependency>  
  8.     <groupId>org.spring.framework</groupId>  
  9.     <artifactId>spring-beans</artifactId>  
  10.     <version>${spring.version}</version>  
  11.   </dependency>  
  12.   <dependency>  
  13.     <groupId>org.spring.framework</groupId>  
  14.     <artifactId>spring-web</artifactId>  
  15.     <version>${spring.version}</version>  
  16.   </dependency>  
  17.   <dependency>  
  18.     <groupId>org.spring.framework</groupId>  
  19.     <artifactId>spring-mock</artifactId>  
  20.     <version>${spring.version}</version>  
  21.   </dependency>  
  22. </dependencies>  
  23.   
  24. <properties>  
  25.   <spring.version>2.5</spring.version>  
  26. </properties>  

这里我们定义了一个Maven属性,其名称为spring.version,值是2.5。在这个POM中,我们就能用${spring.version}的方式来引用该属性。我们看到,所有spring相关的依赖的version元素现在都成了${spring.version},当Maven运行的时候,它会自动用值2.5来替换这个引用。

当我们需要升级spring的时候,只要更改一个地方便可,而且,你现在能很高的保证所有的spring依赖包都是同一个版本。

 

依赖范围(scope)

本文的第一个例子其实是有漏洞的,对于Junit,一般来说你只有在运行测试的时候需要它,也就是说,它对于src/main/java的classpath没什么意义,并且,将Junit的jar文件打入最终的发布包也不是好事,这无谓的增加了发布包的大小。

其实我们应该这样做:

Xml代码
  1. <dependency>  
  2.   <groupId>junit</groupId>  
  3.   <artifactId>junit</artifactId>  
  4.   <version>4.4</version>  
  5.   <scope>test</test>  
  6. </dependency>  

于是,junit对于主源码classpath不可用,对于测试源码classpath可用,不会被打包。

再举个例子,在开发javaee应用的时候我们一定会用到servlet-api,它对于主源码和测试源码都是必要的,因为我们的代码中会引入servlet-api的包。但是,在打包的时候,将其放入WAR包就会有问题,因为web容器会提供servlet-api,如果我们再将其打包就会造成依赖冲突,解决方案如下:

Xml代码
  1. <dependency>  
  2.   <groupId>javax.servlet</groupId>  
  3.   <artifactId>servlet-api</artifactId>  
  4.   <version>2.4</version>  
  5.   <scope>provided</scope>  
  6. </dependency>  

将依赖范围设置成provided,就意味着该依赖对于主源码classpath,以及测试classpath可用,但不会被打包。这正是servlet-api所需要的。

这里归纳一下主要的依赖范围以及作用:

 

依赖范围(scope)主源码classpath可用测试源码classpath可用会被打包
compile 缺省值TRUETRUETRUE
testFALSETRUEFALSE
runtimeFALSETRUETRUE
providedTRUETRUEFALSE

 

 

 

 

 

 

 

 

需要注意的是,当我们没有声明依赖范围的时候,其默认的依赖范围是compile。

 

分类器(classifer)

GAV是Maven坐标最基本最重要的组成部分,但GAV不是全部。还有一个元素叫做分类器(classifier),90%的情况你不会用到它,但有些时候,分类器非常不可或缺。

举个简单的例子,当我们需要依赖TestNG的时候,简单的声明GAV会出错,因为TestNG强制需要你提供分类器,以区别jdk14和jdk15,我们需要这样声明对TestNG的依赖:

Xml代码
  1. <dependency>  
  2.   <groupId>org.testng</groupId>  
  3.   <artifactId>testng</artifactId>  
  4.   <version>5.7</version>  
  5.   <classifier>jdk15</classifier>  
  6. </dependency>  

你会注意到maven下载了一个名为testng-5.7-jdk15.jar的文件。其命名模式实际上是<artifactId>-<version>-<classifier>.<packaging>。理解了这个模式以后,你就会发现很多文件其实都是默认构件的分类器扩展,如 myapp-1.0-test.jar, myapp-1.0-sources.jar。

分类器还有一个非常有用的用途是:我们可以用它来声明对test构件的依赖,比如,我们在一个核心模块的src/test/java中声明了一些基础类,然后我们发现这些测试基础类对于很多其它模块的测试类都有用。没有分类器,我们是没有办法去依赖src/test/java中的内容的,因为这些内容不会被打包到主构件中,它们单独的被打包成一个模式为<artifactId>-<version>-test.jar的文件。

我们可以使用分类器来依赖这样的test构件:

Xml代码
  1. <dependency>  
  2.   <groupId>org.myorg.myapp</groupId>  
  3.   <artifactId>core</artifactId>  
  4.   <version>${project.version}</version>  
  5.   <classifier>test</classifier>  
  6. </dependency>  

理解了分类器,那么可供依赖的资源就变得更加丰富。

 

依赖管理(dependencyManagement)

当你只有一个Maven模块的时候,你完全不需要看这个部分。但你心里应该清楚,只有一个Maven模块的项目基本上只是个玩具。

实际的项目中,你会有一大把的Maven模块,而且你往往发现这些模块有很多依赖是完全项目的,A模块有个对spring的依赖,B模块也有,它们的依赖配置一模一样,同样的groupId, artifactId, version,或者还有exclusions, classifer。细心的分会发现这是一种重复,重复就意味着潜在的问题,Maven提供的dependencyManagement就是用来消除这种重复的。

正确的做法是:

1. 在父模块中使用dependencyManagement配置依赖

2. 在子模块中使用dependencies添加依赖

dependencyManagement实际上不会真正引入任何依赖,dependencies才会。但是,当父模块中配置了某个依赖之后,子模块只需使用简单groupId和artifactId就能自动继承相应的父模块依赖配置。

这里是一个来自于《Maven权威指南》的例子:

父模块中如此声明:

Xml代码
  1. <project>  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>org.sonatype.mavenbook</groupId>  
  4.   <artifactId>a-parent</artifactId>  
  5.   <version>1.0.0</version>  
  6.   ...  
  7.   <dependencyManagement>  
  8.     <dependencies>  
  9.       <dependency>  
  10.         <groupId>mysql</groupId>  
  11.         <artifactId>mysql-connector-java</artifactId>  
  12.         <version>5.1.2</version>  
  13.       </dependency>  
  14.       ...  
  15.     <dependencies>  
  16.   </dependencyManagement>  

子模块中如此声明:

Xml代码
  1. <project>  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <parent>  
  4.     <groupId>org.sonatype.mavenbook</groupId>  
  5.     <artifactId>a-parent</artifactId>  
  6.     <version>1.0.0</version>  
  7.   </parent>  
  8.   <artifactId>project-a</artifactId>  
  9.   ...  
  10.   <dependencies>  
  11.     <dependency>  
  12.       <groupId>mysql</groupId>  
  13.       <artifactId>mysql-connector-java</artifactId>  
  14.     </dependency>  
  15.   </dependencies>  
  16. </project>  

你依赖配置越复杂,依赖管理所起到的作用就越大,它不仅能够帮助你简化配置,它还能够帮你巩固依赖配置,也就是说,在整个项目中,对于某个构件(如mysql)的依赖配置只有一种,这样就能避免引入不同版本的依赖,避免依赖冲突。

 

小结

本文讲述了一些Maven依赖中重要的概念,并通过样例提供了一些最佳实践,如依赖归类,依赖范围,分类器,以及依赖管理。我的目的是通过浅显的例子讲述那些你实际工作中会需要了解的80%的内容,如果你需要更深入的了解,请参考《Maven权威指南》

译自:http://www.sonatype.com/about/press/20090122-JDJm2eBringingMavenToEclipse

MavenEclipse所短,反之亦然

by Jason Van Zyl

 

M2e起步

不要轻易相信我的话;根据我们创建的软件来判断m2e项目。虽然为了让Maven和Eclipse项目完美结合,还有一些集成工作要做,但该项目已经发布了十分强大的Eclipse插件。下面的小节,我会仔细介绍一些m2e项目有趣的变革性的特性。


安装m2e

要安装m2e,打开Eclipse 3.4(Ganymede)。如果你还没有下载和安装Eclipse,你可以从http://www.eclipse.org 获得 Eclipse 3.4 。在Eclipse中,从Help菜单选择 Software Updates…。在Software Updates and Add-ons 对话框中,点击 Available Software 按钮,然后点击 Add Site… 添加更新站点URL:http://m2eclipse.sonatype.org/update/ 。在你添加了该URL之后,你将能够为你的项目添加 Maven Integration, Maven Optional Components,和Maven Project Configurators。如果你计划在m2eclipse插件中使用Subclipse,Mylyn,以及AspectJ集成,你也需要安装这些插件。关于安装m2eclipse的完整的指令,见Maven权威指南的m2eclipse章节。下载和安装m2eclipse需要花一段时间,一旦安装过程完成,Eclipse会建议你重启IDE。

一旦安装过程结束,你将能够点击浏览m2eclipse。下面的小节,我会介绍一些m2eclipse插件中十分有趣的功能。


从Maven骨架创建一个Maven项目

Maven骨架是一个类新项目的模板,Maven仓库包含了很多骨架,包含了从web应用程序到消息组件的几乎所有类型项目。有了m2eclipse,使用Maven骨架变得十分容易,只要填写一个简单的New Maven Project对话框,然后从中央仓库的骨架列表中选择一个骨架。如果有人向Maven仓库发布了一个Maven骨架,m2eclipse会在请求仓库索引的时候发现这个骨架。要使用Maven骨架创建一个Maven项目(见下图),选择File -> New -> Project… , 然后在过滤字段输入maven。选择新的Maven项目然后点击Next。下一个屏幕会提供一个界面让你选择新项目的Maven骨架。该列表包含了很多M2eclipse自带的Maven骨架。



这个列表是由叫做Nexus Indexer的东西生成的。Nexus Indexer是一个包含了整个Maven仓库索引的文件。它由Nexus仓库管理器生成。


从POM具体化一个Maven项目

M2eclipse也能让你“具体化”一个Maven项目。具体化和从Subversion签出一个Maven项目的过程类似,但现在不再是手工的输入项目Subversion仓库的URL,该URL现在从项目跟POM文件中发现。如果一个POM文件包含了正确的元素声明了源码仓库的位置,你可以使用该功能从仅仅一个POM文件“具体化”项目。使用该特征,你可以浏览Maven仓库中的项目,然后将其具体化到Eclipse项目中。如果你的项目依赖于第三方的开源类库,并且你需要接触其源码,该功能就十分方便。你不再需要寻找其web站点,寻找去哪里签出源码,只要简单的使用m2eclipse来“具体化”这个项目。


搜索依赖和构件

从Maven仓库中快速搜索和定位依赖能极大的节省时间。有了m2eclipse,你不再需要为一个版本的构件去找遍中央仓库。如果你需要为Maven项目添加一个构件,在项目上右击,然后选择Maven菜单下的Add Dependency。之后,你会看到如下图的对话框。简单的输入groupId和artifactId会让m2eclipse搜索一个Maven仓库的Nexus索引,并显示匹配的构件。快速定位构件意味着你不再需要花无数的时间在Maven仓库中点来点去,寻找一个特定的依赖构件。


分析依赖树

M2eclipse提供了一些有趣的功能让你分析并绘制项目依赖的图表。你可以通过点击项目的pom.xml来载入POM编辑器。如果m2eclipse正确安装了,你会看到POM编辑器,而非POM XML。如果打开了一个带有很多依赖的项目,你可以点击POM编辑器的Dependency Tree选项卡,然后看到依赖以两列的形式显示,如下图。面板左边显示了一个依赖树。树的第一层包含了项目所有的直接依赖,每下一层列出了依赖的依赖。左边的部分能让你很轻松的弄明白一个特定的依赖是如何进入你项目的已解析依赖中的。右边的面板显示了已解析依赖。这是在所有冲突和范围都应用后的有效依赖列表,也是你项目用来编译,测试,和打包的有效依赖列表。



如果你想要以图的形式查看依赖,点击Dependency Graph选项卡查看项目依赖图(如下图)。点击图中的一个节点会标亮该节点并强调项目依赖中该构件和其它构件的关系。


使用Maven构建Eclipse:Tycho

如Eclipse的PDE,Tycho构建插件和更新站点供Eclipse平台使用。Tycho可以使用pom.xml或者Eclipse的本地元数据文件如manifest.mf和feature.xml来构建Eclipse插件。Maven的一个长期努力是基于不同种类的项目元数据构建项目,Tycho是其中的一部分。在本文撰写的时候,Tycho正被一些组织和开源项目(包括m2eclipse)用来构建Eclipse插件。Tycho是Eclipse开发者使用Maven自动化Eclipse插件构建的最直接的方式。共享字节码的故事在Eclipse的世界中变得模糊。有Eclipse发布包,更新站点,P2仓库,动态条款(dynamic provisioning),OSGi仓库的不同提议,甚至一些Eclipse包被签入到Maven仓库中。Sonatype致力于帮助Eclipse社区创建开放的,可互操作的仓库和元数据,这将有益于所有用户,Tycho是让Maven和Eclipse互操作的第一步。


结论

接触新的代码是困难的工作:你需要找到它并弄明白如何与之工作。Nexus索引使寻找构件变得容易,POM让理解项目变得容易,m2eclipse使得在Eclipse中使用Maven变得简单。我相信Eclipse世界会从Maven学到一些技巧,并开始使用Maven元数据和插件。对于那些编写开发工具的人,我相信一个Maven mojo是编写一个工具所需要的最少的工作量,它不仅能在命令行运行,同样能在Eclipse中运行。我同样相信Eclipse插件可以使用Maven POM信息和依赖管理功能来实现其自身的目的。我对这份工作充满期望,并邀请所有人加入并享受结果。请访问http://eclipse.org/m2e


资源

  • 16:19
  • 评论 / 浏览 (4 / 6008)

译自:http://www.sonatype.com/about/press/20090122-JDJm2eBringingMavenToEclipse

MavenEclipse所短,反之亦然

by Jason Van Zyl

这里我要介绍m2e项目,它联结了Maven的能力和Eclipse的可用性。本文中,我会讲述我们对于m2e项目的计划:目前它是什么,将来会朝什么方向发展?M2e不仅仅是Eclipse的一个Maven插件。虽然这是m2e项目很重要的一部分,但Sonatype放眼于更大胆的目标。我们使用m2e项目,通过提供Maven作为构建Eclipse插件和Eclipse平台的解决方案,给Eclipse带来稳定的构建自动化过程。M2e项目正帮助驱动Maven 3平台的需求,驱动Tycho和Mercury等项目,这些项目承诺会改变开发人员和组件及仓库交互的方式。本文简要的概述m2e今天能做什么,有哪些特性正处于开发阶段。对于Eclipse和Maven来说,有很多惊人的,革命性的变化正在到来。

 

为什么m2e不同

之前有很多关于结合Maven和Eclipse的尝试,是什么让m2e与它们不同呢?Aapche Maven和Eclipse有一段努力集成的历史,虽然曾经有基于Eclipse的Maven插件先于m2e,但我们没有看到一种方案是真正的集成插件。之前插件提供的方案是在Eclipse中启动一个Maven构建,并且使用一个GUI编辑器来配置Maven POM,但是这总是会丢失一些东西。所有这些插件认为Maven是一个外部的工具,而在Eclipse平台中启动该工具。这些工具的目标都是使用Eclipse作为一个包裹Maven的外壳。但在Eclipse中启动Maven只是问题的一半。如果要使用Maven来管理并构建Eclipse组件又该如何?


M2e正帮助驱动Maven 3平台以其一些工具的变更。比如Tycho,它可以用来在Maven中构建和管理Eclipse组件。你应该将m2e看成是一项消除Maven和Eclipse平台隔阂的工作,而不是为Eclipse创建一个Maven插件。当然,m2e为Eclipse提供了稳定的Maven插件,但作为该集成的一个副作用,它能让你在一些引用了Eclipse平台相同OSGi组件的项目中使用Maven来构建Eclipse插件。M2e项目正驱动Maven架构的革新,能让用户与多种仓库格式交互,而且,由于m2e的工作,开发人员将能够使用Maven来构建和管理OSGi组件。

在我们开始m2e项目的时候,Eugene Kuleshov和我决定坐下来挖掘两个平台的细节,找出结合它们的正确方式。我们很快得出结论:成功的集成需要更改Maven的架构,使其变得更容易可插入。还需要一些新的开发工作能让Maven与Eclipse开发中不同的仓库格式交互。在满足初始愿景的前进过程中我们做得很好,在这完成之后,不仅仅Eclipse会有稳定的Maven集成,Maven也会成为构建Eclipse平台的一种可行的解决方案。M2e的不同之处在于,它不仅仅为Eclipse带来的Maven的力量,它是两条河流的交汇,帮助驱动两个平台的变革。

 

为Eclipse引入Maven(以及为Maven引入Eclipse)

当我和人们讨论为Eclipse引入Maven的时候,它们更倾向于使用社会的和科学的隐喻来描述所有可能性。这是一段“联姻”,一段“舞蹈”,一段交织的“DNA双螺旋”,或者两个星系碰撞成一个缓慢的,剧烈的,但有趣的物质和能量的交换。归并两种各自都有“可选方案”和项目建模及构件的技术,是一个有趣的挑战,然而我们欢迎这类有趣的问题,因为这也是学习的机会,并能反馈给Maven。我们基于集成的工作对Maven做了一些改变,我们使得Maven更高效,更苗条,以为那些可能在Eclipse中使用Maven的人们提供更快更易控制的体验。虽然我们仍然还未定居在Eclipse基金会中,但你可以为Eclipse下载和使用叫做m2eclipse的完整的Maven插件。此外,虽然还只是一个开始,你已经可以使用开发发布工具如Tycho来开始用Maven自动化你的Eclipse插件构建。

本小节的标题可能有一些误导,我们并不是将Maven“移动”到Eclipse中,而且,你可以放心确认我们没有为Maven实现一些新的,只有通过基于Eclipse GUI才可用的特性。Apache Maven是Apache软件基金会成员中一个健康增长的社区。对使用Eclipse不感兴趣的Maven用户不会被这次集成所影响,但那些使用Eclipse的用户会体验到这里强有力合并带来的好处。有了m2e,用户不用求助于PDF文档之类的东西,就可以引入Maven构件并构建Maven项目。有了m2e,应用程序开发者就能够在几秒钟内从数百个Maven骨架中找出一个来创建一个新的项目,而且现在开发人员只需简单的点击按钮,可以访问Nexus仓库索引来搜索并添加依赖。有了m2e,Eclipse工具开发人员就能够在开发Eclipse的时候使用Maven插件来读取Maven元数据,发布工程师可以使用m2e生成的工具来构建Eclipse插件,部署Eclipse发布包。

你不认为这是最后的部分,对不对?使用Maven构建Eclipse?Maven应该不用运行Eclipse就能够构建Eclipse,RCP,以及OSGi应用程序,就像其它Maven构建那样使用命令行或者在持续集成环境中。Tycho是一个用来构建OSGi应用的Maven插件,你可以从m2eclipse运行Tycho,也可以在其它Maven构建中使用它。集成工作不是简单的让开发者可以在Eclipse中使用Maven,我们预计Eclipse和Maven的“联姻”对两者来说都会是一种转变。为Eclipse引入Maven改变了一切。

 

Maven和Eclipse:强大的同盟

消除Eclipse和Maven之间的隔阂到底意味着什么?本节,我们浏览一下Eclipse和Maven的差异,并且讨论一些集成能给两个平台相互带来好处的区域。我们使用术语axis来表示那些Maven和Eclipse互补的地方。让我们看一下这些互补的axis:

  • 用户交互
  • 项目建模
  • 管理构件
  • 编写扩展和插件
  • 项目约定

用户交互

Maven擅长命令行构建和自动化构建,而Eclipse则是交互式构建的黄金标准。使用Maven,我们通过文本编辑器来观察理解复杂多模块Maven构建的输出。使用Eclipse,一个大型的构建可以被管理成交互式的;编译问题和测试失败通常可以通过点击一个按钮就定位和解决。虽然Eclipse作为一个IDE在交互式构建中表现的非常优秀,但是让Eclipse作为一个持续集成工具或者命令行工具会非常困难,因为像CruiseControl或Hudson这样的工具需要构建能够完全的从命令行运行。在m2e之前,帮助理解Maven项目和依赖的UI支持仅限于一些静态的web页面和报告,也没有很好的方法在自定义编辑器中编辑pom.xml,以及搜索Nexus仓库索引,添加依赖等等。Maven可以为Eclipse带来命令行构建自动化支持,Eclipse可以给Maven带来强大的可视化交互性和交互式报告能力。

 

项目建模

Maven集成了项目对象模型(POM),而Eclipse的项目模型是分散的。Maven POM拥有一个项目工作的全部信息:开发者名称,SCM信息,问题追踪URL,依赖,许可证信息,以及组件描述信息。Eclipse则倾向于为许多不同的插件的每一方面拥有不同的元数据,而这些插件元数据的格式依赖于插件开发者的设计风格。当人们开始对Mylyn问题追踪系统或者ECF开发者聊天工具等特性感兴趣的时候,协调和控制这些元数据的访问成为了工具集成的一个不安因素。

 

管理构件

Eclipse让源代码相关工作变得十分简单,这得益于content-assist,快速修复,以及高级重构操作的支持。在Eclipse中,二进制文件则是另外一回事了;它对寻找或管理项目创建和依赖类库JAR文件的支持非常少。而另一方面,Maven使得依赖构件及构建产品相关的工作变得十分简单,这得益于Maven中所有构件都有注册名称和版本,本地和远程的索引能让你快速找到任何仓库中的代码,元数据,类,以及POM,并直接拉到你的项目中。将Maven的力量带到Eclipse IDE中能让开发者创建依赖图,使用一个Maven仓库的Nexus索引搜索构件。

 

编写扩展和插件

Eclipse和Maven都为编写扩展和插件提供了API和框架。Eclipse拥有一个十分丰富的模型提供很多的集成机会,它有一个很大的API需要XML文档描述组件和声明需求和依赖。Maven为Maven插件开发使用一个简单的POJO方式,它使用了依赖注入。将Maven引入Eclipse能让Eclipse用户访问数以百计的现存的Maven插件,这些插件包括了运行一个内嵌的Jetty实例,以及生成项目源码报告等等。有了Maven,工具开发者能够不用考虑在哪里运行就编写工具,用户简单的配置就能得到Eclipse支持。采用Maven的项目模型来为Eclipse组件建模能让Eclipse开发者使用Maven来构建Eclipse插件和组件。

 

项目约定

Eclipse使用了于项目见扁平的关系来支持无限种类的项目结构,而Maven鼓励项目结构使用通用的约定,并支持层次结构的项目。使用Maven,元数据和构建坐标可以在一个父POM或者聚合POM中指定,而不用分散到一组相互关联的项目中。虽然两者都支持不同构建步骤的配置,Maven使得理解一个未知的项目更容易,并更易协调多模块构建。一旦Eclipse开发者开始理解Maven的约定并开始使用Tycho之类的工具来构建Eclipse,非Eclipse开发者构建和理解Eclipse组件和插件就会变得容易得多。集成Eclipse和Maven能帮助扩展Eclipse开发者社区,为那些对构建Eclipse插件并作贡献感兴趣的人降低进入的门槛。


融合技术

这些axis定义了Maven和Eclipse能相互获益的区域。虽然完全实现高层次的协调有些十分困难的问题,但我相信Maven和Eclipse都会从这个过程中获益。一个特殊的区域能展示Maven可以为Eclipse带来好处,这就是classpath容器。因为Eclipse允许插件定义能被其它插件引用的classpath容器,m2e就能够帮助那些想要使用maven依赖管理功能的插件。有了这个新的功能,插件就可以使用m2e提供的classpath容器来访问那些使用Maven的项目的classpath。

一个Eclipse改变Maven的例子是在项目元数据方面。Eclipse已经有一个分布式的项目和依赖模型。例如,一个Eclipse项目,它构建了一个OSGi包,可能已经有一些Eclipse特定格式的先决条件元数据。这里我们不是要求所有Eclipse插件和组件去维护pom.xml文件,而是构建系统来组合一个虚拟的POM,它包含了Eclipse特定组件描述符的信息。

就像Eclipse允许非Eclipse组件提供classpath容器一样,Maven也在演变以允许非Maven组件提供项目元数据和依赖信息。这两者,其API的变化都需要插件开发者去考虑一个更通用的方式来和Eclipse及Maven交互。如果你在Maven 3中编写一个Maven插件,你就不再能够假定项目信息存储在pom.xml中,如果你正编写一个Eclipse插件,你也许就不能够假设classpath信息会完全的由Eclipse提供。集成工作让两者都更灵活,更通用。

一个存在潜在方式冲突的方面是项目创建过程。使用Maven,用户可以访问一个长长的Maven 骨架列表,从用Wicket编写的web应用项目,到如Mule的消息产品的消息组件。在Eclipse中,你创建一个JET模板或者一个自定义的向导来让用户一步步创建一个新的项目类型。在这些情况下,两个社区使用的不同方式会有健康的竞争,而且社区将不得不解决出现的问题。总之,更多的集成和协作长期来说对开发者就意味着更多的选择。


社区的交汇

随着Maven和Eclipse基金会周围的社区开始交互,还不清楚是否会有势力范围冲突或者共同目标协同和优势互补。为了交流印象,我们说Maven社区倾向于IDE无关,认为可工作的代码比优雅的设计更重要,团队共识比个人领导更重要。而Eclipse社区是另外一种感觉,他们编写分层的模式的代码,通常较团队共识更看重个人领导,并严格遵循一组关于软件如何设计和实现的观念。集成这两个社区一开始可能会有一些问题,但是,最终,我预测该工作会得以成功,因为有一些有趣的趋势驱动两者相互接纳:持续增长的模块化特性,更敏捷的渴望,以及企业中采用开源最佳实践的活动。

软件变得更加模块化,而且今天的企业应用依赖于很多第三方类库十分常见。越来越多的项目正依赖于越来越多的开源组件及类库,其驱动因素之一就是大家接受了开源能让组织减轻负担的同时控制自身目标这一想法。转向更小,更分布,更模块化的团队也推动了这一转变。开发这不会在一个项目上工作很多年,而是在敏捷工作环境中挑选项目,进行二周迭代,然后转向其它模块和组件。

在这敏捷的,开源的,模块化的环境中,Maven为那些想要快速从一个项目转向另外一个项目的开发者提供了一个至关重要的方式。使用m2eclipse,某个开发者可以花几分钟,使用Mylyn和WTP配置,仅从一个Subversion URL或者一个Maven POM具体化一个项目到他的工作空间中。我频繁的和一些公司说,让他们使用开源软件开发环境中用的工具和系统来统一内部的开发基础设施。这些组织看到开源项目的开发者十分高效的协作,他们想知道为了将这种高效的生产率带入企业开发中,需要什么样的工具和结构。他们寻找一个通用的开发平台,该平台提供了经过测试的协作模式,并能劝阻各部门不再持续的去重新发明轮子。

 

Eclipse和Maven是优秀的平台,这两个平台紧密的集成会方便企业中持续的采用开源工具。随着越来越多企业开始依赖于Maven进行企业构建,依赖Eclipse作为开发平台,越来越多的人会希望m2e项目将这次集成推向新的层次。

译自:http://blogs.sonatype.com/people/2009/01/maven-continuous-integration-best-practices/

 

持续集成是你在开发过程中经常会用到的一个最佳实践,它是高效软件开发生命周期(SLDC)至关重要的一部分。如果还没使用这一实践,那么应该立刻就开始使用。持续集成最大的好处是,它能帮你立刻找出引入到系统中的错误,而不是在很多天之后看到测试失败,或者在QA阶段再发现重大错误。本文并不是要介绍CI的优点,本文是介绍如何建立一个最佳的使用Maven的CI环境。这里是一些关于如何在一个CI系统(如Hudson)中运行Maven构建的贴士。

 

#1 自动化Snapshot部署

按照我的经验,最好让CI系统部署你的snapshot。这是保证仓库内容和源码控制系统保持同步的最可靠的方法。要在实践中使用这种方法,你需要结合CI和仓库管理器,如Nexus,它能自动的清除snapshot。我管理过一个项目,它在不到一周的时间内生成了>300gb的snapshot。使用一个仓库管理器会让你保持稳健。

#2 隔离本地仓库

另一个CI设置至关重要的组成部分是本地仓库隔离。Maven中的本地仓库是放置所有Maven下载和生成的构件的地方,并且当前它不是多线程安全的。虽然产生冲突的可能性很小,但它还是会发生的。

让每个项目有一个本地仓库最主要的原因是,这是测试你的项目是否基于公司仓库可构建的唯一方法。如果你没有单独的本地仓库,那么一个构建的产品即使不在公司仓库中,也还会被CI上另一个构建看到。这里说的很重要,因为CI的一个功能是,它应该能够验证代码对于一个真实的开发者来说是可构建的。

提示:使用 -Dmaven.repo.local=xxx 来为每个构建定义唯一的本地仓库。

#3 周期性的清除本地仓库

为了进一步的验证仓库的内容,以及管理硬盘空间,我每晚都清除本地仓库。如果仓库有变化或者构件被移除了,CI系统会检测到。为了方便的清除所有本地仓库,我倾向于将所有本地仓库组织到同一个目录下,如 /opt/repos/*。

很显然拥有多个本地仓库比起一个仓库来说需要更多的硬盘空间,因为有很多依赖的重复,但即使在我们的大型grid上,所有的仓库的总大小也不到10gb。当你不控制snapshot,也不每晚清除仓库,不保持对它的控制,本地仓库就会慢慢的变得巨大。

提示:使用你的CI系统本身来安排本地仓库清理。这样当Maven糊涂的时候,任何人都可以手动的从UI上清理仓库。

久而久之,我也发现了一些更简单的技巧:

#4 开启批处理模式

提示:在构建中开启 -B(batch,批处理)模式。这会让日志变短,因为这避免了依赖下载过程的日志。这也能确保构建不会因为等待用户输入而挂起。

#5 开启完全堆栈跟踪

提示:开启 -e 能让Maven在遇到构建异常的时候产生完全的堆栈跟踪信息。这让我们更容易根据构建失败结构的日志或者email中理解问题,而不用重新构建一次。

#6 打印测试失败至标准输出

提示:开启 -Dsurefire.useFile=false 。这是我最喜欢的选项之一,因为它能让surefire打印测试失败到标准输出,因此也就能被包含在构建失败日志或email中。这样就节省了你的时间,不用再为了一个简单的堆栈日志去机器上寻找surefire报告。

#7 总是检查Snapshot

提示:开启 -U 让Maven总是最检查新的snapshot。该选项同样也可以在CI系统的setting.xml中开启。(提示4和6同样也可以在settings.xml中声明)

小结

使用上述的设置和过程会让每次build都将构件推入仓库中。接下来下游的build会有其自己的干净的本地仓库,然后检测仓库管理器以获取最新的snapshot。然后每天至少一次,本地所有的东西都被清除,所有的依赖都从仓库管理器抓取。

自然的,所有这些更新的清除工作都会给CI和仓库管理器之间的网络带来一些负荷。如果它们共享一个高速的网络,那么没什么问题。但如果你的仓库管理器和CI系统并不靠近,那么你应该在CI系统旁放一个仓库管理器,它代理远处的仓库,并能消除网络对每日本地仓库清理的影响。

注意:如果你想要遵循这些提示,那么很重要的一点是下载一个Nexus。清除你本地仓库的内容,每天每个项目都从中央仓库下载所有依赖,会造成中央Maven仓库的网络拥堵。

 

注:在settings.xml中开启4,6,7:

#4: <interactiveMode>false</interactiveMode>

#6: <properties><surefire.useFile>true</surefire.useFile></properties> —— 在actived profile中

#7: <updatePolicy>always</updatePolicy> —— 在repository定义中

在线阅读地址:http://books.sonatype.com/maven-book/reference_zh/site-generation.html

 

Maven最吸引人的特征之一就是它能帮你自动构建项目web站点,做一些简单的配置,然后输入几条简单的命令,一个像模像样的站点就能发布了,我现在还清晰的记得第一次用Maven成功构建站点后的那份惊喜。

 

大致介绍下本章内容:

  • 如何生成站点,如何发布站点(最基本的,对于大部分人也够用的)

如需定制:

  • 什么是站点描述符,如何修改它以满足你的个性化需求
  • 一个完整站点的目录结构是什么,也就是说你可以知道什么内容放在什么位置
  • 项目文档如何编写

更高级的:

  • 定制CSS(如果你CSS很牛,那么不要犹豫,让自己的站点与众不同吧)
  • 定制模板(Velocity),Maven之父就是Velocity之父,该模板控制站点如何生成
  • Maven站点皮肤,封装CSS和模板,方便重用
  • 一些小提示和技巧

其实想要让你的站点内容丰富,你经常会需要配置一些Maven报告插件,这是本章没有提到的重要内容。

浏览下这个页面:http://maven.apache.org/plugins/index.html,这里是一些最常用Maven插件的介绍,找到Reporting Plugins一栏,你会看到如checkstyle, javdoc, surefire-report等常用的站点报告插件。每个插件的用途都不一样,这里我就不一一解释了,根据你自己的需要,选择其中的一些(甚至所有?),然后弄一个漂亮的报告集成,给你的经理看一下,他会对你的工作感到满意的。

本章内容很少,十页左右,不过讲述的内容非常实用。我们使用Maven的时候,经常会碰到这样的问题:同样的build,在开发的时候会依赖于开发数据库,而在持续集成服务器上,就需要依赖于另外一个数据库。如果用同一个数据库,开发时的build可能会破坏持续集成的build。同理,开发者A和B也想根据自己的数据库build,不想相互干扰。Maven的属性引用和资源过滤可以十分优雅的解决这类问题。

可能你没有想到,在Maven中,我们可以引用很多很多变量的值,比如${project.version}就是定义在POM中的版本号,比如${project.build.sourceDirectory}就代表了目录,还有${env.M2_HOME}代表了M2_HOME这个环境变量,${user.home}代表了Java系统属性用户home目录。这一章详细罗列了所有你可以使用的Maven属性。

本章目录:

13.1. 简介
13.2. Maven属性
   13.2.1. Maven项目的属性
   13.2.2. Maven的Settings属性
   13.2.3. 环境变量属性
   13.2.4. Java系统属性
   13.2.5. 用户定义的属性
13.3. 资源过滤

合理的使用Maven属性和资源过滤能够帮你创建移植性更强的build。

在线阅读:http://books.sonatype.com/maven-book/reference_zh/resource-filtering.html

Have fun!

“分天下为三十六郡,郡置守,尉,监” —— 《史记·秦始皇本纪》

 

所有用Maven管理的真实的项目都应该是分模块的,每个模块都对应着一个pom.xml。它们之间通过继承和聚合(也称作多模块,multi-module)相互关联。那么,为什么要这么做呢?我们明明在开发一个项目,划分模块后,导入Eclipse变成了N个项目,这会带来复杂度,给开发带来不便。

 

为了解释原因,假设有这样一个项目,很常见的Java Web应用。在这个应用中,我们分了几层:

  • Dao层负责数据库交互,封装了Hibernate交互的类。
  • Service层处理业务逻辑,放一些Service接口和实现相关的Bean。
  • Web层负责与客户端交互,主要有一些Structs的Action类。

对应的,在一个项目中,我们会看到一些包名:

  • org.myorg.app.dao
  • org.myorg.app.service
  • org.myorg.app.web
  • org.myorg.app.util

这样整个项目的框架就清晰了,但随着项目的进行,你可能会遇到如下问题:

  1. 这个应用可能需要有一个前台和一个后台管理端(web或者swing),你发现大部分dao,一些service,和大部分util是在两个应用中可。这样的问题,你一周内遇到了好几次。
  2. pom.xml中的依赖列表越来越长以重用的,但是,由于目前只有一个项目(WAR),你不得不新建一个项目依赖这个WAR,这变得非常的恶心,因为在Maven中配置对WAR的依赖远不如依赖JAR那样简单明了,而且你根本不需要org.myorg.app.web。有人修改了dao,提交到svn并且不小心导致build失败了,你在编写service的代码,发现编译不过,只能等那人把dao修复了,你才能继续进行,很多人都在修改,到后来你根本就不清楚哪个依赖是谁需要的,渐渐的,很多不必要的依赖被引入。甚至出现了一个依赖有多个版本存在。
  3. build整个项目的时间越来越长,尽管你只是一直在web层工作,但你不得不build整个项目。
  4. 某个模块,比如util,你只想让一些经验丰富的人来维护,可是,现在这种情况,每个开发者都能修改,这导致关键模块的代码质量不能达到你的要求。

我们会发现,其实这里实际上没有遵守一个设计模式原则:“高内聚,低耦合”。虽然我们通过包名划分了层次,并且你还会说,这些包的依赖都是单向的,没有包的环依赖。这很好,但还不够,因为就构建层次来说,所有东西都被耦合在一起了。因此我们需要使用Maven划分模块。

 

一个简单的Maven模块结构是这样的:

 

---- app-parent

             |-- pom.xml (pom)

             |

             |-- app-util

             |        |-- pom.xml (jar)

             |

             |-- app-dao

             |        |-- pom.xml (jar)

             |

             |-- app-service

             |        |-- pom.xml (jar)

             |

             |-- app-web

                      |-- pom.xml (war)   

 

上述简单示意图中,有一个父项目(app-parent)聚合很多子项目(app-util, app-dao, app-service, app-web)。每个项目,不管是父子,都含有一个pom.xml文件。而且要注意的是,小括号中标出了每个项目的打包类型。父项目是pom,也只能是pom。子项目有jar,或者war。根据它包含的内容具体考虑。

 

这些模块的依赖关系如下:

 

app-dao      --> app-util

app-service --> app-dao

app-web     --> app-service

 

注意依赖的传递性(大部分情况是传递的,除非你配置了特殊的依赖scope),app-dao依赖于app-util,app-service依赖于app-dao,于是app-service也依赖于app-util。同理,app-web依赖于app-dao,app-util。

 

用项目层次的划分替代包层次的划分能给我们带来如下好处:

  1. 方便重用,如果你有一个新的swing项目需要用到app-dao和app-service,添加对它们的依赖即可,你不再需要去依赖一个WAR。而有些模块,如app-util,完全可以渐渐进化成公司的一份基础工具类库,供所有项目使用。这是模块化最重要的一个目的。
  2. 由于你现在划分了模块,每个模块的配置都在各自的pom.xml里,不用再到一个混乱的纷繁复杂的总的POM中寻找自己的配置。
  3. 如果你只是在app-dao上工作,你不再需要build整个项目,只要在app-dao目录运行mvn命令进行build即可,这样可以节省时间,尤其是当项目越来越复杂,build越来越耗时后。
  4. 某些模块,如app-util被所有人依赖,但你不想给所有人修改,现在你完全可以从这个项目结构出来,做成另外一个项目,svn只给特定的人访问,但仍提供jar给别人使用。
  5. 多模块的Maven项目结构支持一些Maven的更有趣的特性(如DepencencyManagement),这留作以后讨论。

接下来讨论一下POM配置细节,实际上非常简单,先看app-parent的pom.xml:

Xml代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <modelVersion>4.0.0</modelVersion>  
  4.     <groupId>org.myorg.myapp</groupId>  
  5.     <artifactId>app-parent</artifactId>  
  6.     <packaging>pom</packaging>  
  7.     <version>1.0-SNAPSHOT</version>  
  8.     <modules>  
  9.         <module>app-util</module>  
  10.         <module>app-dao</module>  
  11.         <module>app-service</module>  
  12.         <module>app-web</module>  
  13.     </modules>  
  14. </project>  

Maven的坐标GAV(groupId, artifactId, version)在这里进行配置,这些都是必须的。特殊的地方在于,这里的packaging为pom。所有带有子模块的项目的packaging都为pom。packaging如果不进行配置,它的默认值是jar,代表Maven会将项目打成一个jar包。

该配置重要的地方在于modules,例子中包含的子模块有app-util, app-dao, app-service, app-war。在Maven build app-parent的时候,它会根据子模块的相互依赖关系整理一个build顺序,然后依次build。

这就是一个父模块大概需要的配置,接下来看一下子模块符合配置继承父模块。、

Xml代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <parent>  
  4.         <artifactId>app-parent</artifactId>  
  5.         <groupId>org.myorg.myapp</groupId>  
  6.         <version>1.0-SNAPSHOT</version>  
  7.     </parent>  
  8.     <modelVersion>4.0.0</modelVersion>  
  9.     <artifactId>app-util</artifactId>  
  10.     <dependencies>  
  11.         <dependency>  
  12.             <groupId>commons-lang</groupId>  
  13.             <artifactId>commons-lang</artifactId>  
  14.             <version>2.4</version>  
  15.         </dependency>  
  16.     </dependencies>  
  17. </project>  

app-util模块继承了app-parent父模块,因此这个POM的一开始就声明了对app-parent的引用,该引用是通过Maven坐标GAV实现的。而关于项目app-util本身,它却没有声明完整GAV,这里我们只看到了artifactId。这个POM并没有错,groupId和version默认从父模块继承了。实际上子模块从父模块继承一切东西,包括依赖,插件配置等等。

此外app-util配置了一个对于commons-lang的简单依赖,这是最简单的依赖配置形式。大部分情况,也是通过GAV引用的。

再看一下app-dao,它也是继承于app-parent,同时依赖于app-util:

Xml代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <parent>  
  4.         <artifactId>app-parent</artifactId>  
  5.         <groupId>org.myorg.myapp</groupId>  
  6.         <version>1.0-SNAPSHOT</version>  
  7.     </parent>  
  8.     <modelVersion>4.0.0</modelVersion>  
  9.     <artifactId>app-dao</artifactId>  
  10.     <dependencies>  
  11.         <dependency>  
  12.             <groupId>org.myorg.myapp</groupId>  
  13.             <artifactId>app-util</artifactId>  
  14.             <version>${project.version}</version>  
  15.         </dependency>  
  16.     </dependencies>  
  17. </project>  

该配置和app-util的配置几乎没什么差别,不同的地方在于,依赖变化了,app-dao依赖于app-util。这里要注意的是version的值为${project.version},这个值是一个属性引用,指向了POM的project/version的值,也就是这个POM对应的version。由于app-dao的version继承于app-parent,因此它的值就是1.0-SNAPSHOT。而app-util也继承了这个值,因此在所有这些项目中,我们做到了保持版本一致。

这里还需要注意的是,app-dao依赖于app-util,而app-util又依赖于commons-lang,根据传递性,app-dao也拥有了对于commons-lang的依赖。

app-service我们跳过不谈,它依赖于app-dao。我们最后看一下app-web:

Xml代码
  1. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
  3.     <parent>  
  4.         <artifactId>app-parent</artifactId>  
  5.         <groupId>org.myorg.myapp</groupId>  
  6.         <version>1.0-SNAPSHOT</version>  
  7.     </parent>  
  8.     <modelVersion>4.0.0</modelVersion>  
  9.     <artifactId>app-web</artifactId>  
  10.     <packaging>war</packaging>  
  11.     <dependencies>  
  12.         <dependency>  
  13.             <groupId>org.myorg.myapp</groupId>  
  14.             <artifactId>app-service</artifactId>  
  15.             <version>${project.version}</version>  
  16.         </dependency>  
  17.     </dependencies>  
  18. </project>  

app-web依赖于app-service,因此配置了对其的依赖。

由于app-web是我们最终要部署的应用,因此它的packaging是war。为此,你需要有一个目录src/main/webapp。并在这个目录下拥有web应用需要的文件,如/WEB-INF/web.xml。没有web.xml,Maven会报告build失败,此外你可能还会有这样一些子目录:/js, /img, /css ... 。

 

看看Maven是如何build整个项目的,我们在 app-parent 根目录中运行 mvn clean install ,输出的末尾会有大致这样的内容:

 

...

...

[INFO] [war:war]
[INFO] Packaging webapp
[INFO] Assembling webapp[app-web] in [/home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Webapp assembled in[50 msecs]
[INFO] Building war: /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war
[INFO] [install:install]
[INFO] Installing /home/juven/workspaces/ws-others/myapp/app-web/target/app-web-1.0-SNAPSHOT.war to /home/juven/.m2/repository/org/myorg/myapp/app-web/1.0-SNAPSHOT/app-web-1.0-SNAPSHOT.war
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] ------------------------------------------------------------------------
[INFO] app-parent ............................................ SUCCESS [1.191s]
[INFO] app-util .............................................. SUCCESS [1.274s]
[INFO] app-dao ............................................... SUCCESS [0.583s]
[INFO] app-service ........................................... SUCCESS [0.593s]
[INFO] app-web ............................................... SUCCESS [0.976s]
[INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4 seconds
[INFO] Finished at: Sat Dec 27 08:20:18 PST 2008
[INFO] Final Memory: 3M/17M
[INFO] ------------------------------------------------------------------------

 

注意Reactor Summary,整个项目根据我们希望的顺序进行build。Maven根据我们的依赖配置,智能的安排了顺序,app-util, app-dao, app-service, app-web。

 

最后,你可以在 app-web/target 目录下找到文件 app-web-1.0-SNAPSHOT.war ,打开这个war包,在 /WEB-INF/lib 目录看到了 commons-lang-2.4.jar,以及对应的app-util, app-dao, app-service 的jar包。Maven自动帮你处理了打包的事情,并且根据你的依赖配置帮你引入了相应的jar文件。

 

使用多模块的Maven配置,可以帮助项目划分模块,鼓励重用,防止POM变得过于庞大,方便某个模块的构建,而不用每次都构建整个项目,并且使得针对某个模块的特殊控制更为方便。本文同时给出了一个实际的配置样例,展示了如何使用Maven配置多模块项目。

2010/04/29更新:我已经在个人网站提供了PDF下载 http://www.juvenxu.com/mvn-def-guide/

 

《Maven权威指南》介绍

关于Maven
----------------
Maven这个单词来自于意第绪语,意味知识的积累,最早在Jakata Turbine项目中它开始被用来视图简化构建过程。当时有很多项目,它们的Ant build文件仅有细微的差别,而JAR文件都由CVS来维护。于是Maven创始者开始了Maven这个项目,该项目的清晰定义包括,一种很方便的发布项目信息的方式,以及一种在多个项目中共享JAR的方式。
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。 当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。
Maven的官方站点是:http://maven.apache.org/

Maven和Ant
----------------
Ant 没有正式的约定如一个一般项目的目录结构,你必须明确的告诉 Ant 哪里去找源代码,哪里放置输出。随着时间的推移,非正式的约定出现了,但是它们还没有在产品中模式化。
Ant 是程序化的,你必须明确的告诉 Ant 做什么,什么时候做。你必须告诉它去编译,然后复制,然后压缩。
Ant 没有生命周期,你必须定义目标和目标之间的依赖。你必须手工为每个目标附上一个任务序列。
Maven 拥有约定,因为你遵循了约定,它已经知道你的源代码在哪里。它把字节码放到 target/classes ,然后在 target 生成一个 JAR 文件。
Maven 是声明式的。你需要做的只是创建一个 pom.xml 文件然后将源代码放到默认的目录。Maven 会帮你处理其它的事情。
Maven 有一个生命周期,当你运行 mvn install 的时候被调用。这条命令告诉 Maven 执行一系列的有序的步骤,直到到达你指定的生命周期。遍历生命周期旅途中的一个影响就是,Maven 运行了许多默认的插件目标,这些目标完成了像编译和创建一个 JAR 文件这样的工作。
此外,Maven能够很方便的帮你管理项目报告,生成站点,管理JAR文件,等等。这些都是Ant的弱项。
当然,Ant的强大在于它的灵活性,但是,随着项目规模的增大,使用Maven是更明智的选择。

关于本书
----------------
本书是关于Maven最权威最全面的材料,它清晰了解释了Maven这个工具如何给你的软件开发项目带来秩序。作者都拥有丰富的经验,其中包括Maven的创始人Jason Van Zyl。
由于Maven在不断发展,本书也只是一个开始,你应该把它看成是第1版。本书的主要目的还是给Maven开发者提供支持。
本书一开始介绍了Maven是什么,以及在各个环境下安装运行Maven。之后的内容分为两个部分:第一部分为Maven实战,沿用了Maven:A Developer's Notebook(该书覆盖的是Maven1的内容,目前绝大多数用户都在使用Maven2)的风格,通过一个个生动实际的例子,循序渐进的阐释了Maven的用法和核心概念,这些例子从最简单的命令行程序,到Web应用,到多模块项目,直至多模块企业级项目。第二部分为Maven参考,当你渐渐熟悉Maven,并且遇到问题需要解决的时候,这些参考就是最权威和详细的材料了,通过它们,你能了解Maven的高级特性,更深刻的理解Maven系统如何工作。参考覆盖的内容包括项目对象模型,构建生命周期,Profile,站点生成,编写插件等等。值得一提的是,该部分还详细介绍了m2eclipse和Nexus,m2eclipse是强大的在Eclipse中集成Maven的插件,Nexus是一个功能丰富的仓库管理器,能让你更方便的管理项目构件。
本书提供英文版在线的浏览及PDF下载: http://www.sonatype.com/book 。在这里你下载到样例源代码,也可以提出你的意见和建议。

作者简介
----------------
本书是由Sonatype的成员编写的,作者有:Tim O'Brien (Sonatype, Inc.) , John Casey (Sonatype, Inc.) , Brian Fox (Sonatype, Inc.) , Bruce Snyder () , Jason Van Zyl (Sonatype, Inc.)
Jason Van Zyl
Sonatype的创始人和CTO,Sonatype是Java开发基础设置的领导者,它的客户包括Intuit,eBay,Qualcomm和eTrade。Jason拥有多于10年的开源及私人企业软件开发经验。在Sonatype之前,Jason是Periapt, Inc.的创始人,这是一个为财富500强公司如丰田,美国银行,和可口可乐提供软件基础设置开发服务的公司。作为一个开源热衷者,Jason是Apache Maven项目的创始人之一,以及Plexus Ioc框架的创始人,Apache Velocity项目的创始人。Jason目前主持着Apache Maven项目管理委员会。它已经参与Apache软件基金会达7年之久,帮助建立了Codehaus,一个备受尊重的开源社区项目孵化设置。他还是很多主流软件会议的常见发言人,如JavaOne,EclipseCon,EmergingTech,和ApacheCon。
Tim O'Brien
专业的作家,程序员,歌手,居住并工作于芝加哥区域。最早在一台TRS-80上开始了解编程,之后去弗吉尼亚大学学习电子工程(之后不久就忘光了)。Tim积极参与开源项目,活动于Jakatra Commons。在此之前他著有Harnessing Hibernate, Maven: A Developer's Notebook 以及 Jakarta Commons Cookbook 。

翻译介绍
----------------
本书从2008年07月起开始陆续翻译成中文版,频率为大约每月两章,中文版的在线浏览地址为:http://www.sonatype.com/book/reference_zh/public-book.html。此外,译者Juven Xu同时维护了一个Maven中文博客,更新翻译进程,提供Maven相关新闻,以及编写Maven相关技术文章:http://juvenshun.iteye.com/。
Juven Xu
南京大学软件学院毕业,兴趣方向为Java,敏捷开发。有较丰富的使用Maven管理项目的经验。
博客中一些读者的评价:
- 继续关注楼主的翻译,正是看到了楼主的文章,才决定试用maven。 —— Caixiaopig
- 对juven极力推广maven表示敬佩。工作中一直使用maven,深刻的体会它的便利。而且maven的插件(plugin)的开发相当简单,可以解决项目中一些特定而重复性问题,don't repeat yourself ! —— nasvel
- 前五章已经看完,作者的翻译水平是一流的,期待其他章节中。。。。! —— killvin
- 你用过Maven吗?凡是要拍Maven板砖,麻烦先认真使用一下Maven,至少说出个1,2,3来,从Maven2.0开始,我就用Maven替代了Ant的大部分工作,Ant只做一下灵活的任务~~另外、这个东西就是一个项目管理工具。。。复杂在那里。。。 —— phoenixup

本书目录
----------------
前言

1. 介绍Apache Maven
2. 安装和运行Maven
I. Maven实战
  3. 一个简单的Maven项目
  4. 定制一个Maven项目
  5. 一个简单的Web应用
  6. 一个多模块项目
  7. 多模块企业级项目
  8. 优化和重构POM
II. Maven参考
  9. 项目对象模型
  10. 构建生命周期
  11. 构建Profile
  12. Maven装配器
  13. 属性和资源过滤
  14. Maven和Eclipse: m2eclipse
  15. 站点生成
  16. 仓库管理器
  17. 编写插件
  18. 使用其它语言编写插件
附录A. Settings细节
服务B. Sun规格说明非官方实现

 

英文版在线阅读:http://books.sonatype.com/maven-book/reference/public-book.html

中文版在线阅读:http://books.sonatype.com/maven-book/reference_zh/public-book.html

 

翻译进度:http://groups.google.com/group/maven-zh/web/maven

翻译勘误:http://groups.google.com/group/maven-zh/web/maven-2

 

附件是目前已完成章节的PDF。

“一法度衡石丈尺,车同轨,书同文字” —— 《史记·秦始皇本纪》

 

标准的重要性不用我过于强调,想象一下如果不是所有人都基于HTTP开发WEB应用,这个世界会乱成怎样。IE,FF等浏览器之间的差别已经让很多开发者头痛不已。JAVA成功的原因之一就是由于它能屏蔽大部分操作系统的差异,XML流行的原因之一是所有语言都接受它。Maven当然还不能和这些既成功又成熟的技术相比,但所有Maven的用户都应该清楚,Maven提倡的是“约定优于配置(Convention Over Configuration)”,这是Maven最核心的理念之一。

 

Maven是服务于项目生命周期的(有些人说它是build工具,但build只是生命周期的一部分),它试图抽象整个项目生命周期,实际上它也做到了。几乎所有的项目都离不开Mave所定义的生命周期阶段(clean compile test package site...)。不止如此,基于这些阶段,Maven通过插件提供了绝大部分的默认实现,它们不用做任何配置(或者仅需要很少的配置),就能帮你完成你的工作。

 

先看一个简单的基于Ant的build.xml文件。(在Maven之前,你在使用Ant,对吧?如果连Ant都没用,那就更糟了)

Xml代码
  1. <project name="my-project" default="dist" basedir=".">  
  2.     <description>  
  3.         simple example build file  
  4.     </description>  
  5.   <!-- set global properties for this build -->  
  6.   <property name="src" location="src/main/java"/>  
  7.   <property name="build" location="target/classes"/>  
  8.   <property name="dist"  location="target"/>  
  9.   
  10.   <target name="init">  
  11.     <!-- Create the time stamp -->  
  12.     <tstamp/>  
  13.     <!-- Create the build directory structure used by compile -->  
  14.     <mkdir dir="${build}"/>  
  15.   </target>  
  16.   
  17.   <target name="compile" depends="init"  
  18.         description="compile the source " >  
  19.     <!-- Compile the java code from ${src} into ${build} -->  
  20.     <javac srcdir="${src}" destdir="${build}"/>  
  21.   </target>  
  22.   
  23.   <target name="dist" depends="compile"  
  24.         description="generate the distribution" >  
  25.     <!-- Create the distribution directory -->  
  26.     <mkdir dir="${dist}/lib"/>  
  27.   
  28.     <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->  
  29.     <jar jarfile="${dist}/lib/MyProject-${DSTAMP}.jar" basedir="${build}"/>  
  30.   </target>  
  31.   
  32.   <target name="clean"  
  33.         description="clean up" >  
  34.     <!-- Delete the ${build} and ${dist} directory trees -->  
  35.     <delete dir="${build}"/>  
  36.     <delete dir="${dist}"/>  
  37.   </target>  
  38. </project>  

这段代码做的事情是清除目录,创建目录,编译源文件,复制依赖至目标目录。这已经十分简单了,但是,为此你还是需要做一些配置:指定源码目录,指定编译字节码目录,指定目标目录,你还需要记住一些ant的配置命令,如delete, mkdir, javac, jar。这看起来没什么问题,至少目前是这样。

 

可能还有用shell脚本在做build这件事情,那么配置可能会比这个复杂一点(笔者不熟悉shell,如果有能将上述代码用shell重写一遍,笔者将不甚感激)。

 

现在说说Ant或shell/bat存在的问题,首先,有很多配置需要你填写,源码目录,字节码目录……,每一个项目,你都要重复这些劳动;第二,也是更重要的一点,你根本无法保证N个项目的ant配置(或shell)配置使用了相同的风格,后果是,每接受一个项目,开发者都要去学习这份脚本,以了解如何构建项目。如果第一个原因只是为了技术的简化的话,第二个原因更是软件工程的因素,我们不是一个人在开发项目,不是只开发一个项目,所以应该时刻谨记为了别人,为了将来。

 

现在看看使用Maven我们需要什么配置,就一个pom.xml文件:

Xml代码
  1. <project>  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>org.sonatype.mavenbook</groupId>  
  4.   <artifactId>my-project</artifactId>  
  5.   <version>1.0</version>  
  6. </project>  

不用惊讶,Maven不会变魔术,它能做到这么简单,是有条件的,条件就是你要遵守Maven约定。pom.xml所在的目录应为项目的根目录,假设该目录为${proj-dir},那么Maven有以下假设:

  • ${proj-dir}/src/main/java —— 存放项目的.java文件。
  • ${proj-dir}/src/main/resources —— 存放项目资源文件,如spring, hibernate配置文件。
  • ${proj-dir}/src/test/jave —— 存放所有测试.java文件,如JUnit测试类。
  • ${proj-dir}/src/test/resources —— 测试资源文件。
  • ${proj-dir}/target —— 项目输出位置。

运行一条mvn clean package命令,Maven会帮你清除target目录,重新建一个空的,编译src/main/java类至target/classes,复制src/main/resources的文件至target/classes,编译src/test/java至target/test-classes,复制src/test/resources的文件至target/test-classes;然后运行所有测试;测试通过后,使用jar命令打包,存储于target目录。Maven做的事情一点也不少,只是都对用户隐蔽起来了,它只要求你遵循它的约定。

 

这么做有什么好处呢?第一,显而易见,配置大量减少了,随着项目变得越复杂,这种优势就越明显。第二,我这里要强调的是,对于软件工程来说,所有使用Maven的项目,对外都暴露统一的命令集。如mvn clean install。只要项目被正确配置好了,任何一个新人,输入一行Maven命令,就能将项目构建起来了,这大大减少了交流学习的时间。

 

这时可能会有人说,我不想遵守这种约定呢?我要把源码放在${proj-dir}/src/code目录下。首先,问自己三遍,你真的需要这样做么?如果仅仅是因为喜好,那么别耍个性,个性意味着牺牲通用性,意味着增加无谓的复杂度。以下是一个“个性”的例子:

Xml代码
  1. <project>  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>org.sonatype.mavenbook</groupId>  
  4.   <artifactId>my-project</artifactId>  
  5.   <version>1.0</version>  
  6.   <build>  
  7.     <sourceDirectory>src/java</sourceDirectory>  
  8.     <testSourceDirectory>src/test</testSourceDirectory>  
  9.     <outputDirectory>output/classes</outputDirectory>  
  10.     <testOutputDirectory>output/test-classes</testOutputDirectory>  
  11.     <directory>target/jar</directory>  
  12.   </build>  
  13. </project>  

很显然,Maven允许你自定义,比如这个例子中,Maven就被配置成从src/java目录寻找源码,编译class文件至output/classes,从src/test寻找测试源码,编译至output/test-classes目录,最后,打包jar文件至target/jar目录。Maven允许你这么做,但不推荐你这么做。因为一旦你使用这种自定义,习惯Maven约定的人一开始会觉得奇怪,src/main/java目录去哪里了?target下面怎么没有我要的jar文件?这些都造成了无谓的交流成本提高。只有一些特殊的情况,这些自定义手段能帮你解决实际的问题,比如你在处理遗留代码,你没办法改变源码目录结构,这个时候只有让Maven妥协。

 

下面总结一些Maven的默认值,也就是说,虽然你没做什么配置,但是你应该知道Maven假设它们成立,也就是所谓的“约定”:

Maven约定
目录src/main/javajava源码目录
目录src/main/resources资源文件目录
目录src/test/java测试java源码目录
目录src/test/resources测试资源文件目录
目录target打包输出目录
目录target/classes编译输出目录
目录target/test-classes测试编译输出目录
目录target/site项目site输出目录
目录src/main/webappweb应用文件目录(当打包为war时),如WEB-INF/web.xml
jar默认打包格式
*Test.javaMaven只会自动运行符合该命名规则的测试类
%user_home%/.m2Maven默认的本地仓库目录位置
中央仓库Maven默认使用远程中央仓库:http://repo1.maven.org/maven2
1.3Maven Compiler插件默认以1.3编译,因此需要额外配置支持1.5

 

其实基本上所有的约定,或者说默认配置,都可以在Maven的超级POM(super pom)中找到。由于所有的POM都继承了这个超级POM(类似于java中所有类都继承于Object),因此它的默认配置就被继承了。以Maven 2.0.9为例,你可以在%m2_home%/lib/下看到一个名为maven-2.0.9-uber.jar的文件,打开这个文件,可以找到org/apache/maven/project/pom-4.0.0.xml这个文件,这就是超级POM。

 

Maven提供了一套科学的默认配置,它要求你遵循这些配置约定,然后它就会帮你处理日常的事务compile, test, package等等。使用Maven的时候,你应该尽可能遵循它的配置约定,一方面可以简化配置,另一方面可建立起一种标准,减少交流学习成本。一旦你习惯了这种约定,你得到的回报是巨大的。反之,恣意的做自定义,想要Maven像Ant一样听你的话,那么你会讨厌Maven,Maven也会讨厌你。

(译自:Nine Reasons to Use a Repository Manager: Sonatype Nexus

 

 

目前有很多组织使用了一些工具依赖于Maven仓库,但他们并没有采用一个仓库管理器,对于这一点我十分惊讶。可能没人提出来这一点,没人站出来告诉别人使用一个仓库管理器能带来什么好处。我经常能从很多不使用Maven仓库管理器的组织那里听到这样的字眼:“我们不需要那么复杂……”,或者“中央仓库够了,我们不需要在本地建立这样一个仓库”

 

不用说,关于什么“是”一个好的Maven仓库,还存在很多误解。首先,安装它一点都不复杂。你只需要花几分钟就能下载并安装好Nexus,而且我们已经特意的使得这一过程变得容易,并且尽可能减少侵入性。第二,一个缓存远程仓库的仓库管理器并不会缓存所有的东西。一个好的仓库管理器只会按需的缓存内容。这里是为那些排斥仓库管理器的人提供的九条理由。

 

九 - 加速构建

当你在Maven中运行多模块项目构建的时候,你认为Maven是怎么知道它是否需要更新插件或者snapshot依赖的呢?它需要为所有需要检验的构件向服务器提出一个请求。即使没什么变化,如果你的项目依赖于一些SNAPSHO或者你没有指定插件版本,Maven就必须对远程仓库提交数十至数百的请求。所有这些通过公共Internet的请求加起来会浪费很多时间。我看到过一个复杂的构建,在安装了本地Nexus之后,节省了75%的时间。不要再浪费编码时间去无畏的等待Maven对远程仓库的请求了。

 

八 - 节省你自己的带宽

组织越大,节省带宽就越重要。如果你有数千开发者不停的浪费带宽在下载同样的文件,那么使用一个仓库管理器可以保存一个本地的缓存,从而节省大量的带宽。即使一些小型的组织,对于网络连接和IT运行的预算也有限,也必须处理很多开发人员浪费带宽不停重复下载同样文件的问题。

 

七 - 节省中央Maven仓库的带宽

这可以说是一种公德心,利己利人。运行一个中央Maven仓库不容易。基于全球软件构件的需要,服务数百万的请求,数T的数据,一点都不便宜。简单的事情如每个组织安装一个本地仓库管理器可以大量降低对中央仓库的带宽需求(至少一半)。如果你有大于两个开发者在使用Maven,那么请为了中央仓库义务的安装一个仓库管理器。

 

六 - 可预见性和稳定性

过去一些年,你多久会遇到一次由于断网引起的业务彻底中断?让每天的工作依赖于中央仓库意味着你正依赖于因特网连接(事实上中央仓库是24/7可用的)。虽然我们有信心保持中央仓库24/7运行,但你还是应该花一点时间建立你自己的仓库,以确保你的开发团队不会由于任意一方的网络中断而惊讶。如果你有本地的仓库管理器,如Nexus,你可以确保即使你失去了网络连接你还能工作。

 

五 - 控制和审计

当然,你已经转移到了Maven(或者Ivy,Ivy读取同样的仓库),而且你有一房间的开发人员,他们被授权添加或者删除依赖,以及试验新的框架。我们都清楚。开发人员可能都对试验新框架更感兴趣,而不是正常工作。但不幸的是,很多时间,一个架构师,或者一个架构小组需要建立组织使用的基线标准。Nexus提供这一层次的控制。

 

如果你需要对那些组织中使用的构件进行更多的监管,看一下Nexus。没有仓库管理器,对于那些你开发团队会使用的依赖你只有很少的控制。

 

四 - 能够部署第三方构件

你希望能够基于开源数据库工作,如MySQL或者Postgres,但是你组织有不同的需求,和Oracle有一个长期的支持合同。因此,你有一系列JAR文件不能够从公共Maven仓库获得。你需要将这些构件部署到仓库中,然后配置Maven去读取该仓库。

 

你不用为此手工编写一些POM,只要下载Nexus,然后花两三分钟时间使用这个免费,功能强大的工具来创建一个仓库,部署第三方构件。Nexus提供了直观的上传界面,你可以使用它上传任意JAR文件,然后在你项目依赖中引用它。

 

三 - 可以建立本地内部仓库

非常好,你正使用Maven,每个人都从头开始根据代码执行快速的构建。整个一年,这都工作得很好,但是当系统变得庞杂,程序员开始抱怨构建花了太多时间。你可以通过分割项目解决这个问题,然后使用Nexus作为一个内部仓库来存储内部的依赖。

 

例如,有一个公司有30个开发者,被分成三个10人的小组,每个小组关注于系统的不同部分。如果不能够很方便的实现内部依赖共享,类似的小组就不得不创建一个基于文件系统的仓库,或者将系统整个的构建,那样依赖就能被安装到每个开发者的本地仓库。

 

另一种选择是将项目分隔成不同的模块,每个模块都依赖于存储在Nexus仓库中的构件。这么做之后,小组之间可以通过基于Nexus交换已编译的snapshot和release构件来协作。换句话说,你不需要让没个开发者签出巨大的包含整个组织代码的多模块项目。组织中的每个小组可以发布snapshot构件至本地Nexus,从而每个小组可以维护一个只包含它负责代码的项目结构。

 

在宏观层次,公共Maven仓库及建立项目定义的严格制度,可以充当开源世界中跨项目协作的基础。(这听起来是不是很重要,很“令人敬畏”?)

 

二 - 可以建立公共仓库

如果是一个开源项目,或者你将软件发布到公共环境,Nexus可以是一个你用来为外部用户提供构件服务的工具。这么想……上你发布项目的一个版本是什么时候?假设它没被发布到Maven仓库中,你很可能需要编写一些脚本来打包此次发布的内容,可能某些特定的人需要使用一个超级权限的密钥来为该发布签名。然后,你必须将其上传到一些web服务器上,然后确保相应的页面被更新,正确描述了此次发布。这里有很多不必要的复杂度。

 

如果你正使用Nexus,它可以被配置成一个暴露在公共世界的宿主仓库,你就可以使用Maven的打包和装配能力,以及Maven仓库的结构,来使得一次发布变得非常的简单。而且,这不仅仅正对于JAR文件和Java web应用;Maven仓库可以存储任何种类的构件(事实上,我正考虑使用Maven仓库结构来发布用户文档和视频内容)。Nexus,及Maven仓库,总得来说为发布定义了大家都知道的结构。如果你正编写一些Java类库,将其发布到你自己的Nexus实例中,然后提供公共仓库服务,就可以让人们更容易的马上开始使用的的代码。

 

一 - 这相当简单

类似的事情我经常听到。有一个早晨的Scrum会议,或者项目状态的周会。一些低层的程序员怯懦的举起他的手说,“我们可以安装一个如Nexus的仓库软件么?我认为它能帮助我们显著的加速很多工作。”一直被时间的预算压着的开发经理,听到单词“安装”和“软件”,就马上否决了这件事情,理由是他们没有时间做这种基础工作,因为这不是“需求驱动”的。(我看到过这种事情发生。)

 

这个经理不知道的是,在一个共享的机器上安装Nexus,然后配置随后的系统服务可能是需要花不到十分钟的时间。事实上,你可以在免费的Maven书上读到安装说明。安装Nexus十分的简单。我已经有了一些实践,但是我认为任何一个知道如何操作web浏览器的人都可以在几分钟内完成这项工作。

 

书继续翻译着... 隔几天做一点,就这么着,离完成距离也不远了。

 

Profile这个单词不知道如何翻译,由于在Maven语境中的特殊性,字典上的解释似乎都不适用,那我就干脆保留英文原样了,毕竟在使用maven profile的时候我们不能在XML文件中写中文TAG。

 

介绍下这一章的内容:

 

  • 构建可移植性。有些项目只能由某人在某台特定的机器上运行才能build成功,而有些项目svn co + mvn clean install 就搞定了,它们的区别是什么?
  • 使用Maven Profile实现构建可移植性。一个环境一个Profile,免去过多手工配置。
  • 激活Maven Profile。如何通过操作系统属性,JDK版本等环境属性激活Profile。此外我还有一文“激活Maven Profile的几种方式”可作为补充。
  • 如何在Settings.xml中配置Profile,它和POM中的Profile有什么区别。
  • 如何将Profile放到外部的profile.xml文件中。
  • 如何列出活动的Profile。mvn help:active-profiles
  • 一些使用Profile的最佳实践。为环境创建Profile,为机密信息使用Profile,为平台分类器使用Profile。

 

再发一下《Mavne权威指南》的在线阅读地址:

http://books.sonatype.com/maven-book/reference_zh/public-book.html

 

如果发现错误,这里有勘误表:

http://groups.google.com/group/maven-zh/web/maven-2

 

  • 00:02
  • 评论 / 浏览 (6 / 1207)

作为目前最强大的Maven仓库管理器(Repository Manager),Nexus每天都在进步,由于它是开源的,任何人都可以随时帮助它改进。这里是Nexus相关的邮件列表地址:http://nexus.sonatype.org/dev/mailing-lists.html

 

这里介绍下几个Nexus 1.1 及 1.1.1 中引入和完善的两个功能:

 

Nexus WAR

 

在Nexus 1.0 及之前的版本中,Nexus是通过一个内嵌的Jetty启动的。比如,在Windows下,要启动Nexus,我们需要进入 %Nexus_install%\bin\jsw\windows-x86-32 目录,执行Nexus.bat脚本。或者,可以使用InstallNexus.bat将Nexus安装为一个Windows服务,之后就可以 StartNexus.bat 和 StopNexus.bat 。大部分情况下,这很方便,但是,肯定有一部分用户希望能直接将 Nexus 扔到Tomcat下,像Hudson那样。

 

OK,Nexus 1.1 中我们做到了,1.1.1中又修复了一些bug,Nexus WAR相当稳定了,至少在Tomcat下如此,GlassFish下还有些问题。由于笔者参与了这部分开发,因此在这里王婆卖瓜下了,以下是一个安装Nexus WAR的简单教程,以Tomcat为例。

 

  1. 到这个地址下载 nexus-1.1.1.war:http://nexus.sonatype.org/downloads/
  2. 将该文件放到 %TOMCAT_HOME%\webapps 目录下(你可以更改文件名为nexus.war,以方便web访问)。
  3. 启动Tomcat,你可以通过浏览器访问Nexus了,如 http://localhost:8080/nexus ,注意Nexus的默认登陆用户名密码是 admin/admin123。
  4. 如果你不做任何配置,你会发现在你的用户目录下有一个名为 sonatype-work/nexus 的文件夹,该文件夹下面存储了Nexus相关的配置文件,以及Nexus仓库的内容(时间长了会比较大)。
  5. 如果你不希望在用户目录下存储这些内容,你也可以自定义,第一种方法是设置一个key为 PLEXUS_NEXUS_WORK 的环境变量,使其值指向你希望的目录。两一种方法是配置WAR中的 WEB-INF\plexus.properties 文件,如 nexus-work=/path/to/your/customization 。

 

Nexus WAR 介绍完毕,想用的去下载吧,下面再介绍一个也是1.1中引入的比较有用的功能。

 

索引浏览(Index Browsing)

 

在1.0及之前的版本中,Nexus支持仓库的本地存储浏览,这是一个非常棒的特性,通过树状的UI,我们可以清晰的看到Nexus仓库中的内容,并能很方便的下载。

 

但是,你会发现,假如一个Nexus仓库是代理仓库(proxy repository),默认它不会有任何内容,只包含索引文件,只有在收到请求后(如来自Maven),它才会去远程仓库下载内容。之后你才能看到本地缓存的内容。好在我们有搜索功能,即使本地存储不包含缓存内容,只要远程仓库包含而且我们下载了索引,就能搜索的到。

 

1.1引入了索引浏览功能,只要下载远程索引,我们就可以以树状的形式浏览远程仓库的内容:

假如你代理了中央仓库,那么你就可以在不下载实际内容的情况下清晰的浏览它所包含的所有内容,这很实用哦。

 

 

最后,Sonatype对外提供了一个公共仓库(基于Nexus),大家可以随时浏览,或者配置对它的代理:http://repository.sonatype.org/

使用Maven进行开发的时候,比较常见的一个问题就是如何寻找我要的依赖,比如说,我想要使用activeMQ,可是我不知道groupId,artifactId,和合适的version。怎么办呢?本文介绍几个提供maven仓库搜索服务的站点。

 

1. http://repository.sonatype.org/index.html

该服务器是由Sonatype提供的,Sonatype是由Maven支付Jaso Van Zyl创立的致力于maven,Nexusm2eclipse等产品的公司。该仓库搜索的后台使用了Nexus,Nexus是目前为止最为强大的Maven仓库管理器,而且,它是开源的。如果你组织内部在大量使用Maven,那么就会需要假设自己的Maven仓库,Nexus是很好的选择,你可以免费下载安装使用。

如上图,你可以在左边的搜索框输入你想要搜索的内容,如org.apache.activemq,右边就会出现相应的结果(pom, jar)。点击最右边的pom链接可以直接查看pom内容(然后直接复制粘贴你需要的groupId,artifactId和version),你也可以点击artifact直接下载jar文件。

 

2. http://www.mvnbrowser.com/

mvnbrowser代理很多主要的公共Maven仓库,并且对这些仓库进行的详细的介绍,包括Apache, Codehaus, JavaNet等等,请看:http://www.mvnbrowser.com/repositories.html。mvnbrowser的功能比nexus简单,但是也非常实用,你可以在右上角的搜索框输入关键字,然后搜索,你会看到一系列搜索结果,有趣的在后面,当你点击某个结果的时候,详细信息页面会有一个POM代码片段,这十分有用,因为你可以直接复制。此外,这个构件的所有版本信息,它是否有可用的javadoc和source code,它依赖了那些构件,拥有哪些文件,被谁引用了,在哪些仓库中可用,许可证是什么,等等。这些非常实用的信息都通过UI很友好的得到的体现。

 

3. http://www.mvnrepository.com/

mvnrepository也是一个非常有用的maven仓库搜索服务,它最大的特点就是简单,除了搜索什么都没有。类似的,你可以在页面最上方输入关键字以进行搜索。得到结果之后再点击以查看详细信息:该构件的坐标POM片段,版本信息,jar下载连接都可以得到。此外,还有一个有趣的特性是,你能看到该构件的文件大小变更状态图。

在mvnrepository中,通过tag cloud页面,哪些关键字最流行,一目了然。

 

最后,上述提到的站点都是公共站点,如果你在公司内部使用Maven,那么最好自己架设一个Maven仓库服务器,那样,对外只需要一次下载。Nexus是一个很好的选择,它提供了异常强大的功能,搜索也当然在内了,要详细了解可以查看:http://books.sonatype.com/maven-book/reference_zh/repository-manager.html

之前写过篇文章:Maven生命周期详解。说是详解,也就寥寥几百字,怎可能多详细?现在好了,Maven权威指南中有一章专门介绍生命周期,现在已经翻译完毕,请看:http://www.sonatype.com/book/reference_zh/lifecycle.html

 

大致内容如下:

  • 什么是生命周期
  • 解释三种生命周期 clean, default, site
  • 插件目标(plugin goal)绑定生命周期阶段(lifecycle phase)
  • 各种打包类型(JAR, WAR, POM ... )绑定的插件目标
  • 通用生命周期的介绍,包括Process Resources, Compile, Test, Install等等。

 

看完本章,你就能很清楚的了解为什么我们要输入mvn clean install, 或者mvn deploy, 或者mvn tes
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值