Maven 对象模型

Maven中定义了一个单独的远程Maven仓库,ID为central,这是所有Maven客户端默认配置访问的中央Maven仓库,可以通过settings.xml文件去覆盖,这个超级POM关闭了从

中央Maven仓库下载snapshot构件的功能,如果要使用snapshot仓库,就要在pom.xml或者settings.xml中自定义仓库位置。

中央仓库包含Maven插件,默认的插件仓库就是这个中央仓库,Snapshot被关闭,而且更新策略设置成了"从不",这意味着Maven将永远不会自动更新一个插件。即使版本更新了

build元素设置Maven标准目录布局中那些目录的默认值,在超级POM为核心插件提供了默认版本。


当前编写的一个POM,需要考虑超级POM,再加上一个或多个父POM覆盖默认配置,最后使用当前项目的POM来覆盖之前生成的配置结果,最后得到一个混合了各个POM配置

的有效POM,想要查看项目的有效POM,你需要运行Maven Help插件的effective-pom目标,运行mvn help:effective-pom


项目版本

一个Maven项目发布版本号用version编码,用来分组和排序发布,Maven中的版本包含了以下部分:主板本,次版本,增量版本,和限定版本号。

格式:<major version>.<minor version>.<incremental version>-<qualifier>


snapshot的Maven仓库使用:例如你的项目依赖一个正在处于开发过程中的项目,你可以依赖于一个SNAPSHOT版本,在你运行构建的时候Maven会定期的从仓库下载最新的

snapshot,作为默认设置,Maven不会从远程仓库检查SNAPSHOT版本,要依赖于SNAPSHOT版本,用户必须在POM中使用repository和pluginRepository元素显示的开启下

载snapshot的功能,当发布一个项目的时候,你需要解析所有对SNAPSHOT版本的依赖至正式发布的版本,SNAPSHOT版本只用于开发过程。


LATEST 和 RELEASE 版本

LATEST是指某个特定构件最新的发布版或者快照版(snapshot),RELEASE是指仓库中最后的一个非快照版本,总得来说,设计软件去依赖于一个构件的不明确的版本,并不

是一个好的实践。如果处于软件开发过程中,你可能想要使用RELEASE或者LATEST,这样做十分方便,你不用为每次一个第三方类库新版本的发布而去更新你配置的版号,

但是当发布软件的时候,应该确定你的项目依赖于某个特定的版本,以减少构建的不确定性,免得被其他不受你控制的软件版本影响。慎用LATEST和RELEASE。


Maven 2.0.9之后,Maven在超级POM中锁住了一些通用及核心Maven插件的版本号,为Maven构建带来了稳定性和重现性。在Maven 2.0.9之前,Maven会自动将核心插件更新

至LATEST版本,这样会导致一些BUG,我们就不能保证构建的重现性,因为插件随时都可能从中央仓库更新至一个新的版本。


属性引用

一个POM可以通过一对大括弧和前面一个美元符号来包含 对属性的引用,Maven提供了三个隐式的变量,可以用来访问环境变量,POM信息,和Maven Settings

1.env变量 暴露了你操作系统或者shell的环境变量

2.project变量暴露了POM。你可以使用点标记(.)的路径来引用POM元素的值,例如:在本节中我们使用过groupId和artifactId来设置构建配置中的finalName元素。这个属性

引用的语法是:org.sonatype.mavenbook-${project.artifactId}。

3.settings变量暴露了Maven settings信息。可以使用点标记(.)的路径来引用settings.xml文件中元素的值。例如,${settings.offline}会引用~/.m2/settings.xml文件中offline

元素的值。

(注意:你可能在老的构建中看到使用${pom.xxx}或者仅仅${xxx}来引用POM属性。这些方法已被弃用,我们只应该使用${project.xxx})

除了这三个隐式的变量,你还可以引用系统属性,以及任何在Maven POM中和构建profile中自定义的属性组。

项目依赖:简要介绍了三种依赖范围:compile,test,和provided。范围控制哪些依赖在哪些classpath中可用,哪些依赖包含在一个应用中

compile(编译范围),依赖的范围就是编译范围。

provided(已提供范围),依赖的范围是测试和编译。运行时不需要将Servlet.jar进行打包,因为你的应用服务器或者Servlet容器提供。

runtime(运行时范围),依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱

动实现。

test(测试范围),test范围依赖 在一般的 编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。

system(系统范围),system范围依赖与provided类似

可选依赖:编译这个项目的时候你需要两个依赖类库,但是你不希望在使用你类库的项目中,这两个依赖类库同时作为传递性运行时依赖出现,即使用可选依赖来完成这个任务

依赖版本界限:你可以使用如下的字符来围绕一个或多个版本号,来实现版本界限,(, )不包含量词,[, ]包含量词,例如<version>[3.8,4.0)</version>

传递性依赖:例如你只要依赖于一些包如Spring Framework,而不用担心Spring Framework的所有依赖,Maven帮你自动管理了,你不用自己去详细了解配置。

但在一些边界情况中,传递性依赖会造成一些配置问题。在这种情况下,你可以使用依赖排除。

传递性依赖和范围:指的是通过传递性依赖所引入的jar包所使用的范围,

冲突解决:排除一个传递性依赖:

<dependency>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>project-a</artifactId>
<version>1.0</version>

<exclusions>
   <exclusion>
     <groupId>org.sonatype.mavenbook</groupId>
     <artifactId>project-b</artifactId>
   </exclusion>
</exclusions>

</dependency>

排除并替换一个传递性依赖的操作:

<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jta_1.1_spec</artifactId>
<version>1.1</version>
</dependency>
</dependencies>

以下列出一些你可能想要排除或者替换传递性依赖的情况:

