Maven实战与原理分析:Maven在应用中的常见问题整理

Posted 黄小斜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Maven实战与原理分析:Maven在应用中的常见问题整理相关的知识,希望对你有一定的参考价值。

Maven下载依赖失败的解决方式

那今天这期视频我们将带来一个 IDEA 中 Maven 设置的小技巧。

那这个技巧可以说非常有用,学会设置之后,再也不用担心 maven 依赖下载变慢的问题。

Maven 设置

当我们下载安装 Maven 之后,如果不修改 maven 中 setting 文件的。

那默认情况下, Maven 远程中央仓库地址为是个国外的地址。

http://repo1.maven.org/maven2
复制代码

那我们国内的网络,因为神秘的力量的影响,访问国外的地址就会比较慢。

如果你的网络情况很差,那么用默认的地址下载依赖资源就会很慢,有可能运行到一半下载失败。

那这种情况下,我们可以通过设置 Maven 仓库镜像地址从而解决这个问题。

那国内可以用的 Maven 的镜像地址其实有很多,比如说阿里云

https://maven.aliyun.com/mvn/guide
复制代码

网易:

https://mirrors.163.com/.help/maven.html
复制代码

腾讯云:

https://mirrors.cloud.tencent.com/help/maven.html
复制代码

那这个 Maven 镜像地址配置其实很简单。

我们以阿里云为例,打开阿里云网站的配置教程。

我们只需要复制这个配置到我们安装的 Maven 的 Setting 文件中就好了。

<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>
复制代码

然后回到 IDEA 中,打开 Maven 设置页面,设置一下Setting.xml 文件地址,然后点击这个 Override 按钮,选择覆盖默认地址。

点击保存,那再次下载相关依赖,你就会发现速度会快了很多。

但其实这里有一个问题,这个 Maven 设置仅对当前这个项目有效。

如果你要新建一个 Maven 的项目,你会发现 IDEA 中 Maven 设置又被初始化。

也就是说,你每次新建 Maven 项目,都需要重新在 IDEA 中设置 Maven。

这就比较烦了!!!

之前好几次在 IDEA 中运行 Maven 项目,发现打包下载依赖特别慢。

最后一看,原来又是 Maven 设置重新初始化导致。

这种情况下你们应该也碰到过吧?

那好了,接下来小黑哥教大家两个解决办法。

默认设置

那我们使用 IDEA 新建 Maven 项目,IDEA 中 Maven 默认设置如下:

IDEA 其实使用的是 Maven 默认约定地址。

它默认会从系统用户 .m2 文件夹下面读取 setting.xml 这个文件。

那如果你是 mac 用户,setting.xml 这个文件地址路径如下:

/Users/$user/.m2/setting.xml
复制代码

那如果你是 windows 用户,setting.xml 这个文件地址路径如下:

C:\\Users\\$user\\.m2\\setting.xml
复制代码

这个 $user指的是你当前系统登录的用户名。

那第一个解决办法,其实就非常简单。

我们把自己的 Setting 文件移动这个上面说的地址下面不就好了。

【打不过,就加入表情包】

打不过,就加入么。

那复制过去以后,新建工程就会加载默认位置的 setting.xml

那这个文件我们是修改过,里面仓库地址是我们配置的镜像地址,那下载依赖就会很快。

我呢其实使用就是这种解决方案。

IDEA 新工程默认设置

那有些小伙伴可能就想把 setting.xml放在自定义一个地方,那下面教大家另外一个解决办法。

我们在 IDEA 修改一些设置,一般情况下只会对当前这个项目生效,比如说这个 Maven 设置。

那 IDEA 其实是可以修改新建项目的默认配置。

修改设置之后,以后新建的所有项目都会按照这个设置。

那这个设置位于:

File | New Projects Settings | Settings/Preferences for New Projects.
复制代码

那如果你的 IDEA 版本比较低,那这个设置地方名称跟上面就不一样了。

File | Other Setting | Default Settings
复制代码

打开这个选项,修改 Maven 的设置,然后点击完成。

这样我们新建的项目都会使用刚才修改的这个配置。

重新再创建一个项目,打开项目工程里面的,这里 Maven 默认设置已经被更改,那后续我们就就不需要再手动更改配置了。


