idea maven dependencies报红_使用maven构建的工程的实践心得

cbdfd113650edb213790a79fbc39447c.gif

点击上方蓝色字体,关注我们

cee1d31e43b640aaa0cd9137025f531e.png

前言

Maven 是众多 Java 开发者所青睐的构建工具之一。还记得未接触Maven之前,所有的依赖包都是从网站找的,找了某个jar包后,又要去搜索它的依赖,如此循环往复。耗费大量的时间和精力,很多初识Maven的开发者可能会存在很多疑惑,没有系统的摸清Maven的用法,所以本篇文章想要介绍在Maven领域的一切实践思路,让读者更好的领略 Maven 的精髓。

maven的兼容性矩阵

eea85d62e0889454f1e57ef13acd37b8.png

maven的兼容性可能一直被大多数人忽视,确实属于比较小众的知识点,可偏偏就被笔者遇到了。事情起源于一次系统重装,重装后高高兴兴的下载的开发工具idea和maven。但是由于两者的兼容性问题,导致一直打包报错。整个经历过程如下:

6f8880b21aeaeb2b0a6f3172dcd29723.png

高高兴兴的下载Idea 2019.2.2 和 Maven 3.6.2。

db58c2e375bf2b51c992a1576c660ff4.png

安装配置,一气呵成!

 36010de241bbc0ac8c99109395aea33e.png

打包报错????

80c50eeb982f44c244ee1381fcc23401.png

配置没问题,环境变量没问题,到底是哪里出了问题?

9329ed0dfb240dbd001c01a9d8f9d868.png

崩溃!换3.6.0版本的Maven

31ae8e9ca2e81975b7b0b515c5ff4f22.png

再次打包成功!

看来是Idea 和 maven的兼容性问题导致的。果然,每次下载最新版本,总能能遇到一些坑。后续通过 idea官网 也印证了这个兼容性的bug。

5cb4ba1289359edb681a433d7bcc656a.png

官方已经在IntelliJ IDEA 2019.2.3 及 2019.2.2.3版本后修复了这个问题。 Maven 和 JDK 的兼容性,这个只需要注意满足最低版本即可,由于现在普遍使用的是Java 8,所以一般不会有什么问题,参考整理的兼容性矩阵如下:

ace0c4685d4144687c23c0bafa56d4ef.png

认识 maven 的生命周期

相信一提到Maven,大家脑海里浮现出来它的第一个作用,就是用来打包,但是经常“用完即走”的我们真的到了需要认认真真梳理一下Maven的相关概念了。 Maven为我们提供了一套自动化的构建方案,从最基础的清理、编译代码、运行测试到打包和部署。这一系列过程称之为它的生命周期。

61067e8fc9e083ee013b6b2756dd19f0.png

默认情况下,Maven有三套相互独立的生命周期,分别是 clean、default 和site。每个生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。每个阶段的内部由 插件的 Goals(目标)组成。 可以这样理解,其实phase就是goal的容器。真正被执行的都是goal (目标)。 phase (阶段)被执行时,实际执行的都是被绑定到该 phase (阶段)的一系列goal。 而 goal 与 goal 之间是独立的。单独执行一个goal不会导致其他goal被执行。

深入理解 maven 的生命周期

maven使用一个名为 components.xml 的配置文件来描述其架构的组织结构,以我本地的maven 3.6.0版本为例。不同版本的maven, 配置文件的路径相似。
apache-maven-${version}\lib\maven-core-${version}.jar
\META-INFO\plexus\conponents.xml
这里我们针对default生命周期来作详细的了解:

b29e7225b55fa6f35b8ddbd5e1815961.png

下图是我整理出default生命周期的所有 phase (阶段)。

c18715972cfe9e502b141d9818ea7694.png

mvn  { 例如: mvn package }
可以看到 当我们执行一个 简单的 mvn package 时,maven其实内部按照顺序流转了很多的phase (阶段)。 那如何来理解 Goal (目标)呢? 我们前面说过,一系列的 Goal (目标) 组成 了 phase (阶段)。 相信你用过maven的很多插件吧?这里我以 打 jar包的插件为例来介绍:

cca30850a8e87ffb05af0b75f9b1548c.png

聪明的你看到这个图可能已经猜到了,jar:后面的一系列单词 就是Goal (目标)。 解释一下,goal 其实是由存在于Maven的插件plugin提供的一个个小的功能程序。你可以根据自己的实际需求,灵活的来组装他们,实现各种定制的功能。执行格式为:
 mvn [插件名称]:[goal的名称]
再举一个实际案例,在打包时,分离出三方的依赖包。我们会使用到一个叫maven-dependency-plugin的插件。

0378c36cc556d2f182e49ce861ddc75f.png

上图表明,我制定了一个copy-dependencies 的目标,在 prepare-package 阶段执行。也就是在执行package打包之前,拷贝依赖到当前工程的lib文件夹下面。 直接通过执行命令也能达到上述的效果:
mvn clean dependency:copy-dependencies package

a397823d09ebaf7c0313aa84732468e8.png

Maven 管理项目的依赖

Maven提供的依赖管理,是其中的一大核心功能。就像文章开始所说的,我们需要一个jar包,这个jar包又依赖于另外的jar包,这种依赖关系会不断的传递,直至最后一个没有任何其他依赖包。 Maven就是通过自动去发现和包含这些传递的依赖,避免了我们人工的去搜索下载。 这个传递依赖的层级是没有限制的,在使用过程中需要注意的一点是,循环依赖的问题。

8bbef1dfd1b0ee85347fde6b5095b2c9.png

