0
点赞
收藏
分享

微信扫一扫

每天学习一点点之关于 Maven 的那些事儿(一)

昨晚周会,接到了一个“任务”,对目前组内一些项目的 Maven 依赖做一个梳理。其实谈起 Maven,是一个很有意思的点,在越来越卷的 Java 行业,动不动就是“分布式、高并发、架构设计”,还得让你从 JVM 的源码来分析下 ​​synchornized​​,但很少会有人提到 Maven,其实相比那些花里胡哨的,Maven 才是真正与日常开发息息相关的“基本功”,因 Maven 引发的问题也不少见。本文主要探讨的议题就是 Maven。

一些命令

mvn help:system

这个命令会打印出所有的 Java 系统属性和环境变量。

比如有时候想知道 Java 的安装目录、Maven 的安装目录,IO 的一些临时文件目录等,都可以通过这个命令获取。

➜  ~ mvn help:system
...
===============================================================================
System Properties
===============================================================================

java.runtime.name=OpenJDK Runtime Environment
sun.boot.library.path=/Users/dongguabai/develope/openJdk/jdk8u202-b08/Contents/Home/jre/lib
java.vm.version=25.202-b08
gopherProxySet=false
java.vm.vendor=Oracle Corporation
...
===============================================================================
Environment Variables
===============================================================================
JAVA_7_HOME=/Users/dongguabai/develope/java7/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home
ZSH=/Users/dongguabai/.oh-my-zsh
LDFLAGS=-L/usr/local/opt/openssl/lib
LIBRARY_PATH=:/usr/local/opt/openssl/lib/
JAVA_MAIN_CLASS_39050=org.codehaus.plexus.classworlds.launcher.Launcher
USER=dongguabai
JAVA_HOME=/Users/dongguabai/develope/openJdk/jdk8u202-b08/Contents/Home
TERM=xterm-256color
CLASSPATH=/lib/tools.jar:/lib/dt.jar:.
MAVEN_CMD_LINE_ARGS= help:system
MAVEN_PROJECTBASEDIR=/Users/dongguabai
...

Archetype 构建

Maven 提供了 Archetype 帮助我们快速构建项目骨架。

Maven 3.x+ 可以直接执行 ​​mvn archetype:generate​​ ,会自动使用稳定版的 archetype 插件。

➜  mavenTemp mvn archetype:generate
[INFO] Scanning for projects...
...
Define value for property 'groupId': com.dongguabai
Define value for property 'artifactId': dongguabai-maven-demo
[INFO] Using property: version = 1.0-SNAPSHOT
Define value for property 'package' com.dongguabai: : 1.0-SNAPSHOT
Confirm properties configuration:
groupId: com.dongguabai
artifactId: dongguabai-maven-demo
version: 1.0-SNAPSHOT
package: 1.0-SNAPSHOT
Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /Users/dongguabai/Desktop/temp/mavenTemp
[INFO] Parameter: package, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.dongguabai
[INFO] Parameter: artifactId, Value: dongguabai-maven-demo
[INFO] Parameter: packageName, Value: 1.0-SNAPSHOT
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /Users/dongguabai/Desktop/temp/mavenTemp/dongguabai-maven-demo
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 58.184 s
[INFO] Finished at: 2021-11-23T08:26:19+08:00
[INFO] Final Memory: 15M/206M
[INFO] ------------------------------------------------------------------------

构建的过程中会让你选择 ​​groupId​​​、​​artifactId​​​ 和 ​​version​​。整个目录如下:

➜  mavenTemp tree .
.
└── dongguabai-maven-demo
├── pom.xml
└── src
├── main
│ └── java
│ └── 1
│ └── 0-SNAPSHOT
│ └── App.java
└── test
└── java
└── 1
└── 0-SNAPSHOT
└── AppTest.java

10 directories, 3 files

这个结构展示了 Maven 的一些约定:在项目的根目录中放置 pom.xml,在 ​​src/main/java​​​ 目录中放置项目的主代码,在 ​​src/test/java​​ 中放置项目的测试代码。

dependency 依赖

​<dependency>​​ 就是标识要引用的依赖。

<dependency>
<groupld>..</groupld>
<artifactld>... </artifactld>
<version>..</version>
<type> ..</type>
<scope>...</scope>
<optional>...</optional>
<exclusions>
<exclusion>
</exclusion>
</exclusions>
</dependency>

接下来介绍一下依赖中的几个关键元素。

pom.xml 元素

type

表示依赖的类型,对应于项目坐标定义的 ​​packaging​​​。大部分情况下,该元素不必声明,其默认值为 ​​jar​​。

其实 Maven 坐标除了我们常用到的 ​​groupId​​​、​​artifactId​​​ 和 ​​version​​​ 之外,还有 ​​packaging​​​ 和 ​​classifier​​​。 ​​packaging​​​ 就是定义的 Maven 项目的打包方式, ​​classifier​​ 我们不能直接定义。

scope

表示依赖的范围,这个是平时开发很常用的。

先说一下什么是 Java 中的 ​​classpath​​​,它说白了就是告诉 JVM,你从哪个路径上找 ​​class​​。

​scope​​​ 就是用来控制依赖与下面三种 ​​classpath​​ 的关系:

  • 编译 ​​classpath​​;
  • 测试 ​​classpath​​;
  • 运行 ​​calsspath​​。

