基本工具之Maven

本篇介绍Maven,内容皆总结摘抄自《Maven实战》,仅用作笔记。

Maven简介

Maven是Apache组织中的一个颇为成功的开源项目,主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。

Maven是一个异常强大的构建工具,能够帮我们自动化构建过程,从清理、编译、测试到生成报告,再到打包和部署。只需要使用Maven配置好项目,然后输入命令即可。

Maven通过一个座标系统准确的定位每一个构件(artifact),也就是说通过一组座标Maven能够找到任何一个Java类库。

Maven还能帮助我们管理原本分散在项目中各个角落的项目信息,包括项目描述、开发者列表、版本控制系统地址等。

Maven还为全世界的Java开发者提供了一个免费的中央仓库,在其中几乎可以找到任何的流行开源类库。

安装和配置

下载Maven

到Maven官网http://maven.apache.org/download.cgi选择下载后缀为bin.zip的文件,然后解压。如下图:

修改本地仓库路径

进入conf目录,打开settings.xml,找到localRepository元素修改为要设置为本地仓库的目录,例如:

设置阿里云镜像

Maven默认访问中央仓库,而中央仓库地址在国外,因此在下载构件时速度很慢。设置阿里云镜像可以有效解决这一问题。

进入conf目录,打开settings.xml,在mirrors元素中添加以下mirror标签:

 <mirror>  
     <id>alimaven</id>  
     <name>aliyun maven</name>  
     <url>http://maven.aliyun.com/nexus/content/groups/public/</url>  
     <mirrorOf>central</mirrorOf>          
 </mirror> 

设置环境变量

在桌面上右击“我的电脑”->“属性”,单击”高级设置“,再选择“环境变量”。在系统变量中添加一个变量,变量名为MAVEN_HOME,变量值为Maven的解压目录。如下图:

然后在系统变量中找到名为Path的变量,在变量值的末尾添加“%MAVEN_HOME%\bin;”,如下图:

配置好环境变量后,打开cmd窗口,输入“mvn -v”检查Maven是否安装成功。如下图是安装成功的。

安装目录分析

解压之后的Maven目录包含如下结构和内容:

  • bin:包含了mvn运行的脚本,这些脚本用来配置Java命令,准备好classpath和相关的Java系统属性,然后执行Java命令。
  • boot:包含了plexus-classworlds-2.6.0.jar文件,plexus-classworlds是一个类加载器框架,相对于默认的java类加载器,它提供了更丰富的语法以方便配置,Maven使用该框架加载自己的类库。
  • conf:包含了一个非常重要的文件settings.xml。直接修改该文件,就能在机器上全局的定制Maven的行为。
  • lib:包含了所有Maven运行时需要的Java类库。此外,还包含一些Maven用到的第三方依赖。可以说,lib目录就是真正的Maven。

使用入门

Maven项目的核心是pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖等等。下面是一个最简单的pom.xml。

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>project</artifactId>
        <groupId>org.galiden</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>module</artifactId>
    <name>module</name>
</project>

代码的第一行是XML头,指定了该xml文档的版本和编码方式。紧接着是project元组,project是所有pom.xml的根元素,它还声明了一些POM相关的命名空间及xsd元素。

根元素下的modelVersion指定了当前POM模型的版本。

groupId、artifactId和version这三个元素定义了一个项目的基本座标。在Maven的世界,任何的jar、pom或者war都是基于这些基本的座标进行区分的。groupId定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联。artifactId定义了当前Maven项目在族中唯一的ID。version指定了该项目当前的版本---1.0-SNAPSHOT,SNAPSHOT意为快照,说明该项目还在开发中,是不稳定的版本。

最后一个name元素声明了一个对于用户更为友好的项目名称。

Maven项目中默认的主代码目录是src/main/java,对应的,默认的测试代码目录是src/test/java。

这篇文章中介绍了idea如何创建maven项目,我们就使用一个具有多个module的project做演示。其项目结构如下:

在项目根目录下运行命令“mvn clean compile”会得到以下输出:

clean告诉Maven清理输出目录target/,compile告诉Maben编译项目主代码。至此,Maven再没有任何额外的配置的情况下就执行了项目的清理和编译任务。

接下来调用Maven执行测试,在使用JUnit单元测试时,要添加JUnit依赖,修改项目的POM代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>project</artifactId>
        <groupId>org.galiden</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>module</artifactId>
    <name>module</name>
    <dependecies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependecies>
</project>

代码中添加了dependencies元素,该元素下可以包含多个dependency元素以声明项目的依赖。这里添加了一个依赖---groupId是junit,artifactId是junit,version是4.12。有了这段声明,Maven就会自动访问中央仓库,下载需要的文件。