作者:楼下小黑哥
链接:https://juejin.cn/post/6963987209452159006
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Maven依赖冲突问题排查经验

木小丰 2021年11月13日 1,586次浏览

一、背景

在日常的开发中,排查问题是一个合格Java开发者的的基本能力。对于常见的NullPointerException,NoClassDefFoundError等问题一般通过google直接就能找到答案。

不过还有一些异常情况不是那么直观,google一般搜不到有效的信息,就需要深入研究排查。新人遇到这类问题,往往一脸懵逼,不知如何下手,请教高手,高手如果只是简单指导一个方向,新人踩过几个坑没解决后会更加沮丧。甚至怀疑自己遇到一个神秘的无法解决的bug。有经验的开发往往也不能一眼就看出问题的修复方案,但是排查问题多了会有大概的思路,需要沉下心来不断的踩坑、爬坑,最终才能定位解决问题。

本文以Maven构建工具为例,从原理、思路、工具、实践几方面分享Java中复杂jar包依赖问题排查经验。

二、Maven依赖问题的表现形式

1、老项目

  • 一般是新引入了一个jar包,导致项目启动不起来
  • 原来的服务好好的,新的改动不涉及这块,但是新改动代码后突然报错了

2、新项目

  • 日志问题:众所周知,jar生态的日志包及其混乱,同一套日志组件,不同版本之间也可能会带来问题
  • 组件整合问题:各组件底层依赖jar包版本不一致,导致问题。最麻烦的是不兼容的依赖。

三、问题排查策略

1、基础知识:Maven依赖传递策略

compile (编译范围):默认策略;编译,测试,运行,打包都能用

provided (已提供范围):编译,测试可用,不会打包到package包中。大部分情况下需要底层环境提供,比如tomcat服务中已经内置了servlet包,代码写代码时需要调用servlet的类

runtime (运行时范围):只有运行时才可用,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC驱动实现。

test (测试范围):test范围依赖在编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。

system (系统范围):system范围依赖与provided 类似,但是你必须显式的提供一个对于本地系统中JAR 文件的路径。适合maven中央仓库中不存在jar包引用。

2、基础知识:Maven依赖树的解析规则

(1)深入优先原则

深度优先遍历依赖,并缓存节点剪枝。比如下图:

  • A→B→D→E/F
  • A→C→D

在第二步A→C→D时,由于节点D已经被缓存,所以会立即返回,不必再次遍历E/F,避免重复搜索。

image-20211113200514057

(2)短路径优先原则

比如下图 A 通过 B 和 D 引入了 1.0 版本的 E,同时 A 通过 C 引入了 2.0 版本的 E。针对这种多个版本构建依赖时,Maven 采用短路径优先原则,即 A 会依赖 2.0 版本的 E。如果想引入 1.0 版本的 E,需要直接在 A 的 pom 中声明 E 的版本。

image-20211113200742469

(3)依赖循环

maven中禁止有循环依赖。比如A 依赖了 B,同时 B 又依赖了 A。这种循环依赖可能不会直接显现,但是会在一个很长的调用关系显现出来

(4)Maven多模块问题

B模块依赖A模块,A模块的依赖修改了,需要install一下,B模块才能感知到最新的依赖。

3、工具

(1)日志

开发环境,建议默认日志级别设置为info,对需要关注的模块,建议设置为debug模式,比如当前工程目录,正在联调的依赖jar包。

如果异常无法定位,第一反应应该是添加日志。

(2)maven命令行

添加以下有空的命令行参数:

  • -e 出错日志

  • -X 打印debug日志

  • -q 只打印错误

(3)IDEA插件:Maven Helper

安装插件后左下角会出现个新标签页,点击后搜所有此模块依赖的jar包及冲突情况

image-20211113201357394

image-20211113201425368

4、新项目

新项目碰到错误,复杂的问题,先不要深入研究,先看下是否有以下情况:

  • 新项目推荐Log4j2,代码里统一用slf4j打日志
  • 新项目推荐spring boot,它们内部把依赖问题都修复了,可以避免很多问题

5、从下往上递归根因

java的错误栈是从报错的最底层逐步往上打,这样能很方便的定位,一般的问题通过此手段定位问题后即可很快修复。

复杂点的异常,比如jar包冲突异常,最底层的异常一般都是基础组件,比如类加载器、日志组件等,这种情况就要多看几层。

