这又是一个系列,一个要把 Maven 讲透的系列,希望能够对大家有帮助!
前言
说到 Maven 的入门使用,其实是特别简单的,如果只是说就是能使用,会使用 Maven,也许只要短短的一两个小时就 OK 了,不需要去理解 Maven 的那些概念,而这篇文章就是要教会你会使用 Maven,而整个系列则是要让你明白整个 Maven。这篇文章就是如此,仅仅就是告诉你怎么用 Maven,仅此而已,会用是学习整个系列的前提。
编写 POM
就像 composer 的 composer.json、Make 的 makefile 文件一样,Maven 项目的核心是 pom.xml 文件。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。
现在我们不借助任何其它命令和 IDE,来创建一个 Maven 项目。
首先,编写 pom.xml 文件。还是按照老规矩,从一个 Hello World 项目进行演示。以下就是创建项目的 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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jellythink.HelloWorld</groupId>
<artifactId>hello-world</artifactId>
<version>1.0-SNAPSHOT</version>
<name>hello-world</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
对于 POM 文件,现在细说一下。
- 代码的第一行是 XML 头,指定了该 xml 文档的版本和编码方式。紧接着是
project
元素,project
是所有 pom.xml 的根元素,它还声明了一些 POM 相关的命名空间及 xsd 元素; - 根元素下的第一个子元素
modelVersion
指定了当前 POM 模型的版本,对于 Maven 2 和 Maven 3 来说,它只能是 4.0.0; - 接下来就是
groupId
、artifactId
和version
了,这三个是上述代码三个元素。这三个元素定义了一个项目的基本坐标,在 Maven 的世界里,所有的 jar 和 war 都是基于坐标进行区分的,会面的文章还会细说坐标; -
groupId
定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联,一般是使用组织或公司的域名;比如上面的groupId
是com.jellythink.HelloWorld
,其中com.jellythink
就是我的网站域名倒过来写的,而HelloWorld
则是整个项目的名称; -
artifactId
定义了当前 Maven 项目在组中唯一的 ID,一般一个大项目组下面可能会包含多个子项目或子模块,而这个artifactId
就是子项目或者子模块的名称; -
version
指定了这个项目当前的版本,后面的文章还会细说 Maven 中版本的含义; -
name
元素声明了一个对于用户更为友好的项目名称,方便后期的管理; -
properties
指定了 Maven 的一些重要属性,后续还会重点说这个属性的一些配置。
创建完 pom.xml 文件后,接下来就是创建代码文件了。在 Maven 中,有这样的一个约定,项目主代码都位于 src/main/java
目录,项目测试代码都位于 src/test/java
目录;接下来我们先按照这个约定分别创建目录,然后在代码目录创建 com/jellythink/HelloWorld/App.java
文件;在测试目录创建 com/jellythink/HelloWorld/AppTest.java
文件。
还是老规矩,我们在 App.java
中打印 Hello World!,代码如下:
public class App {public String sayHello() {return "Hello World";}
public static void main(String[] args ) {System.out.println(new App().sayHello());
}
}
同理,对于 AppTest.java
中编写以下单元测试代码:
public class AppTest {
@Test
public void testSayHello() {App app = new App();
String result = app.sayHello();
assertEquals("Hello World", result);
}
}
在 Java 中,我们进行单元测试时,基本上都是使用的 JUnit,要使用 JUnit 这个包,我们就需要引入这个依赖包,此时,我们就需要在 pom.xml 中添加以下依赖内容:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
代码中添加了 dependencies
元素,该元素下可以包含多个 dependency
元素以声明项目的依赖。前面也说过,groupId
、artifactId
和 version
是任何一个 Maven 项目最基本的坐标,JUnit 也不例外;scope
表示依赖范围,后续的文章还会细说这个依赖和测试相关的内容。
编译和测试
万事俱备,只欠东风。接下来我们编译和测试。
搞定代码后,使用 Maven 进行编译,在项目根目录下运行 mvn clean compile
命令。执行输出如下图所示:
clean
告诉 Maven 清理输出目录 target,compile
告诉 Maven 编译项目主代码。从输出中看到 Maven 首先执行了 clean:clean
任务,删除 target 目录。默认情况下,Maven 构建的所有输出都在 target 目录中;接着执行 resources:resources
任务;最后执行 compiler:compile
任务,将项目主代码编译至 target/classes 目录。
上面说到的 clean:clean
、resources:resources
和compiler:compile
都对应了 Maven 的生命周期及插件,这个在后面还会有专题文章细说。
编译完成后,我们一般都会运行测试代码进行单元测试,虽然很多情况下,我们并没有这么做,但是我还是建议大家通过 Maven 做一些自动化的单元测试。
测试用例编写完毕之后就可以调用 Maven 执行测试,运行 mvn clean test
命令,输出如下:
从输出可以看到,Maven 依次执行了 clean:clean
、resources:resources
、compiler:compile
、resources:testResources
、compiler:testCompile
和surefire:test
。现阶段,我们需要明白这是 Maven 的生命周期的一个特性,这个生命周期后续还会细说。
到此,编译和测试均通过了,接着我们进行应用打包和运行。
打包和运行
打包就是将我们编写的应用打成 JAR 包或者 WAR 包。在我们的 HelloWorld 示例程序 POM 中,并没有指定打包类型,Maven 则默认打包成 JAR 包。我们执行 mvn clean package
命令就可以完成打包。mvn clean package
命令的输出如下:
可以看到,Maven 在打包之前会执行编译、测试等操作,最后通过 jar:jar
任务负责打包。实际上就是 jar 插件的 jar 目标将项目主代码打包成一个名为 hello-world-1.0-SNAPSHOT.jar 的文件,这个最终生成的包会保存在 target 目录下,它是根据 artifact-version.jar 规则进行命名的;当然了,我们可以使用 finalName 属性来自定义该文件的名称。
到现在,我们得到了这个 JAR 包,如果别的项目要引用这个 JAR 包时,我们将这个 JAR 包复制到其它项目的 classpath 中就 OK 了。但是这样拷贝就违背了我们当初想要自动解决依赖的问题,所以如何才能让其它的 Maven 项目直接引用这个 JAR 包呢?我们需要执行 mvn clean install
命令。
从输出可以看到,在打包之后,又执行了安装任务install:install
,最后将项目输出的 JAR 包安装到了 Maven 本地仓库中,我们可以在本地的仓库文件夹中能看到这个示例项目的 pom 和 jar 包。
到目前为止,通过这个示例项目体验了 mvn clean compile
、mvn clean test
、mvn clean package
和mvn clean install
。执行 test 之前会先执行 compile 的,执行 package 之前会先执行 test 的,而类似地,install 之前会执行 package。我们可以在任何一个 Maven 项目中执行这些命令。
最后,不要忘了,我们生成的 JAR 包是有 main 方法的,也就是说这个 JAR 包是可以单独运行的;但是,由于带有 main 方法的类信息没有添加到 manifest 中,所以默认打包生成的 jar 是不能够直接运行的(使用 jd-gui 打开 jar 文件中的 META-INF/MANIFEST.MF 文件,将无法看到 Main-Class 一行)。为了生成可执行 jar 文件,需要借助 Apache Maven Shade Plugin 来完成,我们需要在 pom.xml 文件中以下插件配置:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.jellythink.HelloWorld.App</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
接下来,我们再执行 mvn clean install
命令,待构建完成之后在 target 目录下可以看到 hello-world-1.0-SNAPSHOT.jar 和 original-hello-world-1.0-SNAPSHOT.jar,前面的是带有 Main-Class 信息的可运行 jar,后者是原始的 jar。我们执行以下命令:
java -jar hello-world-1.0-SNAPSHOT.jar
就可以正常执行。
使用 Archetype 生成项目骨架
上面非常详细的总结了如何全手动的创建一个 Maven 工程。通过上面的总结,我们大体可以总结以下几步:
- 在项目根目录创建 pom.xml 文件;
- 创建
src/main/java
目录,并在该目录开发项目主代码; - 创建
src/test/java
目录,并在该目录开发项目测试代码;
上面的三步我们成为 Maven 项目的骨架,也就是现在常说的“脚手架”。如果我们每创建一个 Maven 项目都需要把上面的步骤执行一次,确实很麻烦,那怎么办?程序就是解放人工的,让人来偷懒的。所以,在 Maven 中,我们可以通过 Archetype 生成项目骨架,将上面的步骤流程化。比如,现在我们要创建一个 Maven 项目,我们只需要输入以下命令就 OK 了。
mvn archetype:generate
执行这个命令后,后看到很多输出,有很多可用的 Archetype 供我们选择,每一个 Archetype 前面都会对应有一个编号,我们根据我们的需要,选择我们对应的骨架来创建项目就好了。然后再按照提示,输出新项目的 groupId、artifactId、version 和包名 package,一个 Maven 项目就创建成功了。
我们在运行 mvn archetype:generate
时,实际上是在运行 maven-archetype-plugin
插件。
总结
到此,这篇关于 Maven 的初级入门文章就到此总结完毕,这篇文章的知识点比较多,而且还很杂,如果看的有点不是很懂,也没有关系,后续的文章都会对这些你不懂的地方,你不熟悉的地方在进行深入的剖析和总结。当然了,也希望大家能通过这篇文章对 Maven 的使用有一个整体的认识,至少没有后续的文章,通过这篇文章,你也应该知道怎么使用 Maven 了,不是吗?
这一夜,深深的自责中 ……
果冻想,认真玩技术的地方。
2019 年 4 月 1 日,于内蒙古呼和浩特。