上述POM代码中还有一个值为test的元素scope,scope为依赖范围,若依赖范围为test则表示该依赖只对测试有效。换句话说,测试代码中的import JUnit是没有问题的,但如果在主代码中用import JUnit代码,就会造成编译错误。如果不声明依赖范围,默认值是compile,表示该依赖对主代码和测试代码都有效。

在项目根目录执行命令“mvn clean test”。由下图可以看出任务执行成功了。

将项目进行编译、测试之后,下一个步骤就是打包。在项目根目录执行命令“mvn clean package”进行打包,可以看到如下输出:

类似的,Maven在打包之前执行编译、测试等操作。打包其实就是jar插件的jar目标将项目主代码打包成一个名为module-1.0-SNAPSHOT.jar的文件。该文件也位于target/输出目录中。

至此,我们得到了项目的输出,如果有需要的话,就可以复制这个jar文件到其他项目的Classpath中从而使用App类。但如果要让其他的Maven项目直接引用这个jar包,还需要一个安装步骤,执行命令“mvn clean install”安装jar包。

该任务将项目输出的jar安装到了Maven本地仓库中,只有将类的构件安装到本地仓库之后,其他Maven项目才能使用它。

使用Archetype生成项目骨架

在上面生成的Maven项目中有一些Maven的约定,例如在项目的根目录中放置pom.xml,在src/main/java目录中放置项目的主代码,在src/test/java中放置项目的测试代码。我们称这些基本的目录结构和pom.xml文件称为项目的骨架。

我们可以使用maven archetype来创建项目的骨架。事实上,上面例子中的project就是笔者在介绍idea时使用骨架创建的一个Maven项目。所以不需要手动创建pom.xml和src/main/java目录以及src/test/java目录结构。

座标和依赖

Maven的一大功能是管理项目依赖。为了能自动化的解析任何一个Java构件,Maven就必须它们唯一标识。这就依赖管理的底层基础---座标。

Maven的世界中拥有数量巨大的构件,也就是平时用的一些jar、war等文件。在Maven为这些构件引入座标概念之前,我们无法使用任何一种方式来唯一标识所有这些构件。Maven定义了这样一组规则:世界上任何一个构件都可以使用Maven座标唯一标识,Maven座标的元素包括groupId、artifactId、version、packaging、classifier。只要我们提供正确的座标元素,Maven就能找到对应的构件。

下面详细解释一下各个座标元素:

  • groupId:定义当前Maven项目隶属的实际项目。Maven项目和实际项目不一定是一对一的关系,例如SpringFramework这一实际项目对应的Maven项目会有spring-core、spring-context等。这是由于Maven中模块的概念,因此,一个实际项目往往会被划分为很多模块。其次,groupId不应该对应项目隶属的组织或公司,因为一个组织下会有很多实际项目,如果groupId只定义到组织级别,由于artifactId只能定义Maven项目,那么实际项目这个层将难以定义。
  • artifactId:定义实际项目中的一个Maven项目,推荐的做法是使用实际项目名称作为artifactId的前缀。
  • version:定义了Maven项目当前所处的版本。
  • packaging:定义Maven项目的打包方式。打包方式通常与所生成构件的文件扩展名对应,例如打包方式为jar包,最终的文件名的后缀为.jar。当不定义packaging的时候,Maven会使用默认值jar。
  • classifier:用来帮助定义构建输出的一些附属构件。

在上述5个元素中,groupId、artifactId和version是必须定义的,packaging是可选的,classifier是不可直接定义的。

根元素project下的dependencies可以包含一个或多个dependency元素,以声明一个或多个项目依赖。每个依赖可以包含的元素有:

  • groupId、artiafactId和version:依赖的基本座标,对于任何一个依赖来说,基本座标是最重要的,Maven根据座标才能找到需要的依赖。
  • type:依赖的类型,对应于项目座标定义的packaging,默认值为jar。
  • scope:依赖的范围。
  • optional:标记依赖是否可选。
  • exclusions:用来排除传递性依赖。

仓库

座标和依赖是任何一个构件在Maven世界中的逻辑表示方式;而构件的物理表示方式是文件,Maven通过仓库来统一管理这些文件。

在Maven世界中,任何一个依赖、插件或项目构建的输出,都可以称为构件。得益于座标机制,任何Maven项目使用任何一个构件的方式都是完全相同的。在此基础上,Maven可以在某个位置统一存储所有Maven项目共享的构件,这个统一的位置就是仓库。实际的Maven项目将不再各自存储其依赖文件,只需要声明这些依赖的座标,在需要的时候,Maven会自动根据座标找到仓库中的构件,并使用它们。

对于Maven来说,仓库只分为两类:本地仓库和远程仓库。当Maven根据座标寻找构件时,它首先会查看本地仓库,如果本地仓库存在此构件,则直接使用;否则,Maven就会去远程仓库查找,发现需要的构件之后,下载到本地仓库再使用。如果本地仓库和远程仓库都没有需要的构件,就会报错。

在这个最基本分类的基础上,还有一些特殊的远程仓库。中央仓库是Maven核心自带的远程仓库,它包含了绝大部分开源的构件。在默认配置下,当本地仓库没有Maven需要的构件时,就会尝试从中央仓库下载。

私服是另一种特殊的远程仓库,为了节省带宽和时间,应该在局域网内架设一个私有的仓库服务器,用其代理所有外部的远程仓库。

使用Maven进行日常开发时,一个常见的问题就是如何寻找需要的依赖,我们可能只知道需要使用类库的项目名称,但添加Maven依赖需要提供确切的Maven座标。这是可以使用仓库搜索服务来根据关键字得到Maven座标。

笔者使用的是一个叫做Maven Repository的仓库搜索服务,网址为https://mvnrepository.com/。例如搜索junit的座标,如下图:

选择第2个之后如下图:

我们可以根据使用次数来选择相对来说可能更“稳定”的那个版本,选择4.12版本后出现如下图:

将红框中的内容复制到pom.xml的dependencies元素中即可。

生命周期和插件

Maven的生命周期是为了对所有的构建过程进行抽象和统一,包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。也就是说,几乎所有项目的构建,都能映射到这样一个生命周期上。

Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际的工作,在Maven的设计中,实际的任务都交由插件来完成。生命周期抽象了构建的各个步骤,定义了它们的次序,但没有提供具体实现。因此Maven设计了插件机制,每个构建步骤都可以绑定一个或者多个插件行为,而且Maven为大多数构建步骤编写并绑定了默认插件。

聚合与继承

当Maven应用到实际项目中的时候,需要将项目分成不同的模块。Maven的聚合特性能够把项目的各个模块聚合在一起构建,而Maven的继承特性则能帮助抽取各模块相同的依赖和插件配置,在简化POM的同时,还能促进各个模块配置的一致性。

聚合

当一个实际项目拥有多个Maven项目时,我们会想要一次构建多个项目,而不是到多个模块的目录下执行mvn命令。Maven聚合特性就是为该需求服务的。

为了使用一条命令就能构建多个模块,我们需要创建一个额外的模块A,然后通过该模块构建整个项目的所有模块。A本身作为一个Maven项目,它必须要有自己的POM,但作为一个聚合项目,其POM又有特殊的地方。其第一个特殊的地方在packaging元素,值为pom。对于聚合模块来说,其打包方式packaging的值必须为pom,否则就无法构建。

实现聚合最核心的配置是module元素。用户可以通过在一个打包方式为pom的Maven项目中声明任意数量的module元素来实现模块的聚合。这里的每个module的值都是一个当前POM的相对目录。为方便用户构建项目,通常将聚合模块放在项目目录的最顶层,其他模块作为聚合模块的子目录存在。

继承

在多模块Maven项目中,他们的pom.xml可能会存在大量的重复,而重复往往就意味着更多的劳动和更多的潜在的问题。类似于面向对象设计中的父子结构,我们也可以创建POM的父子结构,然后在父POM中声明一些配置供子POM继承,以实现“一处声明,多处使用”的目的。

作为父模块的POM,其打包类型也必须为pom。另外,由于父模块只是为了帮助消除配置的重复,因此它本身不包含除POM之外的项目文件,也就不需要src/main/java之类的文件夹了。

有了父模块,就需要让其他模块来继承它。子模块中增加parent元素来声明父模块,parent下的子元素groupId、artifactId和version指定了父模块的座标,这三个元素是必须的。元素relativePath表示父模块POM的相对路径。在前面“使用入门”中举例的project就是一个父模块,而module是一个子模块。project的POM的部分内容如下:

<groupId>org.galiden</groupId>
<artifactId>project</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
    <module>module</module>
</modules>
<name>project</name>

可以看出其packaging的值为pom且包含一个子模块module。而子模块module的POM部分内容如下:

<parent>
    <artifactId>project</artifactId>
    <groupId>org.galiden</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>

声明了其父模块project的座标。

idea配置Maven

使用Ctrl + Alt + s或在上方菜单栏选择File->Settings打开Settings,找到Maven,如下图:

eclipse配置Maven

在上方菜单栏选择Window->Preference,找到Maven->Installations,如下图:

点击Add,选择要配置的Maven目录,如下图:

找到Maven->User Settings,如下图:

设置完成后本地仓库目录修改为之前在settings.xml文件中设置的目录:

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章