6、从上往下查看变动

大部分问题都是新引入依赖导致,从上往下查看变动,配合从下往上定位信息,定位出问题的组件。

四、案例分享

1、Tomcat项目启动报错

背景:新需求接入舆情SDK,测试环境测试通过,线上发布有大量机器tomcat启动失败。

(1)查找错误日志

异常日志:

image-20211113201801746

(2)尝试修复:第一次

通过module-info.class,和 tag in constant pool: 19分析,直观感受是tomcat不支持java9中的模块策略

机器上使用的是tomcat6, 还不支持java9的新模块策略,遂升级tomcat6到tomcat8,启动错误还有

(3)尝试修复:第二次

此异常是lombok-1.18.10引起的,这个版本为了支持java9添加了module-info.class,而lombok包是java源代码增强的一个工具,运行时,并不需要,遂把依赖的scope改成provided。

重新发布,又报了以下异常:

image-20211113201859921

(4)尝试修复:第三次

报错Not running on Jetty,而我们的服务是运行在tomcat上,配合从上到下排查的思路,在新引入的maven依赖项打开pom.xml,查看里面的依赖项还有parent里的依赖项,找到了以下元凶:parent里会依赖***-boot-starter-web,而这里默认使用jetty作为引擎,遂排除掉,并验证业务逻辑是否正常。

image-20211113202044303

重新发布,问题解决。

(5)经验总结

  • 排查jar包也有风险,很可能导致业务功能受影响,排除后要经过充分验证

2、新加依赖后服务启动失败

背景:新需求接入用户个性化api jar包,使用MDP的thrift注解注册了 thrift client bean,引入后项目启动失败。

(1)异常日志

image-20211113202156042

(2)定位问题

从堆栈初步看是以下包不兼容导致的:swift-codec或guava,还有mtthrift。刚开始重点排查了swift-codec或guava的兼容问题,将其降级和新引入jar包里的版本一致,启动错误还在

刚开始没往mtthrift方面想,因为mtthrift是美团的基础组件,一般情况下不会出现兼容的问题,查看官方wiki也没说有兼容问题

(3)从上到下排查

查看新引入的依赖,打开pom.xml,发现里面引入了MDP 1.5.5版本,而我们的项目的是最新的MDP 1.6.6.1, 初步判断是MDP包冲突,所以就把新包的MDP包排除掉

image-20211113202304318

重新启动,错误依旧

(4)排查版本不一致的依赖

到现在基本可以认为是mtthrift升级导致的不兼容问题。我们引入的jar包只需要其中了api定义,并不需要依赖的依赖,所以把所有的依赖都排除掉。重新引入和依赖包一致的mtthrift版本。重新启动,问题解决。

image-20211113202412654

(5)总结

这个问题解决过程中走了很多弯路,一般我们的认为基础组件升级会兼容老版本,还有一点加深了这个认识:项目里引入的其他的thrift-client包,并没有出现问题。

但不幸的是,这个正好遇到了此类问题。从最终的解决方案看,应该是新引入的jar包编译的问题。

五、对外发布jar包规范

  • 对外提供的SDK或API包,不要包含不必要的额外依赖
  • 严禁包含有依赖的parent的jar包发布
  • 必须要依赖的,可以依赖通用包比如common-langs、guava包等,最好设置provided。

六、总结

Maven包依赖问题是开发中的常见问题,如果不熟悉排查方案,会浪费大量时间。本文从工具、方法论、实践方面做了一些思考,希望对大家日常开发有帮助。

原文链接Maven依赖冲突问题排查经验 | 木小丰的博客

作者简介:美团Java高级工程师,关注软件架构及职业成长,不定期分享各种技术、资源,对文章中涉及的技术感兴趣或有任何问题请关注微信交流。

以上是关于Maven实战与原理分析:Maven在应用中的常见问题整理的主要内容,如果未能解决你的问题,请参考以下文章

Maven实战与原理分析:maven 自定义插件开发实战

Maven实战与原理分析:maven 自定义插件开发实战

Maven实战与原理分析:maven实战

Maven实战与原理分析:maven超全使用指南总结

Maven实战与原理分析:maven超全使用指南总结

Maven实战与原理分析:手把手带你搭建maven私有仓库