您的位置:首页 > 编程语言 > Java开发

Java Security Architecture--Java安全体系技术文档翻译(五)

2017-10-26 15:52 344 查看
返回目录

五 类加载安全

动态类加载对于Java虚拟机来说是一个重要的特征,因为它给予Java平台在运行时注册软件的能力。动态类加载有几个独特的特质。首先,懒加载(lazy loading)意味着类在需要的时候才会被加载并且在可能的情况下到最后时刻才加载。第二,动态类加载通过增加连接阶段的检查保持了Java虚拟机的类型安全,连接阶段的检查取代了某些运行时检查并且只执行一次。此外,程序员可以定义他们自己的类加载器,例如可以指定某些类从远程位置加载,或者分发合适的安全属性给他们。最后,类加载器可以用来为各组件划分不同的命名空间。例如,一个浏览器可以使用不同的类加载器来加载来自不同网站的applets,这样就可以对这些applet类保持一定程度的隔离。实际上,这些applets可以包含相同名字的类--这些类被Java虚拟机认为是不同的类型(因为是被不同的类加载器加载)。
类加载机制不止在Java编程语言的动态特性中是核心机制。其在提供安全性方面也扮演了很重要的角色,因为类加载器负责定位和寻找类文件,咨询安全规则(policy),并给类的对象定义合适的许可。

5.1 加载器类的层次体系

当加载一个类时,因为在Java虚拟机中存在着很多的类加载器实例,一个重要的问题是我们如何决定使用哪个类加载器。Java 2 SDK引入了很多拥有不同属性的加载器类,所以另外一个重要的问题是我们要用哪个加载器类型。

加载器类层次体系的根是一个叫做java.lang.ClassLoader的抽象类,在JDK1.0中最先定义然后被扩展。Java 2 SDK v1.2中定义的类型

java.security.SecureClassLoader是抽象ClassLoader类的一个子类和具体实现。java.net.URLClassLoader是SecureClassLoader的子类。
一个叫做Appletviewer的应用程序使用了一个私有的类sun.applet.AppletClassLoader来加载applets。在JDK1.0中,AppletClassLoader是ClassLoader的一个子类和具体实现。在Java 2 SDK中,它是URLClassLoader的一个子类。
当创建一个定制的加载器类,该类可以是上面提到的所有那些类的子类,取决于该定制加载器类有什么特许需求。由于AppletClassLoader是定义在sun.*中的一个私有类,它不受支持并且经常改变,所以我们的类加载器不应该继承它。

5.2 始祖类加载器

因为每一个类都是被它的类加载器所加载,而每一个类加载器本身也是一个类,也需要被另一个类加载器加载,我们似乎遇到了明显的鸡生蛋蛋生鸡的问题,换句话说,第一个类加载器从哪而来?确实存在一个“始祖”的类加载器启动了类加载的过程。始祖类加载器一般是用本地语言例如C语言编写,并且不会将自己显露在Java的上下文中。这个始祖类加载器经常以一个平台依赖(也就是与操作系统相关)的方式加载本地文件系统中的类。
一些类,例如在java.*包中定义的类,对于Java虚拟机和运行时系统的正常执行是至关重要的。他们经常被视为基础类。基于历史原因,所有这些类的类加载器都是null。这个null类加载器或许就是始祖类加载器存在的唯一迹象。实际上,把null类加载器视为始祖类加载器会更容易理解一点。
给定了一个Java应用程序中的所有类,我们可以轻松的构造一个类加载树来表达类加载关系。每一个非加载器的类都是一个叶子节点。每一个类的父节点都是其类加载器,而null类加载器是根。这样的结构是一个树形结构,因为不可能有环——一个类加载器不能加载他自己的祖先类加载器。

5.3 类加载委托

当一个类加载器被要求加载一个类时,它或者可以自己加载这个类,或者可以请求另一个类加载器做这个工作。换句话说,第一个类加载器可以委托第二个类加载器加载一个类。类加载器之间的委托关系和类加载器之间谁加载了谁是毫不相关的,从这方面说是委托关系是一种虚拟的关系。与加载关系不同的是,委托关系在类加载器对象生成时确定,以一种父子关系的形式存在。不过,系统类加载器是所有类加载器的委托祖先。必须注意委托关系不能包含环。否则,委托过程会进入一个无限的循环。

5.4 类解析算法

Java 2 SDK ClassLoader加载类的方法中查找一个类的算法如下:

检查该类是否已经被加载。
如果本类加载器有一个明确的委托双亲,则委托其双亲尝试加载该类,如果没有明确的双亲,则委托始祖类加载器。
如果以上都没得到类,则使用自己的加载类方法来加载类。

这里,第一步寻找类加载器的本地缓存(或者功能上的等价物,例如全局缓存)来查看已经加载的类中是否存在与目的类相同的类。最后一步提供了对加载机制的可定制化;这样一个定制的类加载器可以重写加载类方法以指定如何查找一个类。例如,一个applet类加载器可以重写该方法,以指定去某个applet服务器上定位class文件,然后通过网络加载该class文件。
无论在哪一步定位到一个类,都会立刻返回。如果上述所有步骤执行完后还没有找到类,一个ClassNotFound异常会被抛出。
注意到相同类不能被相同的类加载器加载超过一次,这对于类型安全是至关重要的。如果正在加载的类并不在那些已被加载的类中,当前的类加载器尝试将加载它的任务委托给父类加载器。这个过程是递归的。这保证了类会被合适的类加载器所加载。例如,当要定位一个系统类,委托过程会一直继续直到委托给系统类加载器。
我们前面已经看过委托算法了。但是,给定任意一个类的名字,我们在加载它的时候从哪个类加载器开始呢?决定用哪个类加载的规则如下:

当加载一个应用的第一个类时,使用一个新创建的URLClassLoader的实例。
当加载一个applet的第一个类时,使用一个新创建的AppletClassLoader的实例。
当直接调用java.lang.Class.ForName时,使用始祖类加载器。
当从一个已存在的类中通过引用触发了类加载时,使用这个已存在类的类加载器来加载。
注意,对于URLClassLoader和AppletClassLoader实例的使用规则有意外的情况,并且在特定的系统环境下会有所不同。例如,一个web浏览器可能选择对于来自同一个网站的applet复用AppletClassLoader。

由于类加载器的威力,对于谁能创建类加载器实例我们进行了严格的限制。另一方面来讲,提供一个便利的机制让应用和applet能指定URL位置并从那里加载类是很有吸引力的。我们提供了允许任何程序都能创建URLClassLoader实例的静态方法,然而并没有提供其他类型的类加载器。

===========================================================================

本技术文档的翻译工作由不动明王1984独自完成,特此声明。

翻译辛苦,珍惜劳动,引用时请注明出处!

===========================================================================

返回目录

上一篇:Java Security Architecture--Java安全体系技术文档翻译(四)

下一篇:Java Security Architecture--Java安全体系技术文档翻译(六)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: