第一章 Maven介绍
Maven是什么
Maven包括一系列构建标准:一个产品库模型,一个管理和描述项目的软件引擎。定义了构建、测试、部署项目产品的标准生命周期。提供了一个简单实用符合 Maven标准的通用构建逻辑。是一个在Apache软件基金会下的开源项目,是一个声明式项目管理工具(通过项目对象模型Project Object Model),用来简化软件项目管理过程的框架。
Maven好处
一致(Coherence)
Maven以一系列最佳实践为基础使组织标准化,因为Maven是根据标准化模型构造的。
重用(Reusablity)
Maven构建于可重用的基础之上。当你使用Maven时你可以高效的重用整个行业完整的最佳实践。
敏捷(Agility)
Maven降低了重用构建逻辑和软件组件的门槛。使创建一个组件然后再整合它到多个项目中变得容易。
可维护(Maintainability)
使用Maven的组织不必为了构建而构建,可以集中精力于构造应用程序。Maven项目的可维护性是很高的,因为它遵从了通用的公共定义模型。
Maven原则
习惯优于配置(Convention over configuration)
标准的项目目录结构
这个就不解释了,应该都明白
一个项目一个主输出的思想
举个例子,如果有一个客户端/服务端项目,Maven只能有一个输出,所以Maven鼓励把该项目分割成3个子项目,客户端一个,服务器端一个和公共类库 一个,通过依赖性来引用jar包,这样符合依赖拆分(separation of concerns(SoC))原则
标准命名规则
如<artifactId>-<version>.<extension>(产品名-版本号.扩展名)
common-logger-1.2.jar不能叫common-logger.jar,因为缺少版本号。
重用构建逻辑(Reuse of build logic)
Maven鼓励依赖拆分(Soc)。通过封装构建逻辑到一致性的模块中形成插件来执行这一原则。Maven可以被认为是一种协同各种可执行插件的框架。任何东西在Maven中都是插件的运行结果。在Maven中,插件是所有事物的关键构建块。
声明式执行(Declarative execution)
项目对象模型Project Object Model(POM)
Maven是以项目为中心设计的,POM是单个项目的描述。没有POM,Maven毫无用处。POM驱动了Maven的执行,这种方式称为模型驱动或者声明式执行。
POM是一个xml文件如
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
上面的POM可以允许你编译、测试、生成基本的文档,为什么只需要这几行呢,因为Maven有个隐含的Super POM,Super POM是Maven鼓励的一个规则,就像java中的所有类的父类是Object一样,Maven中的所有POM都有一个Super POM。
元素说明:
project:pom.xml的顶层元素
modelVersion:pom文件的版本号,一旦有模块更改就会更新
groupId:说明创建这一项目组织的唯一标识。通常为基于组织的全限定名,例如org.apache.maven.plugins
artifactId:本项目生成产品的标识
packaging:打包类型如(JAR,WAR,EAR...),默认为jar
version:产品的版本号
name:产品显示名
url:项目网站
description:项目描述
所有元素的参考http://maven.apache.org/maven-model/maven.html
构建生命周期(build life cycle)
软件项目的构建路径为:预处理(preparation),编译(compilation),测试(testing),打包(packaging),安装 (installation),等。Maven为这种项目提供的这些路径称为构建生命周期。在Maven中构建生命周期由一些列阶段组成,每个阶段可以运 行1个或多个与该阶段相关的行为或目标。例如,编译阶段调用一些目标去编译一些类。
在Maven中你所要做的就是告诉Maven你所需要的在标准构建声明周期中的阶段。需要注意的是,每个阶段都会被执行直到你指定的阶段。如,如果你告诉 Maven到compile阶段,Maven将自动执行validate,initialize,generate-sources,process- sources,generate-resources,compile这些先于complile的阶段。这些标准构建声明周期由许多阶段组成,这些阶段 可以被认为是扩展点。当你想在构建生命周期中加入功能时你可以将可重用的构建逻辑插件直接插入到构建生命周期中,任何时候当你需要优化项目构建时你可以使 用一个已存在的插件或者创建一个自定义的插件。
一致性组织和依赖(Coherent organization of dependencies)
看一下Maven是如何做到这点的,下面是个junit的例子
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
dependency表示对产品库(artifact respository)的一个引用,一个dependency通过groupId,artifactId,version来唯一标识一个产品,在 Maven中依赖是声明式的,你无需告诉Maven依赖的物理位置,只需告诉项目的特定期望。如上例你无需关心junit jar包的具体位置只需在POM中配置需要什么产品。依赖管理是Maven的一个强大能力。Maven查找所有可访问的远程库(remote respository)中的最接近依赖请求的产品,一旦找到就把它复制到本地库中(local respository)以供项目使用。如果本地库中存在则默认不会查找远程库。
本地库(Local Maven repository)
默认路径:<user_home>/.m2/repository
目录结构:
<组织名,如果有点分割则有多层目录和java包组织方式相同>
<产品>
<版本号>
产品-版本.扩展名
产品-版本.pom
...
定位依赖产品(Locating dependency artifacts)
生成路径 如/groupId/artificatId/version/artificatId-version.jar,先从本地库中查找,如果本地库中不存在 则会到远程库中获取。默认情况下Maven会到Maven中心库查找,中心库位置:http://repo1.maven.org/maven2,如有多 个远程库,maven会按配置次序获取。一旦依赖满足,产品会被安装到本地库。这样就可以使本机上的所有项目都共享你的本地库,不需要把每个包都复制一份 到一个项目下。避免了当项目增多时包容量的不断增大,而且包并不是你的代码的一部分不需要放到版本控制工具中(SCM)。
第二章 起步
2.1 准备使用Maven
下载地址和安装方法:
http://maven.apache.org/download.html
代理配置(如果你在防火墙后面)
创建文件<user_home>/.m2/settings.xml,内容如下
外部代理
<settings>
<proxies>
<proxy>
<active>true</active>
<protocol>http</protocol>
<host>proxy.mycompany.com</host>
<port>8080</port>
<username>your-username</username>
<password>your-password</password>
</proxy>
</proxies>
</settings>
内部代理
<settings>
<mirrors>
<mirror>
<id>maven.mycompany.com</id>
<name>My Company's Maven Proxy</name>
<url>http://maven.mycompany.com/maven2</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
你可以在这里找到settings.xml的详细配置http://maven.apache.org/ref/2.0.8/maven-settings/settings.html
验证安装成功
mvn --version
查看版本号,如果成功你可以进入下一步
2.2 创建第一个maven项目
创建第一个项目需要用到Maven的原型(Archetype)机制。Archetype定义了一个统一的模型,是一个产出完整功能Maven项目的模 板。详情请见http://maven.apache.org/guides/introduction/introduction-to- archetypes.html
快速创建Maven项目
mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-app
执行后你会发现在当前目录下创建了my-app文件夹,此文件夹中包括了pom.xml,内容如下
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
你会发现pom.xml在项目的顶层目录下,当你发现一个目录中有pom.xml,你就应该知道你在处理一个Maven项目,该项目按照习惯(Convention)结构生成的目录结构如下
my-app
----src
--------main
------------java
----------------com
--------------------mycompany
------------------------app
----------------------------App.java
--------test
------------java
----------------com
--------------------mycompany
------------------------app
----------------------------AppTest.java
----pom.xml
2.3 编译源程序
进入<my-app>目录,运行命令
mvn compile
下面来分析一下maven执行上述命令的过程,Maven是怎样查找源代码来编译他们,如何知道把它们编译到哪里?这就是Maven 的"convention over configuration"原则的作用。默认情况下,应用程序源文件放在src/main/java下,默认值继承于Super POM。这就是说如果你使用默认位置的话,你无需告诉POM你的源代码存放位置,当然在极少数情况下你也可以指定特定的位置,默认的编译类输出路径位于 target/classes。
Maven是怎样编译源程序的?这就是Maven的第二个原则"reusable build logic"起得作用。根据默认配置,标准的编译插件会编译你的应用程序源文件。被封装于编译插件中相同的构建逻辑会在任何其他项目中一贯的执行。
Maven是怎样将编译插件和后台进程关联并调用的呢?其实Maven的确有这种形式的映射,叫做默认构建生命周期(default build of life cycle)
Maven是怎样获取插件的呢?在安装完后你找不到编译所需的插件,Maven会从远程库中自动下载所需要的插件。当你第一次执行命令时Maven会从远程下载插件,以后再执行时则不会下载已有插件。
通过使用Maven的习惯配置你可以花很少力气做很多事。
2.4 编译测试源代码和执行单元测试
接下来你要测试你的源程序,这就表示你需要执行在生命周期中所有先于test阶段的步骤,输入如下命令
mvn test
该命令执行动作
下载测试插件
编译源代码,执行测试
如果你仅仅想编译你的测试类可以输入如下命令
mvn test-compile
这个不是必须的因为mvn test总会调用comile,test-compile以及先于test的所有步骤
执行测试类的规则
默认情况下
包括
**/*Test.java
**/Test*.java
**/*TestCase.java
排除
**/Abstract*Test.java
**/Abstract*TestCase.java
2.5 打包和安装到你的本地库
下一个逻辑步骤是打包,命令如下
mvn package
执行完毕后,你会target下看到my-app-1.0-SNAPSHOT.jar包
执行命令安装到本地库供其他项目使用
mvn install
执行完毕后,你会在本地库中默认路径<user_home>/.m2/repository/com/mycompany/app/my-app下看到你的项目和jar包
上述的构建,测试,打包,安装是Maven的主要任务,你也可以调用其他任务。其中一个非常有用的是为你的项目生成网站,命令如下
mvn site
其他命令
在构建之前清理target目录
mvn clean
生成IntelliJ IDEA描述
mvn idea:idea
生成eclipse描述
mvn eclipse:eclipse
2.6 处理classpath资源
另一个频繁使用的情况是打包资源文件到JAR文件中。对于这种任务,Maven再一次使用标准目录结构。意味着只要采用Maven的标准习惯,你就可以将资源打包到JARs,而你所要作的仅仅是把资源放到标准目录结构中。
Maven的规则是src/main/resources目录中的所有资源都会以相同的名字打包到JAR的根目录下
练习:
在你的src/main/resources下新建META-INF/application.properties然后运行mvn install,解压目的输出jar包就能发现META-INF下包括了这个文件,并且META-INF下还包括了 maven/com.mycompany.app/my-app/{pom.properties,pom.xml},这使得Maven生成的jar包有 自描述的能力,其他项目可能会需要该产品的信息例如版本号,通过POM(pom.xml)获取,这需要Maven支持,或者通过属性文件 (application.properties)获取,这个只需java APIs。
2.6.1 处理测试路径下的classpath资源
要在单元测试中加入资源,你需要把资源放到src/test/resources目录下
当你的测试程序中需要访问资源时的示例代码片段如下
[...]
// Retrieve resource test.properties是放到src/test/resources下的资源
InputStream is = getClass().getResourceAsStream( "/test.properties" );
// Do something with the resource
[...]
如果需要覆盖你自己的manifest文件,你可以使用如下配置maven-jarplugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
2.6.2 过滤classpath资源
有时可能需要根据属性过滤资源——动态产生所需要的资源中的某些值,要达到这个目的maven中使用${<property name}标记,一个属性可以是定义在pom.xml(项目级别)中,定义在settings.xml(本机级别)中或者外部属性文件 (properties文件)或者系统属性(java -D后的属性或者java自带的runtime属性),要使用过滤必须设置build元素中resources下的resource中的 filtering为true,配置文件如下
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</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>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
引用pom.xml里的属性
只要引用标签名就可以了如${poject.name}引用了pom的project的项目显示名,${project.version}引用了版本名,${project.build.finalName}引用了最终打包生成的文件名
练习:
创建文件src/main/resources/META-INF/
application.properties
输入
# application.properties
application.name=${project.name}
application.version=${project.version}
application.build.finalName=${project.build.finalName}
执行mvn mvn process-resources,该命令指运行资源复制和筛选的构建生命周期
执行完毕后target/classes/application.properties这个文件中的值变为
# application.properties
application.name=my-app
application.version=1.0-SNAPSHOT
application.build.finalName=my-app-1.0-SNAPSHOT
引用外部文件的属性
练习:
创建src/main/filters/filter.properties
把外部属性文件的引用加入pom文件中
<build>
<filters>
<filter>src/main/filters/filter.properties</filter>
</filters>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
加入属性到刚才的src/main/resources/META-INF/
application.properties文件中
# application.properties
application.name=${project.name}
application.version=${project.version}
application.build.finalName=${project.build.finalName}
message=${my.filter.value}
执行mvn mvn process-resources,结果如下
# application.properties
application.name=my-app
application.version=1.0-SNAPSHOT
application.build.finalName=my-app-1.0-SNAPSHOT
message=hello!
也可以把属性直接定义在pom中,效果相同,如下最后properties元素
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Quick Start Archetype</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>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
< filtering>true</filtering>
</resource>
</resources>
</build>
<properties>
<my.filter.value>hello</my.filter.value>
</properties>
</project>
引用系统属性
如mvn process-resources "-Dcommand.line.prop=hello again"
配置方式和上面例子类似
2.6.3 防止过滤二进制文件
当需要防止过滤某些文件时,你需要定义一个防止过滤的入口和一个资源入口
例如你要防止过滤src/main/resources/images下的文件,配置如下
<project>
[...]
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>images/**</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>images/**</include>
</includes>
</resource>
</resources>
</build>
[...]
</project>
2.7 使用maven插件
如前所述如需要自定义构建Maven项目,你需要包括额外的插件或者配置已存在的插件参数。
例如,你可能像配置java编译器jdk的版本,配置文件如下
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
你可能已经注意到plugin的配置和dependency的配置很相似。如果该插件没有出现在你的本地系统中他会以dependency同样的方式去远 程库下载并自动安装。为了说明两者的相似性,上述配置显示了groupId和version元素,但是大多数情况下这不是必须的。
默认情况下如果你没有定义groupId,Maven将会以org.apache.maven.plugins或org.codehaus.mojo为groupId去寻找,你也可以定义了一个额外的groupId在POM或setting.xml中。
如果你没有定义版本,Maven会假设下载最新的插件版本。虽然插件一般向下兼容,但是为了兼容性,你也可以定义版本号。
如果你想查看插件的属性选项,可以使用命令mvn help:describe
例如:你想查看maven-compiler-plunin的选项输入如下命令
mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-compiler-plugin -Dfull=true
也可以通过插件参考手册查看详情网址:http://maven.apache.org/plugins/
第三章 用Maven创建应用程序
3.1 介绍
现在你将钻研的更深一些,在这部分你将学习通过一个管理FAQ应用程序的真实例子学习Maven的最佳实践和高级应用。这个应用程序名字叫Proficio,该名字来自于拉丁语帮助的意思。
3.2 建立应用程序目录结构
Proficio的模块组成
Proficio API
Proficio的应用程序接口
Proficio CLI
提供命令行接口
Proficio Core
接口实现
Proficio Model
Proficio的数据模型
Proficio Stores
存放存储模块,Proficio有一个简单的基于内存(memory-based)的仓库和一个基于流(XStream-based)的仓库
查看Proficio的顶层POM时,你会在modules元素下看到所有的子模块构成了Proficio应用程序。一个子模块是另一个Maven项目的引用。如下
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven Proficio</name>
<url>http://maven.apache.org</url>
[...]
<modules>
<module>proficio-model</module>
<module>proficio-api</module>
<module>proficio-core</module>
<module>proficio-stores</module>
<module>proficio-cli</module>
</modules>
[...]
</project>
上述pom中的一个特征就是版本属性为1.0-SNAPSHOT。对于一个应用程序来说,通常会一起发布许多子模块,这对于所有模块都有一个公共版本来说是很有意义的。建议你的应用程序的所有子模块都用顶层POM的版本号。
你应该注意packaging元素,在这个例子中它的值为pom。对于包括子模块的POMs来说,packaging类型必须被设置成pom,这告诉Maven你打算构建一个多模块的结构。目录结构如下
proficio
----proficio-api
----proficio-cli
----proficio-core
----proficio-model
----proficio-stores
--------proficio-store-memory
--------proficio-store-xstream
--------pom.xml
----pom.xml
各个模块的打包类型
Module Packaging
proficio-api jar
proficio-cli jar
proficio-core jar
proficio-model jar
proficio-stores pom
在大多数时候打包类型为默认的jar,但是proficio-stores这个模块类型为pom,下面是这个模块的POM
<project>
<parent>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>proficio-stores</artifactId>
<name>Maven Proficio Stores</name>
<packaging>pom</packaging>
<modules>
<module>proficio-store-memory</module>
<module>proficio-store-xstream</module>
</modules>
</project>
当你看到packaging类型为pom时,意味着该项目/模块有多个子模块构成
3.3 使用项目继承
项目继承是Maven的一项重要特征之一。使用项目继承允许你在同一个位置声明你的组织信息,部署信息或者你的通用依赖性。你可能会注意到每个子POMs的头部会像下面这样
[...]
<parent>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
[...]
这是一个pom片段,让你利用顶层POM的资源,定义了从哪个项目继承。让我们来看一个例子。
如果你观察一下Proficio的顶层POM,你会发现在依赖部分有一个JUnit3.8.1的声明。在这个例子中假设Junit将被用来测试所有子项目。所以,通过继承顶层POM的依赖,在你的任何子POMs中你将不需要再次声明这个依赖。这个依赖定义如下:
<project>
[...]
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
[...]
</project>
让我们来看看每个子POM发生了什么,每个子POM都继承了顶层POM的依赖部分。所以如果你看一下proficio-core模块的POM你会发现如下(注意:没有JUnit的依赖声明)
<project>
<parent>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>proficio-core</artifactId>
<packaging>jar</packaging>
<name>Maven Proficio Core</name>
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
</dependency>
</dependencies>
</project>
为了让你看见继承的过程,你需要使用快捷工具mvn help:effective-pom命令。这个命令将让你 看到最终目标POM的结果。进入proficio-core模块目录运行上述命令,你将会看到JUnit的依赖。
<project>
[...]
<dependencies>
[...]
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
[...]
</dependencies>
[...]
</project>
你会发现用mvn help:effective-pom命令输出的pom比预想的大很多,因为这个命令输出了合并后的整个层次结构的pom,当你遇到问题时这个命令是非常有帮助的。
3.4 管理依赖
当你构建应用程序时,有许多依赖需要管理而且随着时间的增长依赖也不断的增长,使依赖管理变得复杂。Maven管理依赖的策略是通过把项目的继承机制和在POM中定义的依赖管理元素相结合来处理这个问题。
当你写的应用程序由许多独立的项目组成,很可能其中一些项目共享相同的依赖。当这个发生时,要保证所有的相同版本的依赖被你的所有项目使用,这样才能保证 你的应用程序正常工作。为了在跨多个项目管理你的依赖,你使用顶层POM中的依赖管理部分来管理依赖。让我们来看一下如下部分
<project>
[...]
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-store-memory</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-store-xstream</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
</dependency>
</dependencies>
</dependencyManagement>
[...]
</project>
注意:${project.version}是指顶层POM的版本元素,也就是应用程序版本
就像上面的依赖管理部分看到的,我们有几个Proficio依赖和一个Plexus依赖注入容器的依赖。在包括依赖元素的 dependencyManagement元素和在POM顶层dependencies依赖元素有一个重要的区 别:dependencyManagement只是用来定义引用的版本号,他自己不会作用于依赖层次图,而顶层dependencies元素会影响层次 图。如果你观察一下proficio-api模块里的POM,你会发现依赖声明部分如下:
<project>
[...]
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
</dependency>
</dependencies>
</project>
这个依赖的版本来源于顶层POM的dependencyManagement元素。只有在子项目的依赖没有声明版本时定义在祖先项目中的dependencyManagement依赖才会生效。
3.5 使用快照(Snapshots)
当你开发拥有多个模块的应用程序时,在某些情况下每个模块更新都很频繁,需要不断的获取最新子模块(原文:it is usually the case that each of the modules are in flux)。你的APIs会改变,你的实现会改变和更新,你可能还会重构。你的构建系统需要以简单的方式来实时更新(获取最新子模块以构建项目),这就是 Maven快照出现的原因。Maven中快照是用来获得最新源代码生成的产品。如果你看一下顶层Proficio的POM,你会发现快照版本定义如下:
<project>
[...]
<version>1.0-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-model</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.devzuz.mvnbook.proficio</groupId>
<artifactId>proficio-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
</dependency>
</dependencies>
</dep