Maven 新兵营
作者: dkvirus 发表于: 2018-08-04 23:24:47 最近更新: 2018-08-09 15:42:46

本文介绍 maven 从安装到上手一套操作,对新手友好,跟着练一遍可以快速入门 maven。dk 的学习素材是 《maven 实战》,想要深入学习的可以看这本书。

一、安装

1. 下载 maven

maven 官方下载页面, window 电脑下载第二个文件,mac 电脑下载第一个文件,下载完解压缩即可,无需安装。接下来就是配置环境变量,这样在终端中才可以直接运行 mvn 命令。

maven 下载

2. mac 下配置 maven 环境变量

编辑当前用户目录下的 .bash_profile 文件,在末尾添加如下内容。其中:MAVEN_HOME 的值替换成您电脑中 maven 文件所在路径。

1
2
3
4
MAVEN_HOME=/Users/dkvirus/Documents/apache-maven-3.5.4
PATH=$MAVEN_HOME/bin:$PATH
export MAVEN_HOME
export PATH

运行 $ source .bash_profile 让配置文件即时生效,在终端中输入 mvn -v 如果打印出版本号说明配置成功。

3. window 下配置 maven 环境变量

window 下配置 maven 环境变量与配置 jdk 变量类似,dk 由于了刚换了机器没法截图,有不会的直接谷歌/百度一下吧。

二、概述

1. maven 能做什么

maven 简化了从 项目创建 > 开发 > 测试 > 打包 整个流程,所有原本复杂/麻烦的操作现在只要在终端中敲几个命令即可完成。

maven 以优雅的方式处理 jar 包之间的依赖关系,这些关系都被定义在 pom.xml 中。

2. pom.xml 文件

每个 maven 项目根目录下都会有一个 pom.xml 文件,该文件定义了项目的基本信息,包依赖关系,插件使用等情况,类似于 npm 里 package.json 的功能,pom.xml 的基本结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?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.dkvirus</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>maven入门项目</name>
</project>

第一行 xml 头,指定该 xml 文档的版本为 1.0,编码方式为 UTF-8。

第二行 project 元素,是 pom.xml 的根元素。project 元素中有一些属性 xmlnsxmlns:xsixsi:schemaLocation,这些属性叫做 命名空间xsd 属性,用处是在开发工具中按【Ctrl + /】键可以提示 pom.xml 中的常用元素,而不用一个个元素的去敲。

第三行 modelVersion 元素定义当前 POM 模型的版本,maven2和maven3版本这里都填写4.0.0;

接下来介绍的三个元素 groupId、artifactId 和 version 确定一个 jar 包在 maven 仓库中的唯一性,就像点在空间中有x、y、z三个坐标确定位置一样。

第四行 groupId 元素定义项目属于哪个组,通常与项目所在的组织或公司关联。如果是个人玩的项目,比如 dkvirus,组名就叫 com.dkvirus 吧;Npm 仓库里确定一个包唯一坐标是通过 包名+版本号,很显然这种设计并不合理,像 Angular、Babel 这些大工程是不可能把所有代码都写到一个包里的,因此经常会看到 @angular/animation 这种写法,前面 @angular 标识这个包所属组织。在这一点上,maven 就显得预卜先知,在设计初就添加了 groupId 定义组的概念。

第五行 artifactId 元素定义当前 maven 项目在组中唯一的ID,项目名称;

第六行 version 元素指定当前 maven 项目的版本。SNAPSHOP 是快照的意思(通常表示还在开发中),每次发布开发版本时会自动加上时间戳, 无需手动递增版本。如果测试通过,发布正式版本,将 SNAPSHOP 去掉,如 1.0.0,没有看到 SNAPSHOP 的包通常为正式发布的稳定包。

第七行 name 元素声明一个对用户更加友好的项目名称,可以填写中文。

3. maven 目录结构

maven 项目有统一的目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
|-- my-app
|-- src
|-- main
|-- java <= 主代码
|-- resources <= 主代码需要用到的资源,如配置文件等
|-- test
|-- java <= 测试代码
|-- resources <= 测试代码需要用到的资源
|-- target
|-- classes <= 打包之后存放路径,.class 文件在这里哦
|-- maven-status <= 作用未知
|-- pom.xml <= 项目对象模型

