引用
https://www.oracle.com/cn/java/technologies/apache-maven-getting-started-1.html
https://www.oracle.com/cn/java/technologies/apache-maven-getting-started-2.html
http://www.itsoku.com/article/240#menu_15
惯例优于配置
Maven 是一个项目管理和构建自动化工具,构建时使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构
- 一个 maven 项目在默认情况下会产生 JAR 文件,另外 ,编译后 的 classes 会放在 $/target/classes 下面, JAR 文件会放在 $/target 下面
构建项目
mvn archetype:generate -DgroupId=com.mycompany.helloworld -DartifactId=helloworld -Dpackage=com.mycompany.helloworld -Dversion=1.0-SNAPSHOT
archetype:generate 目标会列出一系列的 archetype 让你选择。 Archetype 可以理解成项目的模型。Maven 为我们提供了很多种的项目模型,包括从简单的 Swing 到复杂的 Web 应用。我们选择默认的 maven-archetype-quickstart
也可以通过 -D archetypeArtifactId: 指定 ArchetypeId,这里-D表示指定配置文件,通常-D与配置连写(也可分开写)。这里解释一下上文用到的配置
- groupId: 组织名,公司网址的反写 + 项目名称
- artifactId: 项目名-模块名
- package:包名(可不写)
- version:版本
配置文件
一个Maven项目所有的配置都放置在 POM 文件中:定义项目的类型、名字,管理依赖关系,定制插件的行为等等。
<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">
<modelversion>4.0.0</modelversion>
<groupid>com.mycompany.helloworld</groupid>
<artifactid>helloworld</artifactid>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>helloworld</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceencoding>UTF-8</project.build.sourceencoding>
</properties>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
在 POM 中,groupId, artifactId, packaging, version 叫作 maven 坐标,它能唯一的确定一个项目。有了 maven 坐标,我们就可以用它来指定我们的项目所依赖的其他项目,插件,或者父项目。
- 此时需要注意,由于Maven采用惯例大于配置的原则,所以pom.xml中的并不是全部配置,我们可以使用下面命令进行查看(建议在刚刚创建的项目下看一遍)
mvn help:effective-pom
Maven插件
在上文中,我们用了 mvn archetype:generate 命令来生成一个项目。那么这里的 archetype:generate 是什么意思呢?archetype 是一个插件的名字,generate是目标(goal)的名字。这个命令的意思是告诉 maven 执行 archetype 插件的 generate 目标。插件目标通常会写成 pluginId:goalId
一个目标是一个工作单元,而插件则是一个或者多个目标的集合。比如说Jar插件,Compiler插件,Surefire插件等。从看名字就能知道,Jar 插件包含建立Jar文件的目标, Compiler 插件包含编译源代码和单元测试代码的目标。Surefire 插件的话,则是运行单元测试
看到这里,估计你能明白了,mvn 本身不会做太多的事情,它不知道怎么样编译或者怎么样打包。它把构建的任务交给插件去做。插件定义了常用的构建逻辑,能够被重复利用。这样做的好处是,一旦插件有了更新,那么所有的 maven 用户都能得到更新。
Maven 生命周期
Maven中定义了三套生命周期
- clean生命周期
- default生命周期
- site生命周期
3套生命周期是相互独立的,没有依赖关系的,而每套生命周期中有多个阶段,每套中的多个阶段是有先后顺序的,并且后面的阶段依赖于前面的阶段,而用户可以直接使用mvn命令来调用这些阶段去完成项目生命周期中具体的操作
- 注意是调用阶段,如
mvn 生命周期阶段
在上文中,我们有一个命令是:mvn package。这里的 package 是一个maven的生命周期阶段 (lifecycle phase )。
- 再次注意!package是该生命周期的一个阶段!
那么生命周期阶段和上面说的插件目标之间是什么关系呢?插件目标可以绑定到生命周期阶段上。一个生命周期阶段可以绑定多个插件目标。当 maven 在构建过程中通过每个阶段时,会执行该阶段所有的插件目标。
maven 能支持不同的生命周期,但是最常用的是默认的Maven生命周期 (default Maven lifecycle ),生命周期默认按顺序执行,如果你没有对它进行任何的插件配置或者定制的话,那么上面的命令 mvn package 会依次执行默认生命周期中直到包括 package 阶段前的所有阶段的插件目标,图片列出了default生命周期的主要阶段
这里推荐一个查看生命周期的工具buildplan-maven-plugin
mvn fr.jcgay.maven.plugins:buildplan-maven-plugin:list
Maven 依赖管理
管理依赖反而是我们最常用,最简单的一部分,在 POM 中,依赖关系是在 dependencies 部分中定义的
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
那这个例子很简单,但是实际开发中我们会有复杂得多的依赖关系,因为被依赖的 jar 文件会有自己的依赖关系。那么我们是不是需要把那些间接依赖的 jar 文件也都定义在POM中呢?答案是不需要,因为 maven 提供了传递依赖的特性。
所谓传递依赖是指 maven 会检查被依赖的 jar 文件,把它的依赖关系纳入最终解决的依赖关系链中。
- 这里注意!在 POM 的 dependencies 部分中,scope 决定了依赖关系的适用范围。我们的例子中 junit 的 scope 是 test,那么它只会在执行 compiler:testCompile and surefire:test目标的时候才会被加到 classpath 中,在执行 compiler:compile 目标时是拿不到 junit 的。
我们还可以指定 scope 为 provided,意思是 JDK 或者容器会提供所需的jar文件。比如说在做web应用开发的时候,我们在编译的时候需要 servlet API jar 文件,但是在打包的时候不需要把这个 jar 文件打在 WAR 中,因为servlet容器或者应用服务器会提供的。
scope 的默认值是 compile,即任何时候都会被包含在 classpath 中,在打包的时候也会被包括进去。
实战
初识
我们通过mvn help:effective-pom
语句发现compiler也是一个插件
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
可以看到maven-compiler-plugin
插件绑定了生命周期的compile与test-compile阶段(通过phase)。这里id相当于是这个过程的名字,要求唯一,goal则是插件需要执行的目标。
绑定
我们尝试将maven-clean-plugin插件的clean操作绑定在validate阶段上
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<executions>
<execution>
<id>FHC-clean</id>
<goals>
<goal>clean</goal>
</goals>
<phase>validate</phase>
</execution>
</executions>
</plugin>
运行install(该阶段包含validate)发现该clean被执行
参数配置
通常运行命令的时候,我们需要-D进行大量的配置,非常的困难且不美观。我们可以通过configuration配置参数,格式如下:
<configuration>
<目标参数名>参数值</目标参数名>
</configuration>
我们尝试跳过测试,跳过前如下(无视这里的No tests to run.)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
跳过成功,这里跳过是整个插件的所以目标都跳过,如果只针对某个目标,则可以写在execution中