前情回顾:
Java包管理的那些事1——IDE背后发生了什么
Java包管理的那些事2——神奇字节码在哪里
包就是一堆字节码文件的集合
我们在前两回中讲到,JVM的设计相当简单粗暴:
- JVM按照类的全限定类名,去Classpath所指定的目录中挨个查找字节码文件;
- 找到之后,就阅读这份说明书(类的字节码),按照要求工作:每个说明书都写了自己的用法用量,饭前还是饭后服用;
- 如果这份说明书中提到了其他类的全限定类名,则重复步骤1的操作。
在这个过程中,Classpath从哪儿来,里面包含些什么东西,傲娇的JVM完全不关心:我只要能在你给的Classpath里找到说明书就行,其他的我不管。
那么问题来了,假如你要使用第三方库,你要如何找到这些说明书?
或者换个问题,你怎么把你自己写的说明书给全世界人民使用?
很容易想到,你需要把你自己写的一叠说明书(你的库不大可能只包含一个类对吧)装订好,放在一个地方,任人观看和复制。任何一个JVM捡起来这叠说明书,就能毫不费力的理解并执行你写的逻辑。
我们称这样的一叠说明书为:包
![7f60cf34a702ef783418157dc83c1540.png](https://i-blog.csdnimg.cn/blog_migrate/0487bd35d700e84048f6ceca022d5d80.jpeg)
重复一遍:
把你自己写的一叠说明书(包)装订好(打包),放在一个地方(个人网站或者包仓库),任人观看和复制(下载)。
反映到现实世界中,就是你要用哪个包,就从什么地方下载或者复制一个jar包,然后告诉傲娇的JVM(放到Classpath中)。
这就是Java的包系统的基石。
包的依赖和传递性依赖
听上去似乎很简单。你要用哪个类,就把包含它的jar包下载下来,然后放到JVM的Classpath里。举个栗子:
![98e478a949303f47de6e22870b4c7688.png](https://i-blog.csdnimg.cn/blog_migrate/49f0e472c292b17e074827e20b9c8db3.png)
你要使用一个类org.junit.Assert
。你知道这是JUnit 4的一部分,于是你兴冲冲地打开了官网,下载了junit-4.12.jar
,然后放到Classpath里,启动JVM。JVM也兴冲冲地打开了org.junit.Assert
类的说明书(字节码),然后大声的抱怨:
NoClassDefFoundError: org.hamcrest.Matcher
你定睛一看,org.junit.Assert
的说明书在这里提到了一个另外的一个类,org.hamcrest.Matcher
,但是却没有包含在 junit-4.12.jar
里。
发现问题在哪里了么?通常情况下,你发布的这一叠说明书只包含你自己的类——你在写代码的时候引用的类都变成了一个全限定类名,印在说明书里了,这些类的说明书却没有随你的包一起发布。
我们称这种引用关系为包的依赖。换句话说,A包依赖了B包,那么意味着JVM在只有A包的时候是无法运行的。
你说,好办,那我就把B包也下载下来放到Classpath中不就行了?
JVM十分感动,然后十分悲痛地告诉你,B包的类引用了C包的类。C包依赖了D包和E包……
我们称这种关系为传递性依赖。
传递性依赖带来的问题
我们假设你是一个非常坚强的人,把所有的传递性依赖都找到后拼装了一个完整的Classpath给JVM。JVM很开心。
而你不一定开心。因为你数了数你的Classpath,包含了一千个包。
一千个包,放在记事本里黑压压的一大片,想想都觉得觉得刺激。
更刺激的是,你需要维护它。随着项目的规模增长,你需要引用一个额外的包X。你检查了包X,发现它依赖Y和Z。 Y包已经在你的Classpath里了,因此跳过它。Z包不在你的Classpath里,于是你把它添加进来。现在你开始检查Z包的依赖了……
更更刺激的是,假如你在这个过程中犯了一个错误,遗漏了某些包,JVM可能不会立刻崩溃——因为它只有运行到某个类找不到时才会大声抱怨。它可能平稳地运行了几天几个月都没运行到会出错的代码那里。突然,在情人节的晚上你和你的女票你侬我侬的时候,Boom!老板的夺命连环call过来了。
更更更刺激的是,你犯的错误可能不是遗漏了某些包,而是在Classpath中某些包重复了。我们前两节讲过,JVM在按照全限定类名查找一个类的时候,会挨个检查Classpath中的字节码目录(jar包),先找到哪个就用哪个。碰巧,你的Classpath中包含同一个库的不同版本(你一摊手,我的Classpath里有一千个包,犯个错误不是很正常么!),并且有Bug的版本更靠前。
最刺激的是,以上这些问题可能同时、随机发生……
咋办?在Java问世的前十年,业界一直在探索这些问题的解决方案。
欲知后事如何,且听下回分解。