Java的动态链接

 JAVA和C/C++的区别有很多,你知道Java程序需要链接吗?Java 语言与C/C++ 在语法上很相似,但是由于它是伴随着互连网络成长起来的,为了迎合异构主机构架以及支持安全的软件分发,逐步发展成为一个语言平台。我们知道如果用C/C++写程序,从源代码到可执行程序需要经历编译,链接两个步骤。但是用Java语言写程序则不同,你只需要将你的源代码编译成字节码 就可以了,字节代码通过Java虚机来运行。要概括上面两种语言的这一不同之处,我们可以说Java是通过Java解释来执行的,C/C++是通过编译来 执行的。

如果你用C/C++来写一个通讯程序完成Intel/Windows 到Sparcs/Solarise两台计算机之间的通讯功能,你需要经历痛苦编译和链接。也许你会使用标准C编译器,使用其提供的最基本的C语言API, 再加上若干的#if…#define 写出一个在上面两个计算机都可以编译链接成功的源程序,这意味着所有标准C语言API之上的工作都要由你一个人来完成。也许你会在标准的C语言API上使 用C++来封装一个精巧的类库,或者使用别人的类库来屏蔽底层OS API的不同,并在这样类库之上写出一个漂亮的源程序。可见使用C/C++要完成上面的工作可不容易,你除了要为你写的源文件和不同的编译器的语法规则做斗争,将我们的源程序编译成为中间件文件;同时还需要为 你所使用第三方的类库文件与链接程序做斗争如果你源程序和第三方库不是采用一个编译器编译出来的,哪你还得找到第三方类库的源代码,或者是换一个编译器来 编译你的源程序。基本上要经历好几轮的修改、编译、链接,才能让的源程序进入运行状态。

如果你采用Java来写这个程序则轻松了很多。你可以使用JDK所提供的API,也可以使用第三方的Java类库。你的源程序可以很轻松的编译成为字节 码,这些只是一个一个的程序块,如果想让程序运行起来,需要Java虚机在运行时动态加载(RunTime Link)其所需要的资源,使之成为一个可以运行的程序。Java语言这样做的好处显而易见,首先源程序只需要编译一次,成为独立与具体机器指令的中间文 件,然后这些字节码就可以在任意的标准的Java虚机上运行。

说了这些众所周知的C/C++与Java语言在编译运行的不同,对我们有什么启发呢?笔者先后做过C/C++和Java的开发,开始并没有领会其中的真 谛,直到前几个月深入接触到了class loader才真正体会到Java这一RunTime Link 的神奇魅力,可以让我们完成很多静态链接所无法完成的任务。

如果我们拿到了一个C/C++编译完成的可执行程序,如果这个时候你想为这个可执行程序添加一些可以扩展的功能,如果你这时没有源代码,那你等待你的可能 是处理复杂的汇编指令,或者采用钩子函数去替换可执行程序调用的API。如果你拿到的是JAVA程序,你可以像修改C/C++可执行程序一样修改Java 的目标文件字节码的方式来实现,但是我们可以通过一种更优美的方式来修改或者扩这Java程序的运行时的特性。

因为Java是解释执行的,其目标文件字节码是一个非常小的,并且是很容易理解的指令集合。Java虚机为了能够让这些短小的指令集合能够运行起来,这就 是我们前面提到的C/C++程序链接(Link)所做的事情(链接就是要把程序运行所需要API以及相关的资源文件,都重新进行组织生成可以单独运行的程 序)。由于Java程序是运行在Java虚机之上的,自然的Java程序在运行过程中所需要加载的类文件或其他的资源文件都需要Java虚机来负责加载, 并提供管理和调用。

写过Java程序的朋友也许经常会遇到这样一个问题吧,就是Exception in thread “main” java.lang.NoClassDefFoundError:XXX,一般遇到这个问题的时候,基本上都是因为你的CLASSPATH没有设置正确, 设置正确的CLASSPATH,就可以解决这个问题,但是有多少人会去深究其中的奥秘呢?

首先我们来究其原因,我们知道Java虚机在运行的过程中是通过class loader动态读取Class文件,并将加载后Class的字节码交付给Java虚机执行。这个很容易理解,Java虚机不可能预先知道任意一个 Java程序需要的Class文件,所以Java虚机需要通过某种手段来实现Class文件的正常读写,上面的Exception就是Java虚机在加载 Class文件出现的。

知道这一原理之后,我们可以在不修改源程序的基础上扩展Java第三方程序库的功能,或者说我们可以随意改变某个Class文件的指令,加入我们期望的扩展功能。通过修改字节码我们就很容易实现一个特定的扩展功能,或者是暴露给我们一个原来只能内部调用的方法。自己修改字节码会比较困难,我们可以通过调用一些第三方的程序库(BCEL,ASM等)来实现对应字节码的修改,通过class loader实现将修改后的字节码提交给JVM,然后链接执行。

See, It is simple and easy.