问题描述
官方 Maven – Maven in 5 Minutes 文档,其展示 Maven 快速开始的方法;
该笔记将记录:我们将围绕该文档,学习 Maven 的概念术语,形成 Maven 的基本认识;
解决方案
我们摘抄 Maven in 5 Minutes 文档的部分内容,以便于逐步展开对概念术语的学习;
第一步、创建项目
mvn archetype:generate \ -DgroupId=com.mycompany.app \ -DartifactId=my-app \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DarchetypeVersion=1.4 \ -DinteractiveMode=false
目录结构:
my-app |-- pom.xml `-- src |-- main | `-- java | `-- com | `-- mycompany | `-- app | `-- App.java `-- test `-- java `-- com `-- mycompany `-- app `-- AppTest.java
pom.xml
<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> // POM Model 版本;针对 Maven 3 版本,固定写法; <groupId>com.mycompany.app</groupId> // 项目所属组; <artifactId>my-app</artifactId> // 项目唯一标识; <version>1.0-SNAPSHOT</version> // 项目版本;SNAPSHOT 表示快照,即开发中,仅有语义; <properties> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> // 表示该依赖仅对 test 有效,如果在其他地方引用 JUnit,将产生错误; </dependency> </dependencies> </project>
第二步、编译项目
# mvn compile [INFO] Scanning for projects... [INFO] [INFO] ----------------------< com.mycompany.app:my-app >---------------------- [INFO] Building my-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /tmp/demo/my-app/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /tmp/demo/my-app/target/classes [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.018 s [INFO] Finished at: 2022-12-18T19:11:05+08:00 [INFO] ------------------------------------------------------------------------
Maven 本身并不具备编译功能,针对的代码的编译需要调用插件来完成,而插件版本与 JDK 有关联关系的:
// 针对 Java 11 环境,使用 maven-compiler-plugin 3.8.1 版本; <properties> <maven.compiler.release>11</maven.compiler.release> </properties> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> </plugin> </plugins> </pluginManagement> </build> // 部分语法对 Java 版本也有要求,也可以进行修改 <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> </plugins> </pluginManagement> </build>
第三步、测试项目
# mvn test [INFO] Scanning for projects... [INFO] [INFO] ----------------------< com.mycompany.app:my-app >---------------------- [INFO] Building my-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/3.0.2/maven-resources-plugin-3.0.2.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar (245 kB at 469 kB/s) [INFO] [INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app --- Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-component-annotations/1.6/plexus-component-annotations-1.6.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-interpolation/1.24/plexus-interpolation-1.24.jar (79 kB at 70 kB/s) [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /tmp/demo/my-app/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app --- Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-component-annotations/1.7.1/plexus-component-annotations-1.7.1.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-compiler-javac/2.8.4/plexus-compiler-javac-2.8.4.jar (21 kB at 27 kB/s) [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /tmp/demo/my-app/target/classes [INFO] [INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ my-app --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /tmp/demo/my-app/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to /tmp/demo/my-app/target/test-classes [INFO] [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ my-app --- Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/maven-surefire-common/2.22.1/maven-surefire-common-2.22.1.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.22.1/surefire-junit4-2.22.1.jar (85 kB at 278 kB/s) [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.mycompany.app.AppTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.037 s - in com.mycompany.app.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 23.773 s [INFO] Finished at: 2022-12-18T18:49:03+08:00 [INFO] ------------------------------------------------------------------------
第三步、打包项目
# mvn package [INFO] Scanning for projects... [INFO] [INFO] ----------------------< com.mycompany.app:my-app >---------------------- [INFO] Building my-app 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.0.2/maven-jar-plugin-3.0.2.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.0.2/maven-jar-plugin-3.0.2.jar (27 kB at 75 kB/s) [INFO] [INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ my-app --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /tmp/demo/my-app/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ my-app --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ my-app --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] skip non existing resourceDirectory /tmp/demo/my-app/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ my-app --- [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.mycompany.app.AppTest [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.039 s - in com.mycompany.app.AppTest [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] [INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ my-app --- Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-archiver/3.1.1/maven-archiver-3.1.1.pom ... Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.11/commons-compress-1.11.jar (426 kB at 370 kB/s) [INFO] Building jar: /tmp/demo/my-app/target/my-app-1.0-SNAPSHOT.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.337 s [INFO] Finished at: 2022-12-18T18:52:13+08:00 [INFO] ------------------------------------------------------------------------
Coordinate and Dependency
Coordinate
坐标,其通过 groupId artifactId version packaging classifier 来描述某个唯一的构件,而后 Maven 便能通过坐标到仓库中下载该构件;
通过 groupId、artifactId、version 三个属性就能定位一个构件;
示例:
<groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging>
groupId:定义当前 Maven 项目隶属的实际项目。首先,Maven 项目和实际项目不一定是一对一的关系。比如 SpringFramework 这一实际项目,其对应的 Maven 项目会有很多,如 spring-core 、spring-context 等。这是由于 Maven 中模块的概念,所以一个实际项目往往会被划分成很多模块。其次,groupld 不应该对应项目隶属的组织或公司。原因很简单,一个组织下会有很多实际项目,如果 groupld 只定义到组织级别,而后面我们会看到,artifactld 只能定义 Maven 项目 (模块) ,那么实际项目这个层将难以定义。最后,groupld 的表示方式与 Java 包名的表示方式类似,通常与反向域名对应;
artifactId:该元素定义实际项目中的一个 Maven 项目 (模块) ,推荐的做法是使用实际项目名称作为 artifactld 的前级。加入 artifactId 是 nexus-indexer,使用了实际项目名 nexus 作为前缀,这样做的好处是方便寻找实际构件。在默认情况下,Maven 生成的构件,其文件名会以 artifactld 作为开头,如 nexus-indexer-2.0.0.jar,使用实际项目名称作为前缀之后,就能方便从一个 lib 文件夹中找到某个项目的一组构件;
version:该元素定义 Maven 项目当前所处的版本。需要注意的是,Maven 已定义一套完成的版本规范,以及快照 (SNAPSHOT) 的概念;
packaging:该元素定义 Maven 项目的打包方式。首先,打包方式通常与所生成构件的文件扩展名对应,如上例中 packaging 为 jar,最终的文件名为 .jar(未定义 packaging 时的默认行为),而使用 war 打包方式的 Maven 项目,最终生成的构件会有一个 .war 文件,不过这不是绝对的。其次,打包方式会影响到构建的生命周期,比如 jar 打包和 war 打包会使用不同的命令;
classifier:该元素用来帮助定义构建输出的一些附属构件。附属构件与主构件对应,例如主构件是 nexus-indexer-2.0.0.jar,该项目可能还会通过使用一些插件生成如 nexus-indexer-2.0.0-javadoc.jar 、nexus-indexer-2.0.0-sourees.jar 这样一些附属构件,其包含了 Java 文档和源代码。这时候,javadoc 和 sources 就是这两个附属构件的 classifier。这样,附属构件也就拥有了自己唯一的坐标。还有一个关于 classifier 的典型例子是 TestNG,TestNG 的主构件是基于 Java 1.4 平台的,而它又提供了一个 classifier 为 jdk 的附属构件。注意,不能直接定义项目的 classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成;
构件名称与坐标的关系:artifactId-version[-classifier].packaging
Dependency
示例:
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
示例如上,依赖将通过如下参数定义:
1)groupId、artifactId、version:坐标
2)type:类型,默认为 jar 类型;
3)scope:依赖范围;
4)optional:标记该依赖是否为可选依赖;
5)exclusions:其用于排除某个传递依赖;
Plugin and Goal
Plugin
诸如 maven-resources-plugin 、maven-surefire-plugin 等等,都是插件,Maven 将通过这些插件来完成具体的任务;
maven-surefire-plugin,是 Maven 用于执行测试的插件;
Goal
诸如 default-resources、default-testResources 等等,被成为 Goal(目标?),理解为由 Plugin 提供的具体功能即可;
[INFO] — maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ my-app —
执行 maven-compiler-plugin 插件提供的 default-testCompile 目标
Suffice it to say for now that a plugin is a collection of goals with a general common purpose.
所以,实际的 mvn 构建,就是各个 Goal 的依序执行;
Phase、Goal
Phase
针对 mvn package 命令,其 package 是众多 Phase 之一,属于 Build Lifecycle(构建生命周期);
Goal
当给定一个 Phase 时,Maven 执行序列中的每个 Phase,直到并包括定义的 Phase。假如我们按照上述顺序定义 Phase,当执行 package 阶段时,实际执行的阶段是:validate、compile、test、package;
Phase 实际上被映射到底层的多个 Goal,但每个 Phase 所执行的 Goal 取决于项目类型;
Phase and Goal
例如 mvn clean dependency:copy-dependencies package 命令,Phase 与 Goal 能够在命令行中顺序执行;
SNAPSHOT
不同的模块,由不同人员负责开发,但是模块之间存在依赖关系,而开发工作又是同时进行的。
此时上游可以发布快照版本,下游使用该模块的快照版本。同时,快照版本不需要更新版本号,其在发布是会包含时间戳信息,此时下游使用时将自动根据时间戳更新。
快照版本只应该在组织内部的项目或模块间依赖使用,因为这时,组织对于这些快照版本的依赖具有完全的理解及控制权。项目不应该依赖于任何组织外部的快照版本依赖, 由于快照版本的不稳定性,这样的依赖会造成潜在的危险。也就是说,即使项目构建今天是成功的,由于外部的快照版本依赖实际对应的构件随时可能变化,项目的构建就可能由于这些外部的不受控制的因素而失败;
当项目经过完善的测试后需要发布的时候,就应该将快照版本更改为发布版本。例如, 将 2.1-SNAPSHOT 更改为 2.1,表示该版本已经稳定,且只对应了唯一的构件。相比之下, 2.1-SNAPSHOT 往往对应了大量的带有不同时间戳的构件,这也决定了其不稳定性;
参考文献