本文转自:http://www.tianmaying.com/tutorial/maven-basic
看这篇文章之前,你应该先Run起来一个简单的Maven项目,先有个感性认识,然后再来听听对这些基本概念的解析。如果你已经对Maven有了一些初步的了解,这里也会给你一些更清晰更高层的认识。跟着David来稍微理解一下Maven的几个核心概念吧。
Maven的哲学
Maven通常被认为是一个构建工具,与Ant类似。不过,Maven的创建者们可不这么认为,在Maven的哲学里,Maven是通过采用各种模式来创建的一个具有可视性、复用性、可维护性和完整性等特征的基础设施。
这么说有点太高大上了,接地气一点来说吧:Maven希望把软件开发中的一些最佳实践和模式都整合和固化下来,这样使用Maven来进行开发时,开发过程更爽,生产出来的软件更棒,具有以上所罗列的各种特性。
这几种特性对于一个团队一起高效的开发协作的确是非常重要的。Maven最初的诞生就是希望Apache的一些项目能够以相同的方式来开发和构建,这样一个开发者从一个项目转到另外一个项目工作的时候能够更加轻松地切换。因为项目的开发、测试、文档生成、报表和部署,都具有一些共同的特征,这些特征就可以认为是软件开发过程中的一些最佳实践,当这些最佳实践成为共识,开发的协作必然会更加高效。
那么Maven要做的就是把这些最佳实践固化成一个通用的项目管理方法。尽管对于不同的项目,开发中各个阶段会有所不同,但是确实可以找到一条普遍适用的路径,Maven将这条路径以非常清晰的方式结合各种实践模式提供给开发者。
至于模式,学术上一般简单定义为针对一种类型的特性问题的解决方案。小到一个项目的目录模式(比如:代码放到哪?测试代码放到哪?资源放到哪?),再大粒度一些比如项目的依赖如何管理,再大到整个项目构建的生命周期模式(比如:通用的构建过程包含哪些阶段?),都是Maven这个基础设施要提供给大家的,是Maven强制大家形成共同的认知。这样大家就能更快速地生产出棒棒哒的软件。
以上,便是Maven的哲学,学习过软件工程的人可能会比较有感觉,你要能跟别人侃出这一段,B格指数肯定暴涨。David是个学软件工程的人,侃这些比较在行,哈哈。如果你还有点云里雾里,没关系,接着往下看,看完在回过头来想想Maven的哪些特性印证了这里所说的哲学思想。
什么是构建?
Maven这个基础设施落地下来,最重要的还是一个构建工具(虽然Maven创建者们不屑和Ant这样的构建工具相提并论)。所以介绍Maven,必然还是需要先了解构建。
构建是什么呢?简单地说,构建就是软件项目生产的整个过程,比如这个过程应该包括:
- 文档和代码的生成(有些项目会使用代码自动生成工具,比如数据库访问代码的逆向工程)
- 代码的编译、测试和打包
- 打包好的代码进行分发或者部署
大家看看,项目的构建可绝不仅仅是编译软件这件事情。除了写代码,在项目层面做的大部分工作,都包含在构建的过程中。有了Maven,构建中的这些过程都能够进行良好的定义(模式、固化、共识,记住这些关键词哪),而且Maven能够帮我们串起来形成一个自动构建过程,这样比我们手动执行要高效得多。
Maven和Ant的区别
如果没有用过Ant,这一部分你可以Pass掉。Maven和Ant最大的区别就在于方法学的不同,一种是命令式方法,一种是声明式方法。
使用Ant我们需要指明各种执行的动作,以及这些动作的顺序,比如拷贝文件和编译代码等。
而Maven不一样,Maven已经默认了大家都知道有各种动作,默认了大家都遵守一个良好的目录结构,一个通用的构建过程,因此,使用Maven时我们只需要告诉Maven我们希望构建什么,而不需要告诉它如何构建。至于如何构建,已经在Maven的构建生命周期、阶段和目标中预定义好了(别着急,后面会讲这几个概念),我们要做的就是写声明式的POM文件。
Maven的构建模型:POM文件
Maven的核心思想都体现在POM文件中了,POM是Project Object Model的缩写,项目对象模型。一个项目的代码、测试代码(比如JUnit测试代码)、资源(比如模板文件和配置文件)、依赖的包等,都是一个项目构建中的关键信息。POM文件就是一个描述这些信息的XML文件,位于项目的根目录下。
回顾一下我们在Maven示例项目中的POM文件:
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-maven</artifactId>
<packaging>jar</packaging>
<version>0.1.0</version>
<!-- tag::joda[] -->
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
<!-- end::joda[] -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>hello.HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
当你执行一条Maven命令的时候,Maven会找到这个POM文件,并在POM文件描述的资源上执行这条命令,这个过程如下图所示:
图片来源jenkov.com
在上面的图片中,我们看到有这些个重要的概念:
- 构建生命周期、阶段和目标
- 依赖
- 仓库
- 插件
- 配置(Profile)
构建的生命周期、阶段和目标
Maven将构建过程分解为:构建生命周期(Build Lifecycle)、阶段(Phase)和目标(Goal)。其中构建周期由多个有序的构建阶段组成,一个构建阶段可以绑定一个或者多个的目标。
构建生命周期和阶段只是抽象的概念,不涉及具体的功能。 具体的功能由插件(Plugin)实现。一个插件可以实现多个目标(Goal),通过配置来决定在哪个阶段执行(Execution)哪些目标操作。 一个Goal也可以绑定到多个Phase,以实现复用。
Maven内置了三个构建生命周期:
- clean: 主要目的是清理项目
- default:定义了真正构建时所需要执行的所有步骤,它是生命周期中最核心的部分
- site: 生成项目站点文档
一条Maven命令,通常就是构建生命周期、阶段或目标的名字。
如果执行一个生命周期,该生命周期内的所有构建阶段都会被执行。
如果执行一个构建阶段,在预定义的构建阶段中,所有处于当前构建阶段之前的阶段也都会被执行,比如执行package阶段,意味着所有位于package阶段前的构建阶段都会被执行,然后才执行package阶段。
我们可以执行一个构建生命期,如clean
mvn clean
也可以执行一个构建阶段,如default生命期的install
mvn install
或者构建一个构建目标,如dependency:copy-dependencies
mvn dependency:copy-dependencies
提示
你不能直接执行default生命期,你需要指定default生命期中的一个构建阶段或者构建目标。
构建目标是Maven构建过程中最细化的步骤。一个目标可以与一个或多个构建阶段绑定,也可以不绑定。如果一个目标没有与任何构建阶段绑定,则只能将该目标的名称作为参数传递给mvn命令来执行它。如果一个目标绑定到多个构建阶段,该目标在绑定的构建阶段执行的同时被执行。
default生命期包含了相当多的构建阶段和目标,常用的构建阶段有:
- validate :验证项目的正确性,以及所有必需的信息都是否都存在。同时也会确认项目的依赖是否都下载完毕
- compile:编译项目源码
- test:选择合适的单元测试框架,对源码执行测试,测试代码不需要进行打包
- package:将编译后的代码以可分发的形式打包,如Jar包,WAR包
- install :项目打包后安装到本地仓库,安装到本地之后可以作为本地项目的依赖其它
- deploy:将打包的制品复制到远程仓库,可以进行远程共享
项目依赖
Java最大的一个优势之一应该是整个生态中无数的框架和API,我们创建实际的项目不可避免的都需要用到这些框架和API,而它们通常都是以JAR包的形式提供。 相信很多人都经历过JAR Hell的问题吧,事实上让一个项目所依赖的依赖的外部jar包保持正确的版本和最新的状态,是意见非常苦逼的事情。我们编译项目的时候,需要在classpath上存放依赖的jar包(不管直接使用Eclipse还是手动维护Ant)。而且这些外部的jar包还会有其他依赖。你一定经历过递归地一个个去下载所有这些外部依赖,并且要确保下载的版本都是正确的,当项目越来越复杂的时候,这是意见极其麻烦的事情。
Maven现在来拯救我们了,Maven可以自动帮我们做依赖管理,我们需要做的就是在pom文件里指定依赖jar包的名称、版本号,Maven会自动下载,递归地去下载依赖的进一步依赖这件事情我们也不需要管了。
Maven还提供一个非常方便的功能:快照依赖。快照依赖指的是那些还在开发中的内部依赖包。与其经常地更新版本号来获取最新版本,不如你直接依赖项目的快照版本。快照版本的每一个build版本都会被下载到本地仓库,即使该快照版本已经在本地仓库了。总是下载快照依赖可以确保本地仓库中的每一个build版本都是最新的。这对我们快速迭代开发是一个非常酷的特性。
Maven仓库
Maven仓库可以认为是基于Maven的一个构件(主要时Jar包)管理工具,你可以从里面找构件,也可以往里面增加构件。通过Ant来管理项目时,我们一般都需要一个 /lib文件夹,各种依赖的Jar包都会放进去,而且为了协作的一致性,通常还需要放到代码版本管理系统中。现在轻松简单了,基于基于依赖的信息,Maven就可以自动地以递归的方式下载所有的依赖,直到整个依赖树都下载完毕并放到你的本地仓库中。Maven仓库本质上存储了jar包和元数据信息,通过原信息能够准确定位到Jar包,从而能够获取和修改。
Maven有三种类型的仓库:
- 本地仓库:本地仓库默认在用户目录下,包含了下载的所有依赖
- 中央仓库:中央仓库由Maven自己提供的,本地仓库中的没有依赖默认都会去中央仓库查找,下载后会存放到本地仓库
- 远程仓库:远程仓库是远程服务器上可访问的仓库, 本质类似于中央仓库(只不过中央仓库是权威罢了,而且你不能乱动:)。远程仓库可以本地网络中或者互联网上,一般团队内部会设置一个内部的远程仓库,这样可以解决安全共享,网络代理等问题。
Maven首先会从本地仓库,然后是中央仓库,最后如果pom文件中配置了远程仓库,会到远程仓库中查找依赖。
远程仓库需要配置.settings文件,需要加入类似这样的配置
<repositories>
<repository>
<id>tianmaying</id>
<url>http://tianmaying.com/maven2/lib</url>
</repository>
</repositories>
插件和配置
构建插件可以向构建阶段中增加额外的构建目标。如果Maven标准的构建阶段和目标无法满足项目构建的需求,你可以在POM文件里增加插件。Maven有一些标准的插件供选用,如果需要你可以自己实现插件。
配置文件用于以不同的方式构建项目。比如,你可能需要在本地环境构建,用于开发和测试,你也可能需要构建后用于开发环境。这两个构建过程是不同的。在POM文件中增加不同的构建配置,可以启用不同的构建过程。当运行Maven时,可以指定要使用的配置。