Maven某种程度上跟NPM类似,都是软件项目中包依赖解决方案。但是它比NPM更强大,因为它提供了更多地软件项目的编译、打包以及发布等操作。

本篇介绍最基础的步骤。

下载与配置

跟NPM需要安装node.js不同,Maven只需要下载和配置就能使用。

首先,去官方网站去下载。

以Windows系统为例,下载并解压缩到选定的文件夹(/install_dir/)。配置系统变量:

M2_HOME=/install_dir/

同时,更新系统变量Path

Path=$PATH:$M2_HOME/bin

确认下配置成功:

mvn -version

输出结果:

Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: D:\Tools\apache-maven-3.6.3\bin\..
Java version: 15.0.1, vendor: Oracle Corporation, runtime: D:\Tools\jdk-15.0.1
Default locale: en_US, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

新建、运行第一个项目

下载和配置完成之后,动手创建和执行项目才是最关键的。详情参阅官方文档

官方文档建议使用以下命令来创建新建一个项目:

mvn -B archetype:generate -DgroupId=com.alvachien.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

然而,这里有个第一个坑,在Windows环境下,上述命令只会导致下列错误:

The goal you specified requires a project to execute but there is no POM in this directory (D:\xxx). Please verify you invoked Maven from the correct directory.

解决方法:参数输入需要以“”封装:

mvn -B archetype:generate "-DgroupId=com.alvachien.app" "-DartifactId=marvin-tutorial" "-DarchetypeArtifcatId=maven-archetype-quickstart"

当然,可以看看Visual Studio Code的New Maven Project的命令:

mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate -DarchetypeArtifactId="maven-archetype-quickstart" -DarchetypeGroupId="org.apache.maven.archetypes" -DarchetypeVersion="1.4"

这时,就会成功创建项目。打开其中的App.java,就是一个简单的Hello World程序:

package com.alvachien.app;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

然后,编译项目:

mvn compile

接着,第二个坑出现了,因为刚刚创建的project编译通不过:

[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------< com.alvachien.app:marvin-tutorial >------------------
[INFO] Building marvin-tutorial 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ marvin-tutorial ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\srccodes\git\learning-notes\marvin-tutorial\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ marvin-tutorial ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\srccodes\git\learning-notes\marvin-tutorial\target\classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] Source option 5 is no longer supported. Use 7 or later.
[ERROR] Target option 5 is no longer supported. Use 7 or later.
[INFO] 2 errors

解决方法是,更新POM文件,指定使用Java Version 15:

	<properties>
      <maven.compiler.release>15</maven.compiler.release>
	</properties>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>    

然后,进行测试:

mvn test

测试结果顺利通过:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.alvachien.app.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.183 s
[INFO] Finished at: 2020-11-21T15:21:15+08:00
[INFO] ------------------------------------------------------------------------

如果需要运行程序,还需要将package为jar文件:

mvn package

这时,可以观察到target目录下就多了marvin-tutorial-1.0-SNAPSHOT.jar文件。

有了jar文件,可以运行程序了:

java -cp target/marvin-tutorial-1.0-SNAPSHOT.jar com.alvachien.app.App

运行结果:

>>
Hello World!

一个典型的Maven项目

一个典型的Maven项目结构,详情参与Standard Directory Layout

my-app   
├──── pom.xml   
├──── README.txt   
├──── LICENSE.txt   
├──── src   
│     ├──── main   
│     │     ├──── java   
│     │     │     └──── com   
│     │     │           └──── mycompany  
│     │     │                 └──── app   
│     │     │                       └──── App.java   
│     │     ├──── resources   
│     │     └──── filters 
│     ├──── test   
│     │     ├──── java   
│     │     │     └──── com   
│     │     │           └──── mycompany  
│     │     │                 └──── app   
│     │     │                       └──── AppTest.java   
│     │     ├──── resources   
│     │     └──── filters 
│     ├──── assembly
│     ├──── site
│     └──── it   
└──── target   

各文件和目录说明如下:

  • 项目描述文件: pom.xml
  • 项目说明文件: readme.txt
  • 项目版权文件: license.txt
  • 源码目录: src/main/java
  • 项目资源目录: src/main/resources
  • 项目资源filter目录: src/main/filters
  • 测试代码目录: src/test/java
  • 测试资源目录: src/test/resources
  • 测试资源filter目录: src/test/filters
  • 集成测试目录: src/it
  • 编译、打包输出目录:target
  • Assembly:Assembly
  • Site: Site

