本文共 10794 字,大约阅读时间需要 35 分钟。
项目里一直用的 maven,几乎天天和这个“熟知”的工具打交道,但是,最近我发觉自己对 maven 了解的还不够,例如,什么是 goal?什么是 phase?等等。趁着最近有时间,把大致看了一遍,并且做做笔记,也就形成了这篇博客。
本文主要讲解以下内容:
这两个问题,很多文章都有说到,但是,大部分都是翻译了官网的这句笼统的话,看了和没看一样。
Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
以下是我的个人总结,可能稍微好理解一点。
首先,maven 是一个工具,用来帮助我们简化、标准化项目的构建,主要分成四点:
maven:3.6.3
操作系统:win10
JDK:8u231
进入,根据自己的操作系统和 JDK 选择合适的 maven 版本,这里我们也可以选择下载二进制安装包或者源码包。这里我选择版本 3.6.3 的二进制安装包。
将下载的 .zip 文件解压,可以看到以下的目录结构:
进行到这一步可以说 maven 已经安装好了,只是我们还需要进行简单的配置。
首先,因为 maven 是由 Java 编写,需要 JDK 才能运行,所以,我们必须保证配置好了 JAVA_HOME 的环境变量。这个我就不展开了。
然后,将解压文件中的 bin 目录添加到 Path 的环境变量中(我的电脑(右键属性)->高级系统设置->环境变量),如下所示:
在任一位置打开命令行,输入:mvn -v
或mvn --version
,显示以下内容,说明安装完成。
安装完成后,下面通过一个简单的例子来模拟构建 maven 项目的过程。
cmd 进入到你想要存放项目的文件夹,输入以下命令:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
在这个命令中,archetype:generate
为一个goal
(后面展开分析),而后面那些都是执行这个goal
所需的参数。
如果是你的 maven 是刚安装的,这个命令可以会执行比较久,因为 maven 需要将所需的软件包或其他文件下载到你的本地仓库(默认在 ${user.home}/.m2/repository 目录下)。如果出现连接超时等情况,可以尝试多执行几次(可以将settings.xml
的仓库镜像配置为其他地址来提高下载速度)。
执行完这个命令,可以看到指定文件夹下生成了一个 maven 项目,cd my-app
,它的文件结构如下:
my-app|-- pom.xml`-- src |-- main | `-- java | `-- com | `-- mycompany | `-- app | `-- App.java `-- test `-- java `-- com `-- mycompany `-- app `-- AppTest.java
其中,
src/main/java
目录用来放项目的代码,这些代码将会被编译并打包。
src/test/java
目录用来放项目的测试代码,这些代码仅进行编译运行,不打包。
如果我们想要添加一些资源文件,可以在src/main
目录下创建一个resource
目录,这些资源最终也会被打包到项目根目录下。
my-app|-- pom.xml`-- src |-- main | |-- java | | `-- com | | `-- mycompany | | `-- app | | `-- App.java | `-- resources | `-- META-INF | `-- application.properties `-- test `-- java `-- com `-- mycompany `-- app `-- AppTest.java
pom.xml
则包含了构建这个项目的配置信息,后面我们将重点介绍这个文件。
4.0.0 com.mycompany.app my-app 1.0-SNAPSHOT my-app http://www.example.com UTF-8 1.7 1.7 junit junit 4.11 test
上面生成的项目中包含了两个类,其中,App.java 有一个打印 Hello World! 的 main 方法。接下来我们尝试将项目打包并运行。
maven 将构建和部署项目的过程定义成了很多个有序的phase
(可以理解为步骤),我们可以执行以下命令来打包项目。
mvn package
这个命令将执行package
之前的phase
,如validate
、compile
、test
等,以及执行package
本身。严格上来讲,实际上执行的不是phase
,而是绑定在这些phase
上的goal
,后面会展开讲解。
执行成功后,项目根目录生成了一个 target 文件夹,里面就有打包好的 my-app-1.0-SNAPSHOT.jar。
my-app\target│ my-app-1.0-SNAPSHOT.jar│├─classes│ └─com│ └─mycompany│ └─app│ App.class│├─generated-sources│ └─annotations├─generated-test-sources│ └─test-annotations├─maven-archiver│ pom.properties│├─maven-status│ └─maven-compiler-plugin│ ├─compile│ │ └─default-compile│ │ createdFiles.lst│ │ inputFiles.lst│ ││ └─testCompile│ └─default-testCompile│ createdFiles.lst│ inputFiles.lst│├─surefire-reports│ com.mycompany.app.AppTest.txt│ TEST-com.mycompany.app.AppTest.xml│└─test-classes └─com └─mycompany └─app AppTest.class
接下来就是运行 jar 包了,在命令行输入
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
执行完成,可以看到打印:
Hello World!
构建生命周期( build lifecycle )是 maven 的核心理论基础之一,它将项目的构建过程标准化。
maven 有三个独立的生命周期:
一个生命周期包括了许多具体的phase
(可以理解为步骤),如下:
一般我们接触比较多的是默认生命周期,它主要包括以下过程:
validate
- 校验项目是一个正确的 maven 项目compile
- 编译代码test
- 测试src/test/java
中的方法,src/test/java
的内容仅作为测试使用,不会进行打包或部署package
- 将项目打包为可执行的 jar、war 等二进制软件包。install
- 将软件包安装到本地仓库deploy
- 将软件包部署到远程仓库除了这几个常用的phase
,还有initialize
、generate-sources
、process-sources
等等,需要注意一点,当我们执行某个阶段的命令时,类似 pre-*
, post-*
, or process-*
的阶段一般只是产生中间结果,并不会对最终构建结果产生影响。
pre-clean
- 在清理项目前执行一些东西
clean
- 清理项目,例如删除 target 包
post-clean
- 在清理项目后执行一些东西
pre-site
- 在生成站点文档前执行一些东西
site
- 生成站点文档
post-site
- 在生成站点文档后、部署站点文档前执行一些东西
site-deploy
- 部署站点文档
在下面这个 maven 命令中,archetype:generate
称之为一个goal
,后面那些都是执行这个goal
所需的参数。
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
至于goal
,对应的是某个插件的某个方法,执行这个命令,我们可以在界面中看到执行的是maven-archetype-plugin
插件的generate
方法
和上面这个命令不同,下面的这个命令我们并没有传入goal
,传入的是phase
。
mvn clean
执行这个命令,可以看到,这个命令也是执行了插件的方法,换句话来讲,就是执行了goal
。
这里就涉及到一个很重要的概念:当我们在命令中指定了phase
,执行的并不是phase
本身,而是绑定在phase
上面的goal
,绑定的goal
数量可以是一个也可以是多个。
下面是官方给的部分binding
,phase
和goal
的绑定关系主要和项目的packaging
配置有关。
Phase | plugin:goal |
---|---|
process-resources | resources:resources |
compile | compiler:compile |
process-test-resources | resources:testResources |
test-compile | compiler:testCompile |
test | surefire:test |
package | ejb:ejb or ejb3:ejb3 or jar:jar or par:par or rar:rar or war:war |
install | install:install |
deploy | deploy:deploy |
这些绑定关系,在${MAVEN_HOME}\lib\maven-core-3.6.3\META-INF\plexus\ default-bindings.xml
中定义。
当我们执行phase
命令时,在执行指定phase
之前,会先有序地执行指定phase
之前的phase
以及它本身。例如,我执行mvn package
,会出现下面的信息:
在package
之前的phase
,包括compile
、test
等都会被执行,而且是有序的。
与pom.xml
不同,settings.xml
用于全局地配置 maven,而不是配置具体的项目。我们可以在${maven.home}/conf/
目录下找到这个文件。
settings.xml
文件主要包含以下节点:
这个文件的配置可以参考。这里我补充下servers
、mirrors
、profiles
这三个节点的内容。
servers
用于配置仓库(包括下载项目和部署项目的仓库)的认证授权信息,例如,用户密码等。
在具体项目中,我们可以在pom.xml
中的repositories
、pluginRepositories
和distributionManagement
节点配置用于下载项目和部署项目的仓库,但是我们不能把认证授权的信息放在pom.xml
文件中,于是servers
就发挥了作用。
... ... server001 my_login my_password ${user.home}/.ssh/id_dsa some_passphrase 664 775
Mirrors
用于配置下载项目的仓库镜像。前面说过,国内使用 maven 的中央仓库下载项目比较慢,甚至会出现超时失败的情况,这时,我们就可以通过配置镜像来提高传输速度。在此之前,我们需要区分镜像和仓库两个概念,以下这篇文章作出了很好的解释。
在下面这个例子中,我们使用阿里云的镜像来请求 maven 的中央仓库,注意,mirror
的mirrorOf
节点必须指定仓库的 id,当然,这里还支持多种形式。例如,<mirrorOf>*</mirrorOf>
表示匹配所有远程仓库;<mirrorOf>repo1,repo2</mirrorOf>
表示匹配仓库 repo1 和 repo2,使用逗号分隔多个远程仓库;<mirrorOf>*,!repo1</miiroOf>
匹配所有远程仓库,repo1 除外,使用感叹号将仓库从匹配中排除。
... alimaven aliyun maven http://maven.aliyun.com/nexus/content/groups/public/ central ... ... central Central Repository https://repo.maven.apache.org/maven2 default false
补充下,mirror
节点对repositories
、pluginRepositories
和distributionManagement
均生效。
profiles
:提供了一组可选的配置,我们可以根据不同的环境选择激活哪一套配置,它包括:activation
、 repositories
、pluginRepositories
和properties
四个节点。其中,activation
节点用于配置该profile
在什么环境下才能激活。
... ... ... test false 1.5 Windows XP Windows x86 5.1.2600 mavenVersion 2.0.3 ${basedir}/file2.properties ${basedir}/file1.properties ${user.home}/our-project codehausSnapshots Codehaus Snapshots false always warn true never fail http://snapshots.maven.codehaus.org/maven2 default ...
注意,如果settings.xml
的某个profile
被激活,那么,它的配置将覆盖pom.xml
中相同 id 的仓库以及相同名称的 property。
pom.xml
几乎包含了对 maven 项目的所有描述信息,包括项目的坐标、依赖关系、构建配置等等。这个文件非常重要,官网有这么一句话,在 maven 的世界里,一个完整的项目可以不包含任何的代码,而只需要一个pom.xml
。
pom.xml
的节点如下:
4.0.0 ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
关于pom.xml
文件的内容,就不详细展开了,可参考。这里介绍下两个比较重要的概念。
scope
是dependency
的子节点,用于设置以下两个内容:
maven 提供了五种scope
给我们选择,如下。
scope | 编译期 | 运行期 | 测试编译期 | 测试运行期 | 依赖传递 |
---|---|---|---|---|---|
compile | √ | √ | √ | √ | √ |
provided | √ | × | √ | √ | × |
runtime | × | √ | × | √ | √ |
test | × | × | √ | √ | × |
system | √ | √ | √ | √ | √ |
通过dependency
的子节点optional
可以改变传递性。system对应的依赖不会从仓库获取,而是从systemPath
指定的路径中获取。
pom 文件可以通过<parent>
节点来继承其他项目的配置信息,而且,和 Java 的对象默认继承 Object 一样,pom 文件默认会去继承 super pom,该 pom 文件的内容见:
当我们的项目需要依赖某个在中央仓库中不存在的依赖,例如,oracle
的驱动包,我们可以采用三种解决方案:
mvn install:install-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
deploy:deploy-file -Dfile=non-maven-proj.jar -DgroupId=some.group -DartifactId=non-maven-proj -Dversion=1 -Dpackaging=jar
system
作用域指定包的路径。some.group non-maven-proj 1.0 system ${java.home}/lib/non-maven-proj.jar
相关源码请移步:
本文为原创文章,转载请附上原文出处链接: