本文介绍 maven 从安装到上手一套操作,对新手友好,跟着练一遍可以快速入门 maven。dk 的学习素材是 《maven 实战》,想要深入学习的可以看这本书。
一、安装
1. 下载 maven
maven 官方下载页面, window 电脑下载第二个文件,mac 电脑下载第一个文件,下载完解压缩即可,无需安装。接下来就是配置环境变量,这样在终端中才可以直接运行 mvn 命令。
2. mac 下配置 maven 环境变量
编辑当前用户目录下的 .bash_profile 文件,在末尾添加如下内容。其中:MAVEN_HOME 的值替换成您电脑中 maven 文件所在路径。
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 的基本结构如下:
4.0.0
com.dkvirus
hello-world
0.0.1-SNAPSHOT
maven入门项目
第一行 xml 头,指定该 xml 文档的版本为 1.0,编码方式为 UTF-8。
第二行 project 元素,是 pom.xml 的根元素。project 元素中有一些属性 xmlns、xmlns:xsi、xsi: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 项目有统一的目录结构:
|-- 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 提供命令 $ mvn archetype:generate
可以直接生成上述目录结构,下面创建第二个 maven 项目(第6.2.2小节详细介绍,迫不及待的可以先跳转查看)做测试时会用到该命令。
2. 定义 pom.xml
看到这个目录下有 pom.xml 文件就知道这是一个 maven 项目,复制下面内容粘贴到 pom.xml 文件中。
项目组织为 com.dkvirus,项目名称为 hello-world,项目版本为 0.0.1-SNAPSHOT,项目简介为 maven 入门项目。
4.0.0
com.dkvirus
hello-world
0.0.1-SNAPSHOT
maven入门项目
四、开发
1. 新建文件
在 /src/main/java 目录下新建子目录 com/dkvirus/helloworld,在该目录下创建文件 HelloWorld.java(该文件完整路径为:/src/main/java/com/dkvirus/helloworld/HelloWorld.java)。
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 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 包。
junit
junit
4.7
test
dependencies 元素定义当前项目依赖的所有 jar 包,其子元素 dependency 定义依赖的具体包,由 groupId、artifactId、version 唯一确定包的坐标。只需添加上述配置,保存代码,maven 会自动下载 junit 包到本地以供使用。
Q1:maven 是从哪里下的呢??
A1:答案是:maven 中央仓库,全世界 java 开发者都可以往这上面提交自己的 jar 包,供他人下载使用,减少重复造轮子。
Q2:怎么知道 junit 包的三个坐标呢??
A2:进入 maven 中央仓库 这个网站,具体操作参考如下步骤:
1)在搜索框搜索包名,会列举出相关包,根据组织名(groupId)和包名(artifactId)确定目标包;
2)选择指定版本,dk 通常是通过右边红框中数量多少选择具体版本;
3)下方的文本域就是要找的信息,复制到 pom.xml 文件中的 dependencies 子元素位置即可。
2. 编写测试代码
在 src/test/java/com/dkvirus/helloworld 目录下新建测试文件 HelloWorldTest.java
文件。
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
命令查看结果。
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 的格式确定的。
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 自动生成目录结构
|-- work
|-- hello-world
|-- hello-test
切换到 hello-world 所在的父目录位置:work 目录,执行 $ mvn archetype:generate
会在该目录下创建 maven 项目,这样 hello-test 和 hello-world 就是同级关系。
接下来终端会 bulabula 输出一大串东东,过一会看见光标不动了就进入交互模式,maven 会问一些问题需要你作答,这里以 hello-test 项目创建过程的交互为例进行说明。
# ------- 让你选哪个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 依赖配置。
...
com.dkvirus
hello-world
0.0.1-SNAPSHOT
4)测试
修改 src/test/java 下的 APP.java 文件,替换为以下内容。这里可以看到引入了 helloworld 项目中包 com.dkvirus.helloworld 中的 HelloWorld.java 文件了。
回过头看一下第 4.1 留下的问题:为什么创建多层嵌套目录 com/dkvirus/helloworld 而不是直接创建子目录 helloworld 呢??如果直接创建一个子目录 helloworld,那么这里导入包语句变为 import helloworld.HelloWorld;
,想一想这样不是很容易导致重名吗?因此包的命名尽量以 groupId 为前缀创建多层嵌套目录,组织名不至于那么容易重名。
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 项目。
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
执行,实在是很方便。