遭遇到循环依赖如何解决呢?Maven 提供了一个 build-helper-maven-plugin 插件。其原理是:将三个模块合并成一个中间的工程,临时编译出模块D,然后三个模块再分别依赖D 进行编译。 不推荐使用这种办法,这会导致我们的项目工程结构很混乱,只能作为临时规避的一个措施来使用。 还有一种办法是通过分析依赖,从根本上消除掉循环依赖。常用的分析依赖的命令有:
mvn dependency:help
mvn dependency:analyze
mvn dependency:tree
mvn dependency:tree -Dverbose
除了使用命令查看依赖树,常见的办法还有:
  • 在项目启动时把所有的加载的 jar 包都打印出来,添加 VM 参数 -verbose:class 。通过打印的信息确认是否正确的 jar 包被依赖。
  • 有的时候仅仅通过 mvn dependency:tree 可能无法快速的发现冲突,这个时候就可以尝试使用 Enforcer 插件,这个插件也可以自定义许多规则,我们可以使用 dependencyConvergence 规则。来保证所有的依赖都使用相同的版本。
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-enforcer-pluginartifactId>
<version>1.3.1version>
<executions>
<execution>
<id>enforceid>
<configuration>
<rules><DependencyConvergence />rules>
configuration>
<goals>
<goal>enforcegoal>
goals>
execution>
executions>
plugin>
plugins>
build>

Maven 管理的依赖范围

依赖范围主要是用来限制各个包的传递性。并且影响我们最终的构建结果。在引入依赖时,通过 标签来指定依赖的范围。

ac81145b55a4999256cbaef3e1638b19.png

Maven 内部为我们提供了 6种依赖的范围:
  • compile:缺省值,编译、测试都有效。
  • provided:编译,测试都有效,但是在运行时并不会加入。例如Servlet API,因为Web容器本身有,如果加入就会出现冲突。
  • runtime:测试、运行有效,表明这个依赖项不是编译所必须的,但是是执行时需要的。需要位于运行时的类路径中。
  • test:仅测试时有效,这个范围不是可传递的。
  • system:与本机相关,可移植性差。
  • import:导入的依赖范围,仅适用在dependencyManager中,表示从其他pom导入dependency配置。

dependencyManagement 作用

上面介绍的依赖范围都比较好理解,最后一个import范围涉及到一个dependencyManagement,这里我们具体解释一下他和dependencies 的区别。 dependencyManagement 主要有两个作用:
  • 一是集中管理项目的依赖项
  • 二是控制使用的依赖项的版本
 如果有多个子模块依赖相同的包,那么放在  中来管理。 使用 dependencyManagement 声明依赖,在项目中并不会实际引入,因此子项目需要显示声明需要用的依赖。 当不在子项目中声明依赖的时候,是不会从父项目中继承下来的。 只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父 pom 如果子项目中指定了版本号,那么会使用子项目中指定的 jar 版本 而对于 dependencies : dependencies 即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项。父工程使用 dependencyManagement 假引用,目的是管理版本号。dependencies 用于实际上需要引入的工程,这些工程如果继承于父工程会找到对应的版本号。

Maven 的仓库分类

从大类来看,Maven仓库主要分为两大类,一是本地仓库,二是远程仓库。 本地仓库,主要是用来存储从远程仓库下载的插件和 jar 包,当项目需要使用 插件 或 jar 包时,会优先从本地仓库中查找。 远程仓库,是当无法在本地仓库中查找到包时,会默认去远程仓库下载。 远程仓库包括了,中央仓库、镜像仓库和私服。

b9268d649b327d6731f829cc4f30f98f.png

而在配置了多个 Maven 仓库查找依赖的顺序大致如下:
  • 1) 在本地仓库中寻找,如果没有则进入下一步。
  • 2) 在全局配置的私服仓库(settings.xml中配置的并有激活)中寻找,如果没有则进入下一步。
  • 3) 在项目自身配置的私服仓库(pom.xml)中寻找,如果没有则进入下一步。
  • 4) 在中央仓库中寻找,如果没有则终止寻找。

Maven 内置的变量

${basedir} 表示项目根目录(即pom.xml文件所在目录)${project.build.directory} 构建目录,缺省为target目录${project.build.outputDirectory} 构建过程输出目录,缺省为target/classes${project.build.finalName} 产出物名称比如jar包,缺省为${project.artifactId}-${project.version}${project.packaging} 打包类型,缺省为jar${project.xxx} 当前pom文件的任意节点的内容${env.xxx} 获取系统环境变量。例如,"env.PATH"指代了$path环境变量(在Windows上是%PATH%)。${settings.xxx} 指代了settings.xml中对应元素的值。
Java System Properties: 所有可通过java.lang.System.getProperties()访问的属性都能在POM中使用
比较实用的,例如 ${JAVA_HOME}。

总结

本文主要以一个兼容性问题为引,介绍了maven的兼容性矩阵,及其三大默认的生命周期,了解了阶段(phase) 和 Goals(目标)的概念。对比了dependencyManagement和dependencies的区别。以及Maven仓库的分类、依赖管理的范围及内置实用变量的学习。希望能帮助到大家。 ae2215aa8c98517f136659c172c6e13d.gifJava垃圾收集器一网打尽, ZGC 和 Shenandoah 听说过吗动图帮你理解【Java垃圾收集机制】一文搞懂 JVM 架构和运行时数据区 (内存区域)为什么你的 Spring Task 定时任务没有定时执行?搞java开发,看懂JVM的GC日志真的很重要一次容器化springboot程序OOM问题探险Java 应用性能调优的一些实践可视化界面在线生成JVM参数 426b88fc34aa67f5692461997bfd81c9.png我就知道你“在看” 3f31b601461141872c84abeede0bf9e2.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值