项目描述文件

一个典型的项目描述文件:

<project ...>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mycompany.app</groupId>
	<artifactId>my-app</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>
	<properties>
      <maven.compiler.release>11</maven.compiler.release>
	</properties>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>    

	<dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.3.2</version>
            <scope>test</scope>
        </dependency>
	</dependencies>
</project>
项目包的逻辑key

与NPM中使用名称和Version作为唯一标识来确定dependence类似,Maven中依赖管理的逻辑key是:

  • groupId,项目包开发组织的名称;
  • artifactId,该jar包自身的名称;
  • version,该jar包的版本。注意,以-SNAPSHOT结尾的版本会被Maven视为开发版本。

同样,在项目描述文件中,上述三个逻辑key也用作项目描述文件的关键信息。

依赖关系范围

Maven的依赖关系范围:

scope 说明
compile (默认)编译范围,只在编译时用到该依赖
test 编译test时需要用到该依赖。典型的runtime是JUnit
runtime 编译时不需要该依赖,运行时需要。典型runtime是JDBC驱动,如MySQL
provided 编译时用到该依赖,但运行时由JDK或某个服务器提供。典型的provided依赖是Servlet API
Maven下载与国内镜像

当Maven进行编译或测试时候,Maven会自动下载依赖的包。基于国情,指定使用国内镜像还是很有必要的。Windows系统下,在用户主目录下创建.m2目录,创建一个settings.xml配置文件:

<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun</name>
            <mirrorOf>central</mirrorOf>
            <!-- 阿里云的Maven镜像 -->
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </mirror>
    </mirrors>
</settings>

编译Maven项目

在根目录下(即pom.xml所在目录),可以直接进行编译:

mvn clean package

这样,所有文件被编译,并输出到target目录。

Maven的生命周期 (lifecyle)

常见的Maven的生命周期有两个:default和clean。

而Maven的生命周期由一系列阶段(Phase)构成。而Phase则对应了一个或多个Goal。常见的Phase对应的Goal如下:

Phase 对应的Goal
compile compiler:compile
test compiler:testCompile;surefire:test

实际上,每个Phase的执行,都是通过其对应的插件(plugin)来执行的。譬如,Maven本身其实并不知道如何执行compile,它只是负责找到对应的compiler插件,然后执行默认的compiler:compile这个goal来完成编译。

换言之,使用Maven,实际上就是配置好需要使用的插件,然后通过phase调用它们。

Plugin 对应执行Phase
clean clean
compiler compile
surefire test
jar package

Maven提供了能满足需求的标准插件。如果标准插件无法满足需求,我们还可以使用自定义插件。 一些常用的插件:

  • maven-shade-plugin:打包所有依赖包并生成可执行jar;
  • cobertura-maven-plugin:生成单元测试覆盖率报告;
  • findbugs-maven-plugin:对Java源码进行静态分析以找出潜在问题。

Maven的default生命周期

它包含了如下的阶段(Phase):

  • validate: validate the project is correct and all necessary information is available;
  • initialize
  • generate-sources
  • process-sources
  • generate-resources
  • process-resources
  • compile: compile the source code of the project;
  • process-classes
  • generate-test-sources
  • process-test-sources
  • generate-test-resources
  • process-test-resources
  • test-compile
  • process-test-classes
  • test: test the compiled source code using a suitable unit test framework;
  • prepare-package
  • package
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify: run any checks to verify the package is valid and meets quality criteria;
  • install: install the package into the local repository.
  • deploy: done in an integration or release environment, copies the final package to the remote repository.

Maven的clean生命周期

它包含了如下的阶段:

  • pre-clean
  • clean
  • post-clean

常见的Maven的命令

常见的Maven命令如下:

  • mvn clean: 执行clean生命周期中的phase,主要用于清理所有生成的class和jar;
  • mvn clean compile:先执行clean生命周期,再执行default生命周期到compile的phase;
  • mvn clean test:先执行clean生命周期,再执行default生命周期到test的phase;
  • mvn clean package:先执行clean生命周期,再执行default生命周期到package的phase;