Maven基础概述-下篇
约定优于配置:maven的配置文件看似很复杂,其实只需要根据项目的实际背景,设置个别的几个配置项而已。maven有自己的一套默认配置,使用者除非必要,并不需要去修改那些约定内容。这就是所谓的“约定优于配置”。
好处:采用"约定优于配置"的策略可以减少修改配置的工作量,也可以降低学习成本,更重要的是,给项目引入了统一的规范。
版本规范:maven有自己的版本规范,一般是如下定义:
<majorversion>.<minor version>.<incremental version>-<qualifier>,
比如1.2.3-beta-01。要说明的是,maven自己判断版本的算法是major,minor,incremental部分用数字比较,qualifier部分用字符串比较,所以要小心 alpha-2和alpha-15的比较关系,最好用 alpha-02的格式。
maven在版本管理时候可以使用几个特殊的字符串 SNAPSHOT ,LATEST ,RELEASE 。比如"1.0-SNAPSHOT"。各个部分的含义和处理逻辑如下说明:
(1)SNAPSHOT:如果一个版本包含字符串"SNAPSHOT",Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC时间。例如,"1.0-SNAPSHOT"会在2010年5月5日下午2点10分发布时候变成1.0-20100505-141000-1。
这个词只能用于开发过程中,因为一般来说,项目组都会频繁发布一些版本,最后实际发布的时候,会在这些snapshot版本中寻找一个稳定的,用于正式发布,比如1.4版本发布之前,就会有一系列的1.4-SNAPSHOT,而实际发布的1.4,也是从中拿出来的一个稳定版。
(2)LATEST:指某个特定构件的最新发布,这个发布可能是一个发布版,也可能是一个snapshot版,具体看哪个时间最后。
(3)RELEASE:指最后一个发布版,是从快照版本中 挑出的一个稳定的版本,作为发布版。
Maven变量:除了在setting.xml以及pom.xml当中用properties定义的常量,maven还提供了一些隐式的变量,用来访问系统环境变量。
(1)内置属性: ${basedir}表示项目根目录,即包含pom.xml文件的目录
${version}表示项目版本
${project.basedir}同${basedir}
${project.baseUri}表示项目文件地址
${maven.build.timestamp}表示项目构件开始时间
(2)Setting属性:${settings.localRepository }表示本地仓库路径
(3)POM属性: ${project.build.directory}表示主源码路径
${project.build.sourceEncoding}表示主源码的编码格式
${project.build.sourceDirectory}表示主源码路径
${project.build.finalName}表示输出文件名称
${project.version}表示项目版本,与${version}相同
(4)JAVA系统属性:${user.home}表示用户目录
${java.version}表示Java版本
(5)环境变量属性:${env.JAVA_HOME}表示JAVA_HOME环境变量的值
${env.HOME }表示用户目录
(6)商机工程变量:上级工程的pom中的变量用前缀 ${project.parent } 引用。上级工程的版本也可以这样引用: ${parent.version }
依赖关系:在maven的管理体系中,各个项目组成了一个复杂的关系网,但是每个项目都是平等的,是个没有贵贱高低,众生平等的世界,全球每个项目从理论上来说都可以相互依赖。就是说,你跟开发Spring的大牛们平起平坐,你的项目可以依赖Spring项目,Spring项目也可以依赖你的项目(虽然现实中不太会发生,你倒贴钱人家也不敢引用)。
项目的依赖关系主要分为三种:依赖,继承,聚合。
(1)依赖关系:依赖关系是最常用的一种,就是你的项目需要依赖其他项目,比如Apache-common包,Spring包等等。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
<type >jar</ type >
<optional >true</ optional >
</dependency>
注:任意一个外部依赖说明包含如下几个要素:groupId, artifactId, version, scope, type, optional。其中前3个是必须的。
这里的version可以用区间表达式来表示,比如(2.0,)表示>2.0,[2.0,3.0)表示2.0<=ver<3.0;多个条件之间用逗号分隔,比如[1,3],[5,7]。
type 一般在pom引用依赖时候出现,其他时候不用。
maven认为,程序对外部的依赖会随着程序的所处阶段和应用场景而变化,所以maven中的依赖关系有作用域(scope)的限制。
(2)继承关系:继承就是避免重复,maven的继承也是这样,它还有一个好处就是让项目更加安全。项目之间存在上下级关系时就属于继承关系。
父项目的配置如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.clf.parent</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<packaging>pom</packaging>
<!-- 该节点下的依赖会被子项目自动全部继承 -->
<dependencies>
<dependency>
……
</dependency>
</dependencies>
<dependencyManagement>
<!-- 该节点下的依赖关系只是为了统一版本号,不会被子项目自动继承,-->
<!--除非子项目主动引用,好处是子项目可以不用写版本号 -->
<dependencies>
<dependency>
……
</dependency>
</dependencies>
</dependencyManagement>
<!-- 这个元素和dependencyManagement相类似,它是用来进行插件管理的-->
<pluginManagement>
……
</pluginManagement>
</project>
注意:为了项目的正确运行,必须让所有的子项目使用依赖项的统一版本,必须确保应用的各个项目的依赖项和版本一致,才能保证测试的和发布是相同的结果。
Maven 使用dependencyManagement 元素来提供了一种管理依赖版本号的方式。通常会在一个组织或者项目的最顶层的父POM 中看到dependencyManagement 元素。使用pom.xml 中的dependencyManagement 元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。Maven 会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement 元素中指定的版本号。
父项目在dependencies声明的依赖,子项目会从全部自动地继承。而父项目在dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
如果某个项目需要继承该父项目,基础配置应该这样:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.clf.parent.son</groupId>
<artifactId>my-son</artifactId>
<version>1.0</version>
<!-- 声明将父项目的坐标 -->
<parent>
<groupId>org.clf.parent</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<!-- 父项目的pom.xml文件的相对路径。相对路径允许你选择一个不同的路径。 -->
<!-- 默认值是../pom.xml。Maven首先在构建当前项目的地方寻找父项目的pom, -->
<!-- 其次在文件系统的这个位置(relativePath位置), -->
<!-- 然后在本地仓库,最后在远程仓库寻找父项目的pom。 -->
<relativePath>../parent-project/pom.xml</relativePath>
</parent>
<!-- 声明父项目dependencyManagement的依赖,不用写版本号 -->
<dependencies>
<dependency>
</dependency>
</dependencies>
</project>
(3)聚合关系:随着技术的飞速发展和各类用户对软件的要求越来越高,软件本身也变得越来越复杂,然后软件设计人员开始采用各种方式进行开发,于是就有了我们的分层架构、分模块开发,来提高代码的清晰和重用。针对于这一特性,maven也给予了相应的配置。
maven的多模块管理也是非常强大的。一般来说,maven要求同一个工程的所有模块都放置到同一个目录下,每一个子目录代表一个模块,比如:
总项目/
pom.xml 总项目的pom配置文件
子模块1/
pom.xml 子模块1的pom文件
子模块2/
pom.xml子模块2的pom文件
总项目的配置如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.clf.parent</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<!-- 打包类型必须为pom -->
<packaging>pom</packaging>
<!-- 声明了该项目的直接子模块 -->
<modules>
<!-- 这里配置的不是artifactId,而是这个模块的目录名称-->
<module>module-1</module>
<module>module-2</module>
<module>module-3</module>
</modules>
<!-- 聚合也属于父子关系,总项目中的dependencies与dependencyManagement、pluginManagement用法与继承关系类似 -->
<dependencies>
......
</dependencies>
<dependencyManagement>
......
</dependencyManagement>
<pluginManagement>
......
</pluginManagement>
</project>
子模块的配置如下:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.clf.parent.son</groupId>
<artifactId>my-son</artifactId>
<version>1.0</version>
<!-- 声明将父项目的坐标 -->
<parent>
<groupId>org.clf.parent</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
</parent>
</project>
(4)继承与聚合的关系:首先,继承与聚合都属于父子关系,并且,聚合 POM与继承关系中的父POM的packaging都是pom。
不同的是,对于聚合模块来说,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在。对于继承关系的父 POM来说,它不知道有哪些子模块继承与它,但那些子模块都必须知道自己的父 POM是什么。
在实际项目中,一个 POM往往既是聚合POM,又是父 POM,它继承了某个项目,本身包含几个子模块,同时肯定会存在普通的依赖关系,就是说,依赖、继承、聚合这三种关系是并存的。