第一个 Maven 项目
# 20.第一个 Maven 项目
现在,我们从一个最简单的 HelloWorld 项目开始学习 Maven。
# 编写 pom.xml
类似 Make 的 Makefile,Ant 的 build.xml 一样,Maven 也是通过配置文件来管理项目的,该文件就是 pom.xml。
pom 全称 Project Object Model,项目对象模型,pom.xml 里配置了项目的基本信息,如何构建,用到的依赖等等
我们新建一个 LearnJavaMaven 的文件夹,并在里面新建一个 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.peterjxl.LearnJavaMaven</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Hello World Project!</name>
</project>
2
3
4
5
6
7
8
9
10
我们逐行说明文件的内容:
- 代码的第一行是 XML 头,指定了该 XML 文档的版本和编码方式。
- 紧接着是 project 元素,project 是 pom.xml 的根元素。
- modelVersion 指定了当前 POM 模型的版本,对于 Maven 2 及 Maven 3 来说,它只能是 4.0.0,这里不详细说明
- 接下来的三行是最重要的。这三行决定了一个项目的基本坐标,在 Maven 中,无论是寻找什么依赖,都是依靠坐标。groupId 定义了项目属于哪个组,常见的方式是组织名+项目名,而组织名可以用倒置的域名(类似 Java 中包的管理)
- artifactId 说明了该模块的 ID,不同的模块 ID 不同。
- version 指定了 Hello World 项目当前的版本——1.0-SNAPSHOT。SNAPSHOT 意为快照,说明该项目还处于开发中,是不稳定的版本。随着项目的发展,version 会不断更新,如升级为 1.0、1.1-SNAPSHOT、1.1、2.0 等
前面我们说了 Maven 有全局配置和用户配置,其实 Maven 还有第三个配置:针对单个项目的配置。说明如下:
- Global (MAVEN_HOME/conf/settings.xml) 针对所有用户的配置
- User(USER_HOME/.m2/settings.xml) 针对单个用户的配置
- Project(PROJECT_ROOT/pom.xml) 针对单个项目的配置
# Maven 的目录结构
在 Maven 出现之前,一千个项目就有一千个结构,例如
- 有的项目中,源码目录是 source,配置文件目录是 config,构建方式使用 Ant
- 有的项目,源码目录则是 src,配置文件则是 setting,构建方式使用 make
- ......
而有了 Maven 后,全部项目使用统一的目录结构,这样能降低项目的学习成本。
之前我们说过,Maven 采用了约定大于配置的设计,对于项目的目录结构,也有一定的约定。常见的 Maven 项目的目录结构如下:
LearnJavaMaven
├── pom.xml
└── src
├── main
│ ├── java --存放项目的.java 文件
│ ├── resources --存放项目资源文件,如 Log4j等框架的配置文件
│ └── webapp --页面资源,HTML,JS,CSS,图片等
└── test
├── java --存放所有单元测试.java 文件,如JUnit 测试类
└── resources --- 测试资源文件
2
3
4
5
6
7
8
9
10
11
在 Maven 中,默认项目的主代码目录是在 src/main/java 目录下,而测试用的代码则是在 src\test\java 目录下;resources 目录则用来存放配置文件,webapp 目录则是存放前端静态资源。
后续我们使用 Maven,都是基于这个结构来使用的,请读者务必知道这个结构。
我们根据上述约定,创建好对应的文件夹。
# 编写主代码
我们在 src/main/java 目录下创建这样的目录:com\peterjxl\learnjavamaven\demo1
然后在里面创建一个 HelloWorld 类,代码如下:
package com.peterjxl.learnjavamaven.demo1;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello Maven!");
}
}
2
3
4
5
6
7
目前文件夹结构如下:
LearnJavaMaven
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── peterjxl
│ │ └── learnjavamaven
│ │ └── demo1
│ │ └── HelloWorld.java
│ ├── resources
│ └── webapp
└── test
├── java
└── resources
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 编译主代码并运行
然后我们就可以用 Maven 编译了。在项目根目录下输入命令 mvn clean compile
,输出如下:
> mvn clean compile
[INFO] Scanning for projects...
[INFO]
[INFO] --------------< com.peterjxl.LearnJavaMaven:hello-world >---------------
[INFO] Building Maven Hello World Project! 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent!
[INFO] Compiling 1 source file to D:\Projects\LearnJavaMaven\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.567 s
[INFO] Finished at: 2023-04-11T08:02:20+08:00
[INFO] ------------------------------------------------------------------------
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
输出内容有点多,我们不会全部讲解其内容,重点挑一两个重要的讲,其他的后面再说。
Maven 会将构建的结果,放到项目根目录下的 target 文件夹,我们可以看第 17 行,意思是说将编译后的文件放到了 target\classes 目录下。
此时,我们目前的文件夹结构如下:
LearnJavaMaven
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── peterjxl
│ │ │ └── learnjavamaven
│ │ │ └── demo1
│ │ │ └── HelloWorld.java
│ │ ├── resources
│ │ └── webapp
│ └── test
│ ├── java
│ └── resources
└── target
├── classes
│ └── com
│ └── peterjxl
│ └── learnjavamavn
│ └── demo1
│ └── HelloWorld.class
├── generated-sources
│ └── annotations
└── maven-status
└── maven-compiler-plugin
└── compile
└── default-compile
├── createdFiles.lst
└── inputFiles.lst
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
可以看到 Maven 帮我们生成了 target 文件夹,该文件夹就用于存放我们后续编译后的结果。
里面又有 3 个文件夹,我们重点看 classes 文件夹,可以看到 Maven 将我们的类编译好了。
我们可以试着运行:
cd ./target/classes
java com.peterjxl.learnjavamaven.demo1.HelloWorld
Hello Maven!
2
3
可以看到确实编译结果是正常的。至于我们用的 Maven 命令 mvn clean compile
,我们后续再讲,目前关键是运行起来项目。
# 编写测试代码并运行
在 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.peterjxl.LearnJavaMaven</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Hello World Project!</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
我们添加了第 11 至 18 行,<dependencies>
顾名思义就是很多依赖的意思,该标签体里可以放一个个 <dependency>
的标签,每个标签对应一个具体的依赖,可以是 Junit,也可以是 JDBC。
这里我们添加了 Junit 的依赖,关于 <dependency>
标签体里的内容,我们后续会讲解,目前知道这么回事就行。
配置了测试依赖,就可以编写测试类,以之前的 HelloWorld 类为例,测试 main 方法。在 src/test/java 目录下创建对应的包和测试类 HelloWorldTest.java,代码内容:
package com.peterjxl.learnjavamaven.demo1;
import org.junit.Test;
public class HelloWorldTest {
@Test
public void testMain() {
HelloWorld.main(new String[10]);
}
}
2
3
4
5
6
7
8
9
10
测试用例编写完毕之后就可以调用 Maven 执行测试。 运行 mvn clean test
:观察最后的输出如下:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.peterjxl.learnjavamavn.demo1.HelloWorldTest
Hello Maven!
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.330 s
[INFO] Finished at: 2023-04-11T20:57:27+08:00
[INFO] ------------------------------------------------------------------------
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
可以看到测试结果为:1 个测试用例被执行了,失败 0 个,错误 0 个,跳过 0 个(第 10 行)
# 打包
将项目进行编译、 测试之后,下一个重要步骤就是打包(package) 。pom.xml 文件中没有指定打包类型, 默认打包类型 jar。 简单地执行命令 mvn clean package
进行打包,并观察最后的输出:
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: D:\Projects\LearnJavaMaven\target\hello-world-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.728 s
[INFO] Finished at: 2023-04-11T21:06:00+08:00
[INFO] ------------------------------------------------------------------------
2
3
4
5
6
7
8
Maven 会在打包之前执行编译、 测试等操作,并在最后告诉我们打包结果。
可以看到第 4 行告诉我们打包成功了,第 2 行告诉我们包的路径在 target 目录下。
默认打包生成的 jar 是不能够直接运行的, 因为带有 main 方法的类信息不会添加到 manifest 中。打开 jar 文件中的 META-INF/MANIFEST.MF 文件, 将无法看到 Main-Class 一行。
为了生成可执行的 jar 文件, 需要借助一个插件 maven-shade-plugin(什么是插件后续会讲), 我们配置 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.peterjxl.LearnJavaMaven</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Hello World Project!</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.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>com.peterjxl.learnjavamaven.demo1.HelloWorld</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
主要添加了第 20 行到 43 行,该配置是构建(build)时的配置,并配置了主类是什么(第 35 行)
然后我们再次执行 mvn clean package
,并测试能否执行 main 方法:
cd target
java -jar hello-world-0.0.1-SNAPSHOT.jar
Hello Maven!
2
3
运行成功。后续我们开发 JavaWeb 项目的时候,可以配置打包为 war 类型
# 总结
本文我们简单讲解了 Maven 的目录结构,这里也放张图,可能更直观一点:
还介绍了 pom.xml 和一些简单的 Maven 命令。本文已将所有代码上传到 Gitee (opens new window) 和 GitHub (opens new window) 上,本文中涉及到的代码在分支 demo1HelloWorld。