1. Maven的简介
1.1 Maven是什么?
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。
1.2 约定优于配置
Maven 对约定优于配置的应用不仅仅是简单的目录位置,Maven 的核心插件使用了一组通用的约定,以用来编译源代码,打包可分发的构件,生成 web 站点,还有许多其他的过程。 Maven 的力量来自它的"武断",它有一个定义好的生命周期和一组知道如何构建和装配软件的通用插件。如果你遵循这些约定,Maven 只需要几乎为零的工作——仅仅是将你的源代码放到正确的目录,Maven 将会帮你处理剩下的事情。
使用“遵循约定优于配置”系统的一个副作用是用户可能会觉得他们被强迫使用一种特殊的方法。 当然 Maven 有一些核心观点不应该被怀疑,但是其实很多默认行为还是可配置的。 例如项目源码的资源文件的位置可以被自定义,JAR 文件的名字可以被自定义,在开发自定义插件的时候,几乎任何行为可以被裁剪以满足你特定的环境需求。 如果你不想遵循约定,Maven 也会允许你自定义默认值来适应你的需求。
1.3 一个“项目”的概念模型
Maven不仅仅是一个“构建工具”,它不仅仅是在类似于 make 和 Ant 的工具的基础上的改进,它是包含了一组关于软件项目和软件开发的语义规则的平台。这个基于每一个项目定义的模型实现了如下特征:
依赖管理
由于项目是根据一个包含组标识符,构件标识符和版本的唯一的坐标定义的。项目间可以使用这些坐标来声明依赖。
远程仓库
和项目依赖相关的,我们可以使用定义在项目对象模型(POM)中的坐标来创建 Maven 构件的仓库。
全局性构建逻辑重用。插件被编写成和项目模型对象(POM)一起工作,它们没有被设计成操作某一个已知位置的特定文件。一切都被抽象到模型中,插件配置和自定义行为都在模型中进行。
工具可移植性/集成
像 Eclipse,NetBeans,和 InteliJ 这样的工具现在有共同的地方来找到项目的信息。在 Maven 出现之前,每个 IDE 都有不同的方法来存储实际上是自定义项目对象模型(POM)的信息。Maven 标准化了这种描述,而虽然每个 IDE 仍然继续维护它的自定义项目文件,但这些文件现在可以很容易的由模型生成。
便于搜索和过滤构件
像 Nexus 这样的工具允许你使用存储在 POM 中的信息对仓库中的内容进行索引和搜索。
Maven 为软件项目的语义一致性描述的开端提供了一个基础。
1.4 Maven是Ant的另一种选择么?
Maven 是 Ant 的另一种选择。你也可以很容易的在你项目的 Maven 构建中集成 Ant 构建脚本。很明确的说作为构建的基本技术,Maven 是 Ant 的更好选择。
1.5 参考
http://www.sonatype.com/books/maven-book/reference_zh/introduction.html
2. 安装和运行Maven
2.1 Maven下载
Maven官方下载地址:http://maven.apache.org/download.html
当前最新版本:Maven 2.2.1
2.2 Maven安装
在Windows 2000/XP平台上
- 解压发布包,例如apache-maven-2.2.1-bin.zip,到目录下,假定为C:\apache-maven-2.2.1
- 添加M2_HOME的环境变量,设置变量的值为C:\apache-maven-2.2.1
- 添加M2的环境变量,设置变量的值为 %M2_HOME%\bin.
- 可选: 添加MAVEN_OPTS的环境变量,设置变量的值为:-Xms256m -Xmx512m.
- 更新Path的环境变量,追加 %M2% ;Path的变量值上.
- 确认环境变量JAVA_HOME 已经存在, %JAVA_HOME%\bin 已经在Path的环境变量中.
- 在命令行模式下运行mvn –version确认安装正确。
参考:http://maven.apache.org/download.html
2.3 用户相关配置和仓库
当你不再仅仅满足于使用Maven,还想扩展它的时候,你会注意到Maven创建了一些本地的用户相关的文件,还有在你home目录的本地仓库。在%HOME%/.m2目录下有:
%HOME%/.m2/settings.xml
该文件包含了用户相关的认证,仓库和其它信息的配置,用来自定义Maven的行为。
%HOME%/.m2/repository/
该目录是你本地的仓库。当你从远程Maven仓库下载依赖的时候,Maven在你本地仓库存储了这个依赖的一个副本。
2.4配置使用Maven仓库的镜像
1. 拷贝附件settings.xml:到打开的目录下,也就是 %HOME%\.m2
参考:http://www.sonatype.com/books/nexus-book/reference/maven-sect-single-group.html
3. Maven实战
3.1一个简单Maven的web应用项目
1. 运行命令,例如创建webapp1
mvn archetype:create -DarchetypeGroupId=com.channelsoft.app -DarchetypeArtifactId=channelsoft-webapp-archetype -DarchetypeVersion=1.0.0.3 -DgroupId=com.channelsoft.webapp1 -DartifactId=webapp1
2. 运行完后的目录结构:
E:\FANGYUN\TEMP\WEBAPP1
└─src
└─main
├─java
│ └─com
│ └─channelsoft
│ └─webapp1
├─resources
└─webapp
└─WEB-INF
参考:
a) 配置模板工程:http://maven.apache.org/plugins/maven-archetype-plugin/advanced-usage.html
b) 共享模板工程:http://maven.apache.org/plugins/maven-deploy-plugin/
3.2 简单的项目对象模型 (Project Object Model)
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.channelsoft.webapp1</groupId>
<artifactId>webapp1</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>channelsoft-webapp Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
这个
当Maven运行的时候,它是根据项目的
$ mvn help:effective-pom
一旦你运行了此命令,你应该能看到一个大得多的POM,它暴露了Maven的默认设置
3.3核心概念
3.3.1 Maven插件和目标 (Plugins and Goals)
一个Maven插件是一个单个或者多个目标的集合。Maven插件的例子有一些简单但核心的插件,像Jar插件,它包含了一组创建JAR文件的目标,Compiler插件,它包含了一组编译源代码和测试代码的目标,或者Surefire插件,它包含一组运行单元测试和生成测试报告的目标。而其它的,更有专门的插件包括:Hibernate3插件,用来集成流行的持久化框架Hibernate,JRuby插件,它让你能够让运行ruby称为Maven构建的一部分或者用Ruby来编写Maven插件。Maven也提供了自定义插件的能力。一个定制的插件可以用Java编写,或者用一些其它的语言如Ant,Groovy,beanshell和之前提到的Ruby。
Figure 3.1. 一个插件包含一些目标
一个目标是一个明确的任务,它可以作为单独的目标运行,或者作为一个大的构建的一部分和其它目标一起运行。一个目标是Maven中的一个“工作单元(unit of work)”。目标的例子包括Compiler插件中的
目标定义了一些参数,这些参数可以定义一些明智的默认值。在
Maven的核心对你项目构建中特定的任务几乎毫无所知。就它本身来说,Maven不知道如何编译你的代码,它甚至不知道如何制作一个JAR文件,它把所有这些任务代理给了Maven插件,像Compiler插件和Jar插件,它们在需要的时候被下载下来并且定时的从Maven中央仓库更新。当你下载Maven的时候,你得到的是一个包含了基本躯壳的Maven核心,它知道如何解析命令行,管理classpath,解析POM文件,在需要的时候下载Maven插件。通过保持Compiler插件和Maven核心分离,并且提供更新机制,用户很容易能使用编译器最新的版本。通过这种方式,Maven插件提供了通用构建逻辑的全局重用性,有不会在构建周期中定义编译任务,有使用了所有Maven用户共享的Compiler插件。如果有个对Compiler插件的改进,每个使用Maven的项目可以立刻从这种变化中得到好处。(并且,如果你不喜欢这个Compiler插件,你可以用你的实现来覆盖它)。
3.3.2 Maven生命周期 (Lifecycle)
类似命令mvn package。命令行并没有指定一个插件目标,而是指定了一个Maven生命周期阶段。一个阶段是在被Maven称为“构建生命周期”中的一个步骤。生命周期是包含在一个项目构建中的一系列有序的阶段。Maven可以支持许多不同的生命周期,但是最常用的生命周期是默认的Maven生命周期,这个生命周期中一开始的一个阶段是验证项目的基本完整性,最后的一个阶段是把一个项目发布成产品。生命周期的阶段被特地留得含糊,单独的定义为验证(validation),测试(testing),或者发布(deployment),而他们对不同项目来说意味着不同的事情。例如,打包(package)这个阶段在一个项目里生成一个JAR,它也就意味着“将一个项目打成一个jar”,而在另外一个项目里,打包这个阶段可能生成一个WAR文件。Figure 3.2, “一个生命周期是一些阶段的序列”展示了默认Maven生命周期的简单样子。
Figure 3.2. 一个生命周期是一些阶段的序列
插件目标可以附着在生命周期阶段上。随着Maven沿着生命周期的阶段移动,它会执行附着在特定阶段上的目标。每个阶段可能绑定了零个或者多个目标。在之前的小节里,当你运行mvn package,你可能已经注意到了不止一个目标被执行了。检查运行mvn package之后的输出,会注意到那些被运行的各种目标。当这个简单例子到达
我们知道,在包类型为jar的项目中,打包阶段将会创建一个JAR文件。但是,在它之前的目标做什么呢,像compiler:compile和surefire:test?在Maven经过它生命周期中package之前的阶段的时候,这些目标被运行了;Maven执行一个阶段的时候,它首先会有序的执行前面的所有阶段,到命令行指定的那个阶段为止。每个阶段对应了零个或者多个目标。我们没有进行任何插件配置或者定制,所以这个例子绑定了一组标准插件的目标到默认的生命周期。当Maven经过以package为结尾的默认生命周期的时候,下面的目标按顺序被执行:
resources:resources
Resources插件的resources目标绑定到了resources 阶段。这个目标复制src/main/resources下的所有资源和其它任何配置的资源目录,到输出目录。
compiler:compile
Compiler插件的compile目标绑定到了compile 阶段。这个目标编译src/main/java下的所有源代码和其他任何配置的资源目录,到输出目录。
resources:testResources
Resources插件的testResources目标绑定到了test-resources 阶段。这个目标复制src/test/resources下的所有资源和其它任何的配置的测试资源目录,到测试输出目录。
compiler:testCompile
Compiler插件的testCompile目标绑定到了test-compile 阶段。这个目标编译src/test/java下的测试用例和其它任何的配置的测试资源目录,到测试输出目录。
surefire:test
Surefire插件的test目标绑定到了test 阶段。这个目标运行所有的测试并且创建那些捕捉详细测试结果的输出文件。默认情况下,如果有测试失败,这个目标会终止。
jar:jar
Jar插件的jar目标绑定到了package 阶段。这个目标把输出目录打包成JAR文件。
Figure 3.4. 被绑定的目标随着它们阶段的运行而运行
总结得来说,当我们运行mvn package,Maven运行到打包为止的所有阶段,在Maven沿着生命周期一步步向前的过程中,它运行绑定在每个阶段上的所有目标。你也可以像下面这样显式的指定一系列插件目标,以得到同样的结果:
mvn resources:resources \
compiler:compile \
resources:testResources \
compiler:testCompile \
surefire:test \
jar:jar
3.3.3 Maven坐标 (Coordinates)
POM为项目命名,提供了项目的一组唯一标识符(坐标),并且通过依赖 (dependencies) ,父 (parents) 和先决条件 (prerequisite) 来定义和其它项目的关系。
groupId
d 团体,公司,小组,组织,项目,或者其它团体。团体标识的约定是,它以创建这个项目的组织名称的逆向域名(reverse domain name)开头。来自Sonatype的项目有一个以com.sonatype开头的groupId,而Apache Software的项目有以org.apache开头的groupId。
artifactId
在groupId下的表示一个单独项目的唯一标识符。
version
一个项目的特定版本。发布的项目有一个固定的版本标识来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识给版本加上一个“SNAPSHOT”的标记。
项目的打包格式也是Maven坐标的重要组成部分,但是它不是项目唯一标识符的一个部分。一个项目的groupId:artifactId:version使之成为一个独一无二的项目;你不能同时有一个拥有同样的groupId, artifactId和version标识的项目。
packaging
项目的类型,默认是jar,描述了项目打包后的输出。类型为jar的项目产生一个JAR文件,类型为war的项目产生一个web应用。
还有第五个,名为
3.3.4 Maven仓库(Repositories)
Maven自带了一个用来下载Maven核心插件和依赖的远程仓库地址(http://repo1.maven.org/maven2)。
Maven仓库的标准是按照下面的目录格式来存储构件,相对于仓库的根目录:
/<de<groupId de<>/< de<artifactId de<>/< de<version de<>/< de<artifactId de<>-<version>.< de<packaging de<>
如果你运行mvn install命令,Maven会把我们项目的构件安装到本地仓库。
3.3.5 Maven依赖管理 (Dependency Management)
Maven最强大的特征之一,它支持了传递性依赖(transitive dependencies)。假如你的项目依赖于一个库,而这个库又依赖于五个或者十个其它的库(就像Spring或者Hibernate那样)。你不必找出所有这些依赖然后把它们写在你的
展现了一个传递性依赖的可能场景。
Figure 3.7. Maven处理传递性依赖
在上图中,项目A依赖于项目B和C,项目B依赖于项目D,项目C依赖于项目E,但是项目A所需要做的只是定义对B和C的依赖。当你的项目依赖于其它的项目,而这些项目又有一些小的依赖时(向Hibernate, Apache Struts 或者 Spring Framework),传递性依赖使之变得相当的方便。Maven同时也提供了一种机制,能让你排除一些你不想要的传递性依赖。
Maven也提供了不同的依赖范围(dependency scope)。Simple项目的pom.xml包含了一个依赖——junit:junit:jar:3.8.1——范围是test。当一个依赖的范围是test的时候,说明它在Compiler插件运行compile目标的时候是不可用的。它只有在运行compiler:testCompile和surefire:test目标的时候才会被加入到classpath中。
可以配置Maven让它在生成的构件中捆绑依赖,你也可以配置Maven,使用
3.3.6 站点生成和报告 (Site Generation and Reporting)
另外一个Maven的重要特征是,它能生成文档和报告。在simple项目的目录下,运行以下命令:
$ mvn site
这将会运行
4. 定制一个Maven项目
4.1添加新的依赖
通过一个十分有用的站点 http://www.mvnrepository.com 来定位的。 该站点提供了针对 Maven 仓库的搜索接口,你可以用它来搜索依赖。当你在这上面搜索构件时,它会显示一个 artifactId 和所有 Maven 中央仓库所知道的版本。 点击某个特定的版本后,它会载入一个页面,这个页面就包括了你需要复制到你自己项目 pom.xml 中的依赖元素。 你经常会发现某个特定的类库拥有多于一个的 groupId,这个时候你需要通过 mvnrepository.com 来帮助确定你到底需要怎样配置你的依赖。
4.2 运行项目
$ mvn install
$ mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main
想要查看 Exec 插件的完整描述,运行:
$ mvn help:describe -Dplugin=exec -Dfull
4.3 浏览你的项目依赖
如果你需要找出 classpath 中有什么,你可以使用 Maven Dependency 插件来打印出已解决依赖的列表。 要打印出 Simple Weather 项目的这个列表,运行
$ mvn dependency:resolve
如果你想知道你项目的整个依赖树,你可以运行
$ mvn dependency:tree
如果你还不满足,或者想要查看完整的依赖踪迹,包含那些因为冲突或者其它原因而被拒绝引入的构件,打开 Maven 的调试标记运行:
$ mvn install -X
4.4 执行单元测试
如果你想要运行到
$ mvn test
Maven Surefire 插件有一个
4.5 跳过单元测试
Maven 提供了跳过单元测试的能力,只需要使用 Surefire 插件的
$ mvn install -Dmaven.test.skip=true
另一种配置 Maven 跳过单元测试的方法是给你项目的
Example 4.18. 跳过单元测试
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
4.6 构建一个打包好的命令行应用程序
使用预定义的 jar-with-dependencies 格式。 要配置 Maven Assembly 插件, 我们需要在 pom.xml 中的 build 配置中添加如下的 plugin 配置。
Example 4.19. 配置 Maven 装配描述符
<project>
[...]
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
添加好这些配置以后,你可以通过运行 mvn assembly:assembly 来构建这个装配。
我们可以在命令行重新运行
$ cd target
$ java -cp simple-weather-1.0-jar-with-dependencies.jar org.sonatype.mavenbook.weather.Main 10002
5. Maven和Eclipse: m2eclipse
5.1安装 m2eclipse 插件
从如下的Eclipse更新URL安装m2eclipse插件:
m2eclipse 插件: http://m2eclipse.sonatype.org/update/
6. 仓库管理器Nexus
6.1从Sonatype下载Nexus
要下载Nexus,访问http://nexus.sonatype.org/downloads/
6.2安装Nexus
安装Nexus很简单,打开Nexus归档文件至一个目录。如果你正在本地工作站上安装Nexus,以测试它的运行,你可以将其安装至你的用户目录,或者随便什么你喜欢的地方;Nexus没有任何硬编码的目录,它能在任意目录运行。
6.3运行Nexus
nexus-webapp-1.4.1/bin/jsw/linux-x86-32/nexus start
要使用Nexus,启动一个web浏览器然后输入URL:http://localhost:8081/nexus。点击web页面右上角的“Log In”链接,你应该看到如下的登陆对话框。
默认的NEXUS用户名和密码是"admin" 和 "admin123"。
6.4部署构件至Nexus
要部署一个发布版构件至Nexus,你需要配置你项目POM中
Example 6.1. 为部署配置发布版本仓库
<project>
...
<distributionManagement>
...
<repository>
<id>releases</id>
<name>Internal Releases</name>
<url>http://10.130.41.233:8081/nexus/content/repositories/releases</url>
</repository>
...
</distributionManagement>
...
</project>
你可以使用你Nexus安装的主机和端口来替换
$ mvn deploy
6.5部署第三方构件
Maven项目可以依赖于一个构件,这个构件不能从中央Maven仓库或任何其它公开Maven仓库找到。有很多原因可能导致这种情形发生:这个构件可能是私有数据库的JDBC驱动如Oracle,或者你依赖于另一个JAR,它既不开源也无法免费获得。在这样的情况下,你就需要手动拿来这些构件然后发布到你自己的仓库中。Nexus提供宿主的"third-party"仓库,就是为了这个目的。
例如:你将需要发布Oracle JDBC构件至你的Nexus third-party仓库。为此,从http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html下载Oracle JDBC驱动,然后保存至文件
mvn deploy:deploy-file -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.3.0 -Dpackaging=jar -Dfile=ojdbc.jar -Durl=http://10.130.41.233:8081/nexus/content/repositories/thirdparty -DrepositoryId=thirdparty