- 前言
之前其实一直在用到Maven,但却没有怎么下功夫了解。但了解Maven还是非常有必要的。我一直很认同ber哥的一句话。
强大的IDE会割裂你和这些底层的运作。但你还是要了解这些底层运行的原理,因为总会遇到各种各样的问题让你不得不和这些底层打交道,对于工程师来说,生疏的表现是不合适的。了解这些细节,有助于你解决问题。工作就是在解决各种各样的问题。
通过了解Maven所为我们做的,我们可以更深刻的理解整个JAVA世界的历史变革,对我们现在使用的一切有一定认知性。虽然不能讲得很深,但也能大致对Maven有一个感性的认识了。
什么是Maven? 什么是包????
首先,Maven是一个包管理工具,也有人叫它项目管理工具。但此包非彼包,并不是初学JAVA时接触的那个Package包。那到底是什么包?我们不妨先引入一个概念。JVM的运行机制
JVM的工作只有一个:
执行这个类的字节码,过程中若碰到了新的类,加载它。(循环往复直到把事情做完)
那么,它是如何找到这些类的呢?我们知道,命令行中的那些可执行程序
是在哪儿找到的呢,答案是在当前的PATH环境变量
中找到的。相应的,我们找类也有一个路径,叫Classpath(类路径), 和命令行业寻找可执行程序一样。JVM会挨个在PATH里面找,是压缩包就解压来找(这是全自动的),文件夹就直接开找,找不到就下一个,找到为止。让我们随便看一个JAVA小案例的Classpath。
1 | -classpath "C:\Program Files\Java\jdk1.8.0_221\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar;F:\shape-polymorphism\target\classes" com.github.hcsp.polymorphism.Main |
由于存在包依赖关系,如果是真正的项目,这个Classpath会长得超出显示限制。。。。
类的全限定类名(目录层级)确定了一个类
包就是把许多类放在一起打的压缩包啊!
jar包就是一堆类的集合
介绍完包了之后,需要说的是,我们知道JVM懂得看到了方法去找类,找类也就是在找包(通过Classpath),但包并不会凭空出来,很多第三方包都需要我们Down到项目中,添加到Classpath中,这也是Maven诞生的主要原因,包管理的本质就是告诉JVM如何找到所需的第三⽅类库。 开发过程更爽!生产出来的软件更棒!
让我们看一看没有Maven之前的年代,JAVA是如何走过来的。
蛮荒时代
这个时候,全世界的JAVA工程师还没有形成包管理这样的概念,
一个一个手写命令进行编译运行,just like that1
javac -cp ./commons-lang3-3.9.jar StringIsBlank.java
想象一下这里需要不仅仅是一个包,而是上面成千上百个。。。真正的编程到昏厥。苦不堪言(如果以现在的眼光看的话,无论什么情况当事人一般都会觉得还行哈哈哈哈哈哈哈哈哈哈)
启蒙时代
Apache Ant 提出了XML配置文件来规范化所谓包管理
,制定变异的源代码目录,依赖的JAR包,输出目录等。虽说有了点规范的意思,但还是跳不过一个一个手动下载的命,也没有解决JVM傻呆呆的处理重名JAR包的问题。(Classpath hell),且大家的编写各不相同,配置并不通用。你写你的libs,我写的library,他写他的依赖jar包文件夹,她写她的输出目录。大家经常拿Ant 和 Maven 作比较,现在Ant似乎不行了,大家就拿Gradle和 Maven 作比较。 由此可见Maven还是很坚挺的 :)
Maven时代
Maven是一款跨时代的包管理工具,它使用了一套相当先进的项目结构及约定征服了全世界的工程师。首先它存在一个中央仓库,还制定了一些规则,让全世界的工程师不用再一个一个手写命令行编译运行,而是变为了导包
。我们只需要在pom.xml文件按照相应的约定
格式导入即可,就下载到了你的本地仓库。下载的第三方包放在这里进行缓存。它还会把你下载的第三方包所以来的其他包一并下载进来。
中央仓库的构建,对于包管理方面按照约定为所有的包编号,⽅便检索
1 |
|
Maven这一严谨的规定折服了所有的工程师,以至于后面诞生的包管理工具相关规定也有这样的影子。除此之外,Maven还解决了一个JAVA历史性难题。Classpath Hell (类路径地狱)。
什么是Classpath Hell,也就是包冲突?
如果你的classpath或项目里面不幸地引入了两个版本不一样,但同名的jar包。就是噩梦的开始,JVM可不会想人一样会斟酌选择,它只会找到哪个用哪个。用不到这个包自然不报错,它会让你错误地认为一切顺风顺水,然而,它也会在某一天毫无征兆的暴毙,然后你只得大半天赶到开始接受地狱般的bug挑战。
常见的包冲突的一些报错:
- AbstractMethodError
- NoClassDefFoundError
- ClassNotFoundException
- LinkageError
包管理有一个原则:绝对不允许最终的classpath出现同名不同版本的JAR包。
只有能够得知包冲突,才能解决。
maven 是 如何得知 两个包冲突呢
因为maven把这些包按照名字格式把它们归类起来了。
依赖冲突的解决:最近的胜出
这个机制让maven能够解决绝大多数的情况,但在现实生活中,我们还是会遇到需要亲自手动去解决的情况。为什么?
因为mvn解决掉的是0.2版本,保留0.1版本。但我们要用的确是更新版本的0.2版本!!!!
你可以打开ide点击maven窗口,用肉眼目测整个项目的包依赖关系。
也可以在终端输入1
mvn dependency:tree
来观察。
如何手动解决?
- 我们手动介入让我想要的版本的jar包离的最近,从让它在JVM的包冲突的比较中胜出,自然JVM用的就是这个包。
- 通过
exclusions
声明强行干掉子依赖
- 如果想让ide来帮助你的话,你可能需要
maven-helper
插件,当然非人的调试还是可能出问题。
拓展一下,如果是这样呢?
A->B->C:0.1
D->E->C:0.2
谁赢,答案是0.1版本获胜,maven还是会选择前面的那一个。
maven总是会选择一个,因为绝对不允许最终的classpath出现同名不同版本的JAR包。
那Maven还有一个关键属性 scope
,它有什么用呢?
其实很简单,它的作用就是 让依赖 可以隔离化。换言之就是让 依赖不是全盘默认,而是可以人为的决定这个是否可以依赖。
scope的常用属性为 compile
test
provided
- test: 只在测试代码中可以拿到依赖包
- compile: 在测试和生产环境中都可以拿到依赖包
- provided: 在编译的时候可以拿到,在运行时就拿不到了
Maven的伟大之处——它不仅仅是项目管理工具!
Maven项目构建,版本控制,库依赖三大特性。若想真正了解Maven
建议阅读 Maven实战