1. 构建的groupId和artifactId已经更改了,而当前的项目需要一个与传递性依赖不同名称的版本,

2. 某个构件没有在你的项目中被使用,而且该传递性依赖没有被标志为可选依赖。在这种情况下,你可能想要排除这种依赖,因为它不是你的系统需要的东西,你

要尽量减少应用程序分发时的类库数目。

3. 一个构件已经在运行时的容器中提供了,因此不应该被包含在你的构件中。如果一个依赖依赖于如Servlet API的东西,并且你又要确保这样的依赖没有包含在web应用的

WEB-INF/lib目录中。

依赖管理:当你采用Maven之后,你有了两百多个相互关联的Maven项目,例如每一个使用Mysql数据库驱动依赖的项目都需要独立的列出该依赖的版本,在你需要升级到一个

新版本的时候你就会遇到问题,由于这些版本号分散在你的项目中,需要手动的修改每一个pom.xml,确保每个地方的版本号都更改了。幸运的是,Maven在

dependencyManagement元素中为你提供了一种方式来统一依赖版本号,你通常会在一个组织或者项目的最顶层的父POM中看到dependencyManagement元素,使用

使用pom.xml中的dependencyManagement元素能让你在子项目中引用一个依赖而不用显式的列出版本号。Maven会沿着父子层次向上走,知道找到一个拥有

dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。

<dependencyManagement>

<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.2</version>
</dependency>
...
<dependencies>

</dependencyManagement>

子项目中的引用实例:

<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

注意:只有在子项目没有直接声明一个版本的时候,dependencyManagement定义的版本才会被使用。

注意我们称多模块项目下的项目为“模块”而不是“子项目”。这是有目的的,是为了而不将由多模块项目归类的项目与那些从其它项目继承POM信息的项目混淆。

项目继承:所有的Maven POM从父POM中继承值。如果一个POM没有通过parent元素指定一个直接的父项目,那这个POM就会从超级POM继承值。

当一个项目指定一个父项目的时候,Maven在读取当前项目的POM之前,会使用这个父POM作为起始点。它继承所有东西,包括groupId和version。

你会注意到projecta没有指定groupId和version,它们从a-parent继承而来,有了parent元素,一个POM就只需要定义一个artifactId。但这不是强制的,project-a可以有一个不

同的groupId和version,但如果不提供值,Maven就会使用在父POM中指定的值。如果你开始使用Maven来管理和构建大型的多模块项目,你就会常常创建许多共享一组通用

的groupId和version的项目。

你可以使用Maven的这一特征来指定一些在所有项目被广泛使用的依赖,让它们从顶层POM中继承。例如,如果你的系统全局使用Log4J日志框架,你可以在你的顶层POM中列

出该依赖。任何从该项目继承POM信息的项目会自动拥有Log4J依赖。类似的,如果你能确定每个项目都在使用同样版本的一个Maven插件,你可以在顶层父POM的

pluginManagement元素中显式的列出该Maven插件的版本。

Maven假设父POM在本地仓库中可用,或者在当前项目的父目录(../pom.xml) 中可用。如果两个位置都不可用,默认行为还可以通过relativePath元素被覆盖。

<project>
<parent>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>a-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../a-parent/pom.xml</relativePath>
</parent>
<artifactId>project-a</artifactId>
</project>

依赖归类:你可以创建一个特殊的POM,它除了声明一组通用依赖之外什么也不做,这样就不需要在每个使用Hibernate,Spring和MySQL的项目中包含所有这些依赖。你可以

创建一个项目叫做persistence-deps(持久化依赖的简称),然后让每个需要持久化的项目依赖于这个提供便利的项目。而这个项目只是创建该pom.xml并且运行mvn intall

由于打包类型是pom,这个POM被安装到你的本地仓库,你现在就可以添加这个项目作为一个依赖,所有该项目的依赖就会被添加到你的项目中,

当我们声明一个对于persistence-deps项目的依赖的时候,不要忘了指定依赖类型为pom。

<project>
<description>This is a project requiring JDBC</description>
...
<dependencies>
...
<dependency>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<type>pom</type>
</dependency>
</dependencies>
</project>

多模块 vs. 继承:一个父项目是指它把所有的值传给它的子项目,一个多模块项目只是说它管理一组子模块,或者说一组子项目。当建立一个多模块项目的时候,你告诉一个项

目它的构建需要包含指定的模块。多模块构建用来将模块聚集到一个单独的构建中。父子关系是从叶节点往上定义的,当你给一个子项目关联一个父项目的时候,你告诉Maven

该项目的POM起源于另一个项目。多模块只是为了方便聚集模块构建,其中某个子模块完全不知道他是父模块的一部分,也完全不关心父模块,只是为了方便聚集模块构建

继承:表示多个子项目都继承同一个共享的“团体”父项目--> sonatype,这是一个采用Maven的组织中常见的实践,一个组织定义一个顶层的团体POM,作为一个默认的父项目

为其他项目服务,而不是让每个项目默认去扩展超级POM,”团体“ POM能让组织有机会自定义一些Maven的默认行为,提供一些组织特定的信息,如配置部署设置和构建profile

模块都是子模块是为了方便,要构建整个系统,只要到父模块项目目录下运行mvn package,当两个子项目的分歧出现时,你就要费脑子将构建抽离到父项目中,另一方面又

需要不影响其他的子项目,即使这两个子项目同时依赖于LOG4J,他们也可能拥有截然不同的插件配置,设计一组继承了五六层POM的第三方项目永远都不是一个好主意,

这样的配置下,你可能不再需要在多个地方重复LOG4J依赖,但你会需要查看这5,6个POM来弄清Maven如何计算出你的有效POM。


为特定的项目使用父项目作为“原型”






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值