java 包管理_Java包管理的那些事1——IDE背后发生了什么

我一直想写一篇文章,谈谈Java的包管理,因为我似乎还没看到过由浅入深说的特别明白的文章。我会写一个系列,从写给小白用户的入门指南到最后Java 9的模块系统(Java Platform Module System,JPMS)。上天保佑我能坚持下去吧。

包管理不是一件容易的事情

对于绝大多数人(包括曾经的我自己),包管理的概念就只是在pom.xml中复制粘贴元素。对于其中的原理细节等一概不知,碰到奇怪的NoClassDefFoundError或者AbstractMethodError更是两眼一抹黑整个人处于懵逼状态。

实际上的包管理比这复杂的多。如果你觉得这是一件很简单的事情,看看人们对其他语言的包管理器的吐槽吧:npm install 会下载半个互联网到项目目录你是如何在同一台电脑上安装一万次python的

然而你很少听到人吐槽Java的包管理。有人吐槽Java语法啰嗦,有人吐槽Java运行慢,有人吐槽Java吃内存,但是很少有人吐槽Java的包管理。Java的包管理实在是太——我说无可挑剔是不是给人一种钦定的感觉?连最苛刻的程序员也不得不承认,Java的包管理做的非常成功。Java的成功,很大程度也与包管理有关。基本上,这个世界上没有Java不能做的事情。

这是Java的护城河。

IDE的强大是一柄双刃剑

那我为什么还是不懂包管理?

因为IDE太强大了。IDE的强大割裂了Java开发和Java运行时(Java虚拟机)的联系,造成了难以逾越的鸿沟。IDE就像是一堵巨大的墙,向你屏蔽了Java运行时的所有细节,向你展示了一个简单而单一的假象:IDE向你屏蔽了Java Runtime的细节

作为一个普通Java开发者,如果你不去主动跨越这条鸿沟,完全没问题,你可以一辈子活在IDE的世界里,写代码,点击Run(Debug)按钮,改Bug……

但是站在鸿沟边缘,你心里发虚,像一只外强中干的纸老虎。你碰到奇怪的问题就会立刻现出原形——更别提面对面试官了。

我们接下来的故事,就是帮你拆掉IDE的这一堵墙。IDE先生,拆掉这堵墙吧!

JVM只和类打交道

从你写Java代码的第一天起,你就知道,Java语言的最小单位是类。JVM只认识类,哪怕你写一个跟OOP八杆子打不着的面向过程的程序,你也得把它包在一个毫无意义的类里。

好了, 我们的故事就从类开始。Java的运行时叫做Java虚拟机,我们以后简称JVM。这货是一个独立的虚拟计算机,意思是说,这货虽然运行在你的电脑(物理机)上,但是和你的物理机互不干扰,它有一套独立的运行系统。当然,如果开多个JVM,每个JVM都是独立而互不干扰的。

JVM被设计成只有一个使命:按照类的名字,加载、执行类。

请注意,在JVM的世界里,类的名字指的是全限定类名(Full Qulified Class Name,在很多场合下被缩写为FQCN),也就是说,它只认识类的全名——在它的世界里,只有org.mycompany.Application这种啰嗦的东西。在JVM看来,Java代码都长这样,所有的名字都是全限定类名:

@org.springframework.boot.autoconfigure.SpringBootApplication

public class org.mycompany.Application extends java.lang.Object {

public static void main(java.lang.String[] args) {

org.springframework.boot.SpringApplication.run(org.mycompany.Application.class, args);

}

...

请注意你完全可以这么写这么啰嗦的代码,这么写是合法的,只是显得有点……智障。

为了不让别的语言当成智障,Java允许你通过import的方式省略同一个Java文件(准确的说法是“编译单元”)中的全限定类名。简单的说,这就好像一个人的全名叫“宇宙村东头大槐树下收破烂的王小明”(全限定类名),但是你在他家里可以直接称呼他小明(import之后的简称)。

一般而言(有例外),在同一个JVM中,根据全限定类名可以唯一地确定一个类——按照全限定类名,JVM就可以顺藤摸瓜找到类的位置,然后加载执行之。JVM的职责就这么简单。

JVM并不认识Java代码

看到这里你恍然大悟道,哦原来如此!通过全限定类名,JVM就能找到我写的类,以及我的Java代码中引用的类,然后开心的执行它们了,对不对?

对,也不全对。我先划一个重点:

JVM不认识你写的Java代码!

JVM不认识你写的Java代码!

JVM不认识你写的Java代码!

你可能会说,别特么蒙我,我用IDE写Java代码写的文思如尿崩,6的连我自己都看不懂,你告诉我JVM不认识Java代码?糊你熊脸

这就是为什么我说IDE屏蔽掉了太多的细节。当你写好代码,开心地点击Run/Debug按钮的时候,IDE其实偷偷执行了一个隐藏步骤(编译),把编译后的东西(字节码)放在了某个地方,然后告诉JVM,呐,货我已经运到了,你可以开始你的表演了。如果你注意观察的话,你可能会发现有时候在IDE中点击Run/Debug之后,需要很长的时间才能启动JVM——这个时间有很大一部分就花在编译上了。

JVM只认识字节码

IDE为你隐藏掉了编译的过程,让你产生了一种错觉:写了Java代码,点运行,JVM就会开始执行自己写的Java代码。但是实际上,JVM根本不认识Java代码,它只认识一种东西,就是字节码。

(注意,Java 11引入了脚本化(Scripting)支持,在JVM中直接运行Java代码成为可能。详见:https://blog.codefx.org/java/scripting-java-shebang/)

因此,我们上面说的JVM的使命:

按照类的名字,加载、执行类。

准确的说应该是:

按照类的全限定类名,加载、执行类的字节码。

打开你手边的IDE,在随便一个项目中点击Run/Debug,随后你会在项目文件夹看到:Maven项目默认的编译输出字节码目录是target/classesGradle4+默认的字节码目录是build/classes/{language}/{sourceSet}

如果你注意观察生活,你会发现,在点击Run/Debug之后,IDEA会在第一行输出JVM的启动参数(可能很长),双击复制粘贴,你会在其中找到对应的字节码目录:

JVM用简单粗暴的方式定位字节码

好了,我们说过JVM是根据一个类的全名查找到这个类的字节码的,but how?

我先来问你一个问题,你是如何在你自己的电脑中定位一个文件的?只见你迅速而熟练地打开了你自己的电脑上的D:\共产党员先进性教育\学习\物理\活塞运动文件夹……

JVM做的事情和你非常相似。在上图中,要定位一个全限定类名为hello.Application 的类,JVM会查找/hello/Application.class这么一个文件。一般而言(有例外),JVM定位和查找一个类的方法就是这么简单粗暴:找到了这个文件,就加载并执行之;找不到,就丢一个错误出来(NoClassDefFoundError)。

你可能会问了,那jar包是啥?

我们来做一个小实验。随便去哪搞一个jar包,比如说我手头有一个asm-7.0.jar,然后把它的扩展名改成zip:

然后用你喜欢的解压缩软件解压缩之:

$ tree .

.

├── META-INF

│ └── MANIFEST.MF

├── module-info.class

└── org

└── objectweb

└── asm

├── AnnotationVisitor.class

├── AnnotationWriter.class

├── Attribute$Set.class

├── Attribute.class

...

你会发现,jar包的本质就是一个压缩的字节码文件目录。因此,在JVM看来,jar包和字节码文件目录几乎没有区别。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值