凌云的博客

行胜于言

Maven 入门

分类:maven| 发布时间:2018-04-19 23:58:00


什么是 Maven

Maven 是一个跨平台的项目管理工具。主要服务于基于 Java 平台的项目构建、依赖管理和项目信息管理。

安装 Maven

在 Ubuntu 平台下安装 maven 相当简单,键入以下命令即可:

% sudo apt-get install maven

下面的内容假设你已经安装好 Maven 并且配置好 Java 了。

编写 POM

就像 Make 的 Makefile、Ant 的 build.xml 一样,Maven 项目的核心是 pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建、声明项目依赖,等等。现在先为 hello World 项目编写一个最简单的的 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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany</groupId>
  <artifactId>hello-world</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>Maven Hello World Project</name>
</project>

代码的第一行是 XML 头,指定了该 xml 文档的版本和编码方式。紧接着是 project 元素,project 是所有 pom.xml 的根元素,它还声明了一些 POM 相关的命名空间以及 xsd 元素。 modelVersion 指定了当前 POM 模型的版本,对于 Maven 3 来说它只能是 4.0.0。 这段代码最重要的是包含 groupI、artifactId 和 version 的三行。这三个元素定义了一个项目的基本坐标,在 Maven 的世界,任何的 jar、pom 或者 war 都是基于这些基本的坐标进行区分的。 groupId 定义了项目属于那个组,这个组往往和项目所在的组织或公司存在关联。 artifactId 定义了当前 Maven 项目在组中唯一的 ID。 version 指定项目当前版本。 SNAPSHOT 表示该项目还在开发中,是不稳定的版本。

编写主代码

主代码和测试代码不同,项目的主代码会被打包进最终的构件中(如 jar),而测试代码只在运行测试时用到,不会被打包。 默认情况下,Maven 假设项目主代码位于 src/main/java 目录,我们遵循 Maven 的约定,创建该目录,然后在该目录下创建文件 com/mycompany/helloworld/HelloWorld.java,内容如下:

package com.mycompany.helloworld;

public class HelloWorld
{
    public String sayHello()
    {
        return "Hello Maven";
    }

    public static void main(String[] args)
    {
        System.out.print(new HelloWorld().sayHello());
    }
}

代码编写完毕后,使用 Maven 进行编译,在项目根目录下运行命令 mvn compile 得到如下输出:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.3:resources (default-resources) @ hello-world2 ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/ubuntu/src/hello/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ hello-world2 ---
[INFO] Nothing to compile - all classes are up to date
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.367s
[INFO] Finished at: Thu Apr 19 20:54:28 CST 2018
[INFO] Final Memory: 13M/481M
[INFO] ------------------------------------------------------------------------

项目的住代码会被编译进 target/classes 目录下。

编写测试代码

在 Java 世界中,JUnit 是事实上的单元测试标准。要使用 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">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mycompany</groupId>
  <artifactId>hello-world</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>Maven Hello World Project</name>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

上面的 scop 限定了该依赖只对测试代码有效。 Maven 项目默认测试代码在 src/test/java。创建该目录,并将如下测试代码放进这个目录:

package com.mycompany.helloworld;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class HelloWorldTest
{
    @Test
    public void testSayHello()
    {
        HelloWorld helloWorld = new HelloWorld();
        String result = helloWorld.sayHello();
        assertEquals("Hello Maven", result);
    }
}

使用 mvn test 命令编译并运行测试代码,很不幸,报错了:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.3:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/ubuntu/src/hello/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:compile (default-compile) @ hello-world ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.3:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/ubuntu/src/hello/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:2.0.2:testCompile (default-testCompile) @ hello-world ---
[INFO] Compiling 1 source file to /home/ubuntu/src/hello/target/test-classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.591s
[INFO] Finished at: Thu Apr 19 21:25:18 CST 2018
[INFO] Final Memory: 14M/481M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.0.2:testCompile (default-testCompile) on project hello-world: Compilation failure: Compilation failure:
[ERROR] /home/ubuntu/src/hello/src/test/java/HelloWorldTest.java:[3,7] 错误: -source 1.3 中不支持静态导入声明
[ERROR]
[ERROR] (请使用 -source 5 或更高版本以启用静态导入声明)
[ERROR] /home/ubuntu/src/hello/src/test/java/HelloWorldTest.java:[8,2] 错误: -source 1.3 中不支持注释
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

由于历史原因 Maven 的核心插件之一 compiler 插件默认只支持编译 Java 1.3,因此需要配置该插件使其支持我们当前版本的 Java。

修改 pom.xml,添加如下配置:

...
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
...

再次执行 mvn test,可以正常执行并通过测试了。

-------------------------------------------------------
 T E S T S
 -------------------------------------------------------
 Running com.mycompany.helloworld.HelloWorldTest
 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.049 sec

 Results :

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

 [INFO] ------------------------------------------------------------------------
 [INFO] BUILD SUCCESS
 [INFO] ------------------------------------------------------------------------
 [INFO] Total time: 6.144s
 [INFO] Finished at: Thu Apr 19 21:30:26 CST 2018
 [INFO] Final Memory: 16M/354M
 [INFO] ------------------------------------------------------------------------

打包和运行

可以通过 mvn package 将项目打成一个 jar 包:

...
[INFO]
[INFO] --- maven-jar-plugin:2.2:jar (default-jar) @ hello-world ---
[INFO] Building jar: /home/ubuntu/src/hello/target/hello-world-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.338s
[INFO] Finished at: Thu Apr 19 21:39:38 CST 2018
[INFO] Final Memory: 16M/469M
[INFO] ------------------------------------------------------------------------

默认情况下打包生成的 jar 文件是不能够直接运行的,因为带有 main 方法的类信息不会添加到 manifest 中。 为了生成可执行的 jar 文件,需要借助 maven-shade-plugin,在 pom.xml 中添加如下配置:

...
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.mycompany.helloworld.HelloWorld</mainClass>
          </transformer>
        </transformers>
      </configuration>
    </execution>
  </executions>
</plugin>
...

使用 mvn package 重新打包,可以看到 target 生成了两个 jar 包,其中一个是 original-xxx.jar 这个是不带有 MainClass 的 jar,而另一个则是包含 MainClass 的 jar。 现在我们可以通过:

java -jar target/hello-world-1.0-SNAPSHOT.jar

直接执行这个 jar 文件。

使用 Archetype 生成项目骨架

每次开始一个项目的时候都要手动编写 pom.xml 以及生成 src/main/java src/test/java 目录显然是很麻烦的事。 为此 Maven 提供了 Archetype 用于帮助我们快速地生成项目的骨架。 使用以下命令,然后根据向导填入相关信息即可:

% mvn archetype:generate