JVM类加载机制(ClassLoader)源码解析(2)

来源:互联网 发布:容易流前列腺液 知乎 编辑:程序博客网 时间:2024/05/21 09:11

我们来对defineClass这个方法进行解析,该方法比较复杂,首先如图:

该方法主要是通过一个字节的数组,对该数据进行解析、验证、二进制码格式校验。通过抛出的ClassFormatError的这个异常来看,也是验证这个类的格式是否符合JVM的规范。
最终是将一个字节数组转换成类实例(注意不是实例对象,是有区别的)。
目前该方法已被替代,注意上图里的注释。

目前新方法,如下图:

第一个参数,其实就是类名(包括整个包名在内)。
现在对该方法进行详细的解析,首先看下check方法,如图:

非常简单,就是检测classLoader是否初始化。

preDefineClass方法,如图:

checkName检测类名是否有效(去除数组类型的判断),禁止以java.为前缀包名。如果没有保护域,则获取默认保护域;最后是进行数字签名的验证。
主要看下数字验证方法,checkCerts方法,如图:

数字验证的方式也很简单,如果存在就比较当前保护域里的数字签名与之前的数字签名是否匹配;反之就在package2certs对象(就是一个hashtable)里缓存起来。
这里的数字签名是从CodeSource这个类型的对象来获取,也就是代码源里的签名。
compareCerts方法就是对比两个数字签名是否匹配(方法很简单,这里就不用截图了)。这里主要是针对于类安全考虑,防止有人恶意修改类文件,采用的是通用的java安全框架来实现的。以后会在java安全的源码分析里在进行详细介绍。

defineClassSourceLocation方法,如图:

非常简单,就是获取代码基的路径。
然后就是开始调用本地解析方法defineClass1进行处理(类似于findLoadedClass方法处理)。如果抛出ClassFormatError异常,在给一次机会(调用方法defineTransformedClass),重新传输一次类的二进制码做解析;如果还是失败,就抛出ClassFormatError异常类型。

最后是执行postDefineClass方法,如图:

这里就是用之前比较过的数字签名,对类进行签名操作(也就是类似于加密操作)

这里需要注意的是:
defineClass方法与loadClass方法是两种不同加载类的实现。
区别在于:
前者是有严格的安全机制,输入源是一个二进制码;后者很简单就是一个本机的加载方式,输入源来自本地存放的class文件。
前者比较适合于网络远程加载类,因为需要进行安全控制;后者是一个比较基于自定义目录加载的实现。
前者在实现时主要是findClass方法与之配合使用;而后者主要是要设置加载的URL(也就是路径)。
这里非常明显地体现出了java的核心思想(严格地来讲应该是核心特性),安全和网络。