类装载器体系结构

来源:互联网 发布:股票虚拟盘软件 编辑:程序博客网 时间:2024/05/02 00:45
本文摘自:《深入java虚拟机(第二版)》
Java 沙箱中类装载器体系结构第一道防线。因为是由类装载器将代码(这个代码可能是恶意的或是有漏洞的)装人Java虚拟机中。类装载器体系结构三个方面Java的沙箱起作用:
A、它防止恶意代码去干涉善意的代码。
B、它守护了被信任的类库的边界。
C、它将代码归人某类(称为保护域),该类(保护域)确定了代码可以进行哪些操作。
类装载器体系结构可以防止恶意的代码去干涉善意的代码,这是通过为由不同的类装载器装人的类提供不同的命名空间来实现的。命名空间由一系列唯一的名称组成,每一个被装载的类有一个名字,这个命名空间是由Java 虚拟机每一个类装载器维护的。例如,一旦Java 虚拟机将一个名为volcano的类装人一个特定的命名空间,它就不能再装载名为Volcano的其他类到相同的命名空间了。可以把多个volcano类装人一个Java 虚拟机中,因为可以通过创建多个类装载器.
   命名空间有助于安全的实现,因为你可以有效地在装入了不同命名空间的类之间设置一个防护罩在Java 虚拟机中,在同一个命名空间内的类可以直接进行交互,而不同的命名空间中的类甚至不能察觉彼此的存在,除非显式地提供了允许它们进行交互的机制。一旦加载后,如果一个恶意的类被赋予权限访问其他虚拟机加载的当前类,它就可以潜在地知道一些它不应该知道的信息,或者干扰程序的正常运行。
      类装载器体系结构守护了被信任的类库的边界这是通过分别使用不同的类装载器装载可靠的包和不可靠的包来实现的。虽然通过赋给成员受保护(或包访问)的访问限制,可以在同一个包中的类型间授子彼此访问的特殊权限,但这种特殊的权限只能授给在同一个包中的运行时成员,而且它们必须是由同一个类装载器装载的。
     用户自定义类装载器经常依赖其他类装载器(至少依赖Java虚拟机启动时创建的启动类装载器)来帮助它实现一些类装载请求。在java 1.2版本中,类装载器请求其他类装载器来装载类的过程被常态化,我们称之为双亲委派模式。从java 1.2版本开始,除了启动类装载器之外的类装载器,都有一个称之为双亲的类装载器。在某个特定的类装载器试图以自己的方式装载类以前,它会先默认地将这个任务“委派“给它的双亲,请求它的双亲来装载这个类。这个双亲再依次请求它自己的双亲来装载这个类型;这个委派的过程一直向上继续,直到达到启动类装载器,通常启动类装载器是委派链中的最后一个类装载器。如果一个类装载器的双亲类装载器有能力来装载这个类,则首先由其双亲类装载器来装载这个类,否则,这个类装载器试图自己来装载这个类。
    在版本java 1.2 以前,大多数虚拟机的实现中,内置的类装载器〔 以后将称为原始类装载器)负责在本地装载可用的class文件.在版本java 1.2 中,装载本地可用的Class文件的工作被分配到多个类装载器中。对于刚才称为原始类装载器的内置的类装载器,我们重新命名为启动类装载器,表示它现在只负责装载那些核心Java API的class.因为核心JAVA API的class文件是用于“启动”Java 虚拟机的class 文件,所义我们称它为启动类装载器.
         在版本1.2 中.由自定义类装载器(包括系统自定义用户自定义装载器)来负责其他class文件的装载。这些class文件包括包括标准扩展库的class文件类路径中发现的类库class文件应用程序本身的class文件等等。1.2版本中,在应用程序启动以前,Java虚拟机至少创建一个或多个系统自定义类装载器。所有这些类装载器被连接在一个双亲->孩子的关系链中,在这条链的顶端是启动类装载器,而在这条链的末端的类装载器,我称它为系统默认类装载器.它是指Java应用程序创建的、新的用户定义类装载器的默认委派双亲。系统默认类装载器它负责装载应用程序的初始类,它可以是任何系统自定义类装载器,这是由实现Java平台的设计者决定的。例如,假设你写了一个Java应用程序,创建了一个类装载器,这个装载器是通过网络下载来装载class义件的.如果你在虚拟机上运行这个应用程序,那么在启动时,首先将实例化两个系统自定义类装载器:一个"标准扩展"类装载器一个“类路径”类装载器。这些系统类装载器和启动类装载器一起联人一个双亲一孩了关系链中,如图3-2所示。类路径的类装载器的双亲是标准扩展"类装载器.而标准扩展"类装载器的双亲是启动类装载器。在图3-2中,类路径类装载器被设计成系统默认类装载器,新的用户自定义类装载器的默认委派双亲将是系统默认类装载器(即类路径类装载器)。因此当应用程序实例化它的网络类装载器时,它将把系统默认类装载器作为它的双亲