根目录下还有两个隐藏文件,

  • .classpath 定义路径用的,定义打包哪个目录下的文件,打包结果放到哪个目录下;
  • .project 作用未知。每个 maven 项目都有这个文件。

4. maven 本地仓库

所有通过 maven 下载的 jar 包都会放在 maven 本地仓库,默认位置是当前登录用户主目录下的 .m2 目录下。

代码中经常会看到 import org.junit.Test;,这是导入其它 jar 包,这个 jar 可以从 .m2 目录下找到。

三、创建项目

1. 创建目录结构

首先按照 2.3 中介绍的 maven 默认的目录结构手动创建项目 hello-world。建议别一开始就用 sts 或者 eclipse 这种 java 开发工具,找一个文本编辑器开始maven 练习吧。

maven默认目录结构

如果每次创建新项目都要一个个手动创建目录,简直麻烦死了。maven 提供命令 $ mvn archetype:generate 可以直接生成上述目录结构,下面创建第二个 maven 项目(第6.2.2小节详细介绍,迫不及待的可以先跳转查看)做测试时会用到该命令。

2. 定义 pom.xml

看到这个目录下有 pom.xml 文件就知道这是一个 maven 项目,复制下面内容粘贴到 pom.xml 文件中。

项目组织为 com.dkvirus,项目名称为 hello-world,项目版本为 0.0.1-SNAPSHOT,项目简介为 maven 入门项目。

1
2
3
4
5
6
7
8
9
10
11
12
<?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.dkvirus</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>maven入门项目</name>
</project>

四、开发

1. 新建文件

在 /src/main/java 目录下新建子目录 com/dkvirus/helloworld,在该目录下创建文件 HelloWorld.java(该文件完整路径为:/src/main/java/com/dkvirus/helloworld/HelloWorld.java)。

1
2
3
4
5
6
7
8
9
10
11
package com.dkvirus.helloworld;

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

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

dk 一开始学习到这里会很疑惑为什么要在 src/main/java 目录下创建多层嵌套的子目录 com/dkvirus/helloworld,直接创建一个 helloworld 目录不是更加简洁吗??事实证明 dk 还是 too young too simple,接着往下看会找到答案。。。(第6.2.4详细介绍)

2. 编译运行

代码写好了,接下来就编译运行了。传统做法是在终端运行 $ javac HelloWorld.java 进行编译,使用 maven 只要执行 $ mvn compile 即可,该命令会自动去 src/main/java 目录下找所有 .java 结尾的文件进行编译,从这里也可以看到 maven 规定默认目录结构是有好处的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1  dkvirus@localhost:hello-world$  mvn compile
2 [INFO] Scanning for projects...
3 [INFO]
4 [INFO] ----------------------< com.dkvirus:hello-world >-----------------------
5 [INFO] Building maven入门项目 0.0.1-SNAPSHOT
6 [INFO] --------------------------------[ jar ]---------------------------------
7 [INFO]
8 [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
9 [INFO] Copying 0 resource
10 [INFO]
11 [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
12 [INFO] Changes detected - recompiling the module!
13 [INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
14 [INFO] ------------------------------------------------------------------------
15 [INFO] BUILD SUCCESS
16 [INFO] ------------------------------------------------------------------------
17 [INFO] Total time: 2.462 s
18 [INFO] Finished at: 2018-08-04T21:27:59+08:00
19 [INFO] ------------------------------------------------------------------------

编译完成后可以去 /target/classes/ 目录下可以找到 HelloWorld.class 文件。

五、测试

1. 依赖 junit 测试包

测试 Java 代码使用 junit 测试包。传统做法是去网上找 junit 包手动下载到本地使用,使用 maven 只需将 junit 的三个坐标添加到 pom.xml 即可,maven 会自动从 maven 的中央仓库下载这个 jar 包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<project>
<!-- ..... -->

<!-- 依赖配置 -->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>

dependencies 元素定义当前项目依赖的所有 jar 包,其子元素 dependency 定义依赖的具体包,由 groupId、artifactId、version 唯一确定包的坐标。只需添加上述配置,保存代码,maven 会自动下载 junit 包到本地以供使用。

Q1:maven 是从哪里下的呢??

A1:答案是:maven 中央仓库,全世界 java 开发者都可以往这上面提交自己的 jar 包,供他人下载使用,减少重复造轮子。

Q2:怎么知道 junit 包的三个坐标呢??

A2:进入 maven 中央仓库 这个网站,具体操作参考如下步骤:

1)在搜索框搜索包名,会列举出相关包,根据组织名(groupId)和包名(artifactId)确定目标包;

找maven包坐标图1

2)选择指定版本,dk 通常是通过右边红框中数量多少选择具体版本;

找maven包坐标图2

3)下方的文本域就是要找的信息,复制到 pom.xml 文件中的 dependencies 子元素位置即可。