Maven 主要有以下几种依赖范围:

  • ​compile​​:编译依赖范围,如果没有指定,就会默认使用该依赖范围。使用此依赖范围的 Maven 依赖,对于编译,测试和运行三种 ​​classpath​​ 都有效。
  • ​test​​​:测试依赖范围。使用此依赖范围的 Maven 依赖,只对于测试 ​​classpath​​ 有效,在编译主代码或者运行项目使用时将无法使用此依赖。典型的例子就是 Junit。
  • 说白了就是在 ​​src/main/java​​​ 目录中放置的项目的主代码是用不到的。仅对 ​​src/test/java​​ 中的测试代码有效。在项目发布的时候可以减少无用依赖的引入。
  • ​provided​​​:已提供依赖范围。使用此依赖范围的 Maven 依赖,对于编译和测试 ​​classpath​​ 有效,但在运行项目的时候无效。典型的例子就是 servlet-api,编译和测试项目时候,需要该依赖,但是在运行项目时,由于容器已经提供,就不需要 Maven 重复的引入一遍。
  • 说白了就是编译、测试、运行都要用到,但是打包的时候不要,可能外部容器已经提供了。
  • ​runtime​​​:运行时依赖范围。使用此依赖的 Maven 依赖,对于测试和运行的 ​​classpath​​ 有效,但在编译主代码时无效。典型的例子就是 JDBC 驱动实现。项目主代码的编译只需要 JDK 提供的 JDBC 接口,只有在执行测试或者运行项目时才需要实现上述接口的具体 JDBC 驱动。
  • 说白了就是这个依赖项目不会参与项目的编译,不过后期的测试和运行周期需要其参与。与 ​​compile​​​ 的区别就是跳过了编译。但是这里有几个细节要注意,这里的“不参与编译”并不是说这个依赖的内容就不编译了(我们通过 Maven 引入的包,里面的类,都是已经编译好的 ​​class​​​),是指这个依赖不参与我们主代码的编译,以上文提到的 JDBC 为例,MySQL 依赖如果是 ​​runtime​​,那么它里面的内容我们是无法使用的,只能使用 JDBC 接口,反正有点“强制使用接口”的意思。

每天学习一点点之关于 Maven 的那些事儿(一)_开发语言

optional

表示可选依赖。关于可选依赖可以参看《​​从 Spring Boot 自动装配看 Maven 可选依赖的使用​​》。

exclusions

​exclusions​​​ 里面就是一堆 ​​exclusion​​​,表示排除依赖。需要注意的是,声明 ​​exclusion​​​ 的时候只需要 ​​groupld​​​ 和 ​​artifactld​​​ ,而不需要 ​​version​​​ 元素,这是因为只需要 ​​groupld​​​ 和 ​​artifactld​​​ 就能唯一定位依赖,换句话,Maven 解析后的依赖,不可能出现 ​​groupld​​​ 和 ​​artifactld​​​ 相同,而 ​​version​​ 不同的情况。

其他问题

依赖传递

这个很好理解,比如 B 依赖了 C,A 又依赖了 B,那么 A 就间接依赖了 C(如果没有配置可选依赖的话)。

依赖范围对传递依赖的影响

上文说的是在默认 ​​scope​​​ 配置(​​compile​​)的情况,直接 B 依赖了 C,A 又依赖了 B,那么 A 就间接依赖了 C。这里 A 对 B 是第一直接依赖,B 对于 C 是第二直接依赖,A 对于 C 是间接依赖。

不同的依赖范围是会影响依赖传递的效果的,这里有一张表格:

每天学习一点点之关于 Maven 的那些事儿(一)_maven_02

第一列是第一直接依赖范围,第一行第二直接依赖范围,比如 A 对 B 第一直接依赖,范围是 ​​test​​​,B 对 C 第二直接依赖,范围是 ​​compile​​​,根据上面的表格,A 对 C 对间接依赖范围就是 ​​test​​。

依赖调解

依赖调解就是来解决这样的问题:A->B->C->D(1.0),A->M->D(2.0),根据依赖传递,A 同时依赖了两个版本不同的 D。那么 Maven 解析后 A 到底依赖 D 的哪个版本呢?

Maven 有两个依赖调节原则(分先后):

  1. 路径最近优先
    a. 比如上面的例子,A 距离 D(2.0)更近,所以 Maven 会解析D(2.0)。
  2. 第一声明优先
    a. 还有一种情况 A->B->D(1.0),A->M->D(2.0),A 距离两个版本的 D 的距离是一样的。这时候就看在 pom.xml 中依赖声明的顺序,顺序最靠前的那个依赖优胜。如果 B 的依赖声明在 M 之前,那么D(1.0)就会被解析。

平时开发中出现的依赖冲突就与依赖调解有关,其实就是因为 Maven 选出来的版本对某些依赖不适用。

References

  • 《Maven 实战》
  • ​​https://mp.weixin.qq.com/s?__biz=MzU1OTgyMDc3Mg==&mid=2247484104&idx=1&sn=badb7959e88bf20a7dbf409d972da712&chksm=fc103843cb67b155a78e1d1ee233e341fc2805fb4549d8c20536e2e2280c8db4b7a142456b5d&token=2068452739&lang=zh_CN#rd​​

每天学习一点点之关于 Maven 的那些事儿(一)_xml_03


举报

相关推荐

0 条评论