类装载器体系结构 - hubingforever - 民主与科学
 
      设想在运行Java 应用程序的时,首先向类装载器(这里是网络类装载器)发出一个装载Volcano类的请求,类装载器(网络类装载器)必须先询问它的双亲(这里是类路径类装载器)来查找并装载这个类。这个类路径类装载器依次将向它的双亲发出同样的请求,这里它的双亲即为标准扩展类装载器标准扩展类装载器也是首先将这个请求委派给它自己的双亲(启动类装载器)。假设Volcano类不是Java Apl 的一部分,也不是标准安装扩展的一部分,也不在类路径上,那么这些类装载器将返回而不会提供一个名为Volcano的己装载类。当类路径类装载器回答,它和所有它的双亲都不能装载这个类时,网络类装载器将试图用它自己特定的方式来装载Volcano类,它会通过网络下载Volcano。如果网络类装载器下载并装载volcano类成功的话,这样volcano类就可以在应用程序以后的执行过程中发挥作用。我们继续讨论这个例子,假设以后的某一时刻volcano类的一个方法首次被调用,并且那个方法引用了Java API中的java.util.HashMap,因为这个引用是首次被运行的程序使用,所以虚拟机会请求你的类装载器(网络类装载器)来装载java.util.HashMap.就像以前一样,你的类装载器首先将请求传递给它的双亲类装载器,然后这个请求一路委派直到委派给启动类装载器。但是这一次,启动类装载器可以将java.util.HashMap 类返回给你的类装载器,因为启动类装载器可以找到这个类,所以标准扩展类装载器就不必在标准扩展类库中查找这个类型,类路径类装载器也不必在类路径中查找这个类型,同样,你的类装载器也不必从网上下载这个类。所有这些类装载器仅需要返回由启动类装载器装载返回的类java.util.HashMap。从这一时刻开始,不管何时Volcano类引用名为java.util.HashMap的类,虚拟机就可以直接使用这个java.util.HashMap类了。
         在给出类装载器怎样工作的背景后,读者可能想知道如何使用类装载器来保护可信任类库。在有双亲委派模式的情况下,启动类装载器可以抢在标准扩展类装载器之前去装载类,而标准扩展类装载器可以抢在类路径类装载器之前去装载那个类,类路径类装载器又可以抢在网络类装载器之前去装载它。这样,在使用双亲一孩子委派链的方法中,启动类装载器会在最可信的类库(核心Java API)中首先检查每个被装载的class,然后,才依次到标准扩展、类路径上的本地文件中进行检查。所以如果网络类装载器装载的某段代码试图通过网络下载装载一个和JAVA API同门的class,比如java.lang.Integer,将不能成功。java.lang.Integerclass文件在Java的API中已存在,它将被启动类装载器抢先装载,而网络类装载器将没有机会下载并装载名为java.lang.Integer的类,它只能使用由它的双亲返回的类,这个类是由启动类装载器装载的。用这种方法,类装载器的体系结构就可以防止不可靠的代码用它们自己的版本来替代可信任的类。
    但是如果这个移动代码不是去试图替换一个被信任的类,而是想在一个看似被信任的包中插入一个全新的类型,情况会怎样呢?想像一下,如果刚才那个例子中,要求网络类装载器装载一个名为java.lang.virus(病毒)的类时,将会发生什么?像以前一样,这个请求将被一路向上委派给启动类装载器,虽然这个启动类装载器负责装载核心Java API的class,但是在核心的Java库中无法找到java.lang.virus,假设这个类在标准扩展库以及本地类路径中也找不到,你的类装载器将试图从网络上下载这个类。假设你的类装载器成功地下载并定义了这个名为java.long.virus的类. Java允许在同一个包中的类拥有彼此包可见的这个特殊权限,而这个包外的类则没有这个权限。所以,因为你的类装载器装载了一个名为java.lang.Virus的类,这个类假装是Java API 的一部分,希望得到访间核心java.lang中包可见的特殊访问权限,希望使用这个特殊的访问权限达到不可告人的目的。类装载器机制可以防止这个代码得到访问核心java.lang包中包可见的访问权限,因为Java虚拟机只把彼此包可见访问的特殊权限授子由同一个类装载器装载到同一个包中的class。因为Java API的java.lang中的类(被信任类)是由启动类装载器装载的,而恶意的类java.lang.virus是由网络类装载器装载的,所以这些类型不属于同一个运行时包运行时包这个名词,是在Java虚拟机第2版规范中第一次出现的,它指由同一个类装载器装载的属于同一个包的多个class的集合。在允许两个class之间对包内可见的成员(声明为受保护的或包访问的成员)进行访问前,虚拟机不但要确定这两个类型属于同一个包,还必须确认它们属于同一个运行时包它们必须是由同一个类装载器装载的。这样,因为java.lang.virus和来自核心Java API的java.lang的类不属于同一个运行时包,java.lang.virus就不能访间Java API的java.lang包中的包可见类,包可见方法及包内可见的成员。
      运行时包的概念是针对使用不同的类装载器装载不同的类而提出的。启动类装载器装载核心Java API的class文件,这些Class文件是最可信的.标准扩展类装载器装载来自于任何标准扩展的class文件,标准扩展是非常可信的,但这个可信度是在一定程度上的可信度,它们不能简单地通过将新类型插人到Java API的包中来获得对包内可见成员的访问权,这是因为标准扩展是由不同于核心API的类装载器装载的。同样,由类路径类装载器在类路径中发现的代码不能访问标准扩展或Java API 中的包内可见成员。
     类装载器可以用另一种方法来保护被信任的类库的边界,它只需通过简单地拒绝装载特定的禁止类型就可以了。例如,你可能己经安装了一些包,这些包中包含了应用程序需要装载的类,这些类必须是由网络类装载器的双亲(类路径类装载器)装载的,而不是由网络类装载器装载的。假设己经创建了一个名为absollltoPower的包,并且将它安装在了本地类路径中的某个地方,在这里它可以被类路径类装载器访问到。而且假设你不想让类装载器装载absollltoPower包中的任何类。在这种情况下,你需要自己编写一个类装载器,让它检查请求装载的类是否是absollltoPower包中的类。如果是的话,你的类装载器就应该抛出一个安全异常,而不是将这个请求传给双亲类装载器。类装载器要知道一个类是否来源于一个被禁止的包,直接检查它的类名(完整形式)就知道了。
   除了设置不同命名空间中的类以及保护被信任的类库的边界外,类装载器还起到另外的安全作用。类装载器将每一个被装载的类放置在一个保护域中,并在保护域名中定义了在运行时它将得到怎样的权限
0 0
原创粉丝点击