找maven包坐标图3

2. 编写测试代码

在 src/test/java/com/dkvirus/helloworld 目录下新建测试文件 HelloWorldTest.java 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.dkvirus.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 World", result);
}

}

3. 编译运行

$ mvn test 会对 src/test/java 下 *Test.java 或者 Test*.java 的文件进行编译,编译后的代码放在 target 目录下。并自动执行测试代码,下方会统计测试结果,这里可以看到测试结果是通过的。尝试将测试代码改为 assertEquals("Hello World2", result);,再次执行 $ mvn test 命令查看结果。

1
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
dkvirus@localhost:hello-world$  mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.dkvirus:hello-world >-----------------------
[INFO] Building maven入门项目 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /Users/dkvirus/work/self/workspace/hello-world/target/surefire-reports

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

Results :

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

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.746 s
[INFO] Finished at: 2018-08-04T21:48:51+08:00
[INFO] ------------------------------------------------------------------------

编译后的测试代码可以在 /target/test-classes/ 目录下查看到。

六、打包

1. 当前项目打成 jar 包

$ mvn package 将当前 java 项目打成 jar 包。maven 在打包前会先执行编译、测试操作;打包过程是将项目主代码打包成一个名为 hello-world-0.0.1-SNAPSHOT.jar 的文件,该文件也位于 target 目录下。打包后的 jar 包命名规则由 pom.xml 中 artifact-version.jar 的格式确定的。

1
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
dkvirus@localhost:hello-world$  mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.dkvirus:hello-world >-----------------------
[INFO] Building maven入门项目 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/dkvirus/work/self/workspace/hello-world/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /Users/dkvirus/work/self/workspace/hello-world/target/surefire-reports

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

Results :

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

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /Users/dkvirus/work/self/workspace/hello-world/target/hello-world-0.0.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.213 s
[INFO] Finished at: 2018-08-04T21:59:01+08:00
[INFO] ------------------------------------------------------------------------

2. 让其它项目使用该 jar 包

1)将当前 jar 包添加到 maven 本地仓库

$ mvn install 将 hello-world 输出的 jar 包安装到 maven 本地仓库中,这样其它 maven 项目通过 import com.dkvirus.helloworld.* 就可以使用 hello-world 项目中的类了。

下面再次新建一个 maven 项目 hello-test,这次不用一个个目录手动创建,直接使用 maven 命令 $ mvn archetype:generate 自动创建目录结构 。

2)maven 自动生成目录结构

1
2
3
|-- work
|-- hello-world
|-- hello-test

切换到 hello-world 所在的父目录位置:work 目录,执行 $ mvn archetype:generate 会在该目录下创建 maven 项目,这样 hello-test 和 hello-world 就是同级关系。

接下来终端会 bulabula 输出一大串东东,过一会看见光标不动了就进入交互模式,maven 会问一些问题需要你作答,这里以 hello-test 项目创建过程的交互为例进行说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# ------- 让你选哪个maven原型,回车选择默认即可 --------
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1219:
# ------- 让你选择maven原型的版本,回车选择即可 --------
Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
7: 1.3
Choose a number: 7:
Define value for property 'groupId': com.dkvirus <===== 输入 groupId,我这里输入 com.dkvirus
Define value for property 'artifactId': hello-test <===== 输入 artifactId,我这里输入 hello-test
Define value for property 'version' 1.0-SNAPSHOT: : <===== 输入包版本号,这里回车选择默认的 1.0-SNAPSHOT
Define value for property 'package' com.dkvirus: : com.dkvirus.hellotest <==== 工程的包名,这里填写 com.dkvirus.hellotest
Confirm properties configuration:
groupId: com.dkvirus
artifactId: hello-test
version: 1.0-SNAPSHOT
package: com.dkvirus.hellotest
Y: :

第一行让选择 maven 原型项目。不同原型项目目录结构略有不同,如 maven-web 原型项目拉下来就直接是个 web 工程的目录结构,而 maven-java 项目拉下来只是个 java 工程的目录结构。当然你也可以定义自己的目录结构,上传 maven 官方仓库,再次敲这个命令时也许会看到你自己原型项目的编号。

第二行让选择 maven 原型项目的版本。maven 项目可能会更新,自然就有版本的概念了,练习的话直接回车选择默认版本即可。

3)添加 helloworld 依赖配置

修改 pom.xml 文件,在 dependencies 元素下添加 helloworld 依赖配置。

1
2
3
4
5
6
7
8
9
10
11
12
<project>

<dependencies>
...
<dependency>
<groupId>com.dkvirus</groupId>
<artifactId>hello-world</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

</project>

4)测试

修改 src/test/java 下的 APP.java 文件,替换为以下内容。这里可以看到引入了 helloworld 项目中包 com.dkvirus.helloworld 中的 HelloWorld.java 文件了。

回过头看一下第 4.1 留下的问题:为什么创建多层嵌套目录 com/dkvirus/helloworld 而不是直接创建子目录 helloworld 呢??如果直接创建一个子目录 helloworld,那么这里导入包语句变为 import helloworld.HelloWorld;,想一想这样不是很容易导致重名吗?因此包的命名尽量以 groupId 为前缀创建多层嵌套目录,组织名不至于那么容易重名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.dkvirus.hellotest;

import static org.junit.Assert.*;
import org.junit.Test;
import com.dkvirus.helloworld.HelloWorld;

/**
* Unit test for simple App.
*/
public class AppTest
{
@Test
public void testSayHello () {
HelloWorld helloWorld = new HelloWorld();

String result = helloWorld.sayHello();

assertEquals("Hello World", result);
}
}

运行 $ mvn test 查看终端输出信息,测试通过。

dk 一开始做到这一步以为 hello-test 引用的是 maven 本地仓库的 hello-world 项目里的包,后来发现并不是。

因为 hello-world 和 hello-test 都在 work 目录下,属于同级关系,因此 hello-test 会优先引用同级目录下的 hello-world 里的 HelloWorld.java 文件,而不是去 maven 本地仓库找 jar 包。修改 work/hello-world 里的 HelloWorld.java 文件,将打印信息改成了 Hello World2,此时运行 hello-test 项目的单元测试代码,发现失败了,这一点即可证明优先级为同级目录下的 hello-world 项目。

1
2
3
4
5
6
7
8
9
10
11
package com.dkvirus.helloworld;

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

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

将 work/ 目录下的 hello-world 工程删除或者移动一个位置,再次运行 hello-test 项目的单元测试代码发现这一次是通过的,说明这一次引用的是 maven 本地仓库里的 hello-world 里的文件。

七、总结

到这里应该对 maven 有了大概的认识了吧,即便作为前端的 dk 现在看公司的 java 工程也比之前清晰很多,总结一下上面的内容:

  • 创建项目:$ mvn archetype:generate
  • 编译代码:$ mvn compile
  • 测试代码:$ mvn test
  • 打包代码:$ mvn package
  • 将当前包安装到本地 maven 仓库:$ mvn install

还有一个常用的命令 $ mvn clean 会删除 target 目录。通常上面几个命令执行前都会先执行 clean 命令,如:$ mvn clean install 就会先执行 clean 再执行 install,而不用分两次命令 $ mvn clean$ mvn install 执行,实在是很方便。

首页
友链
归档
dkvirus
动态
RSS