双亲委派模式

双亲委派模式:类装载器请求另一个类装载器来装载类型的过程。

用户自定义的类装载器经常依赖其他类装载器——至少依赖于虚拟机启动时创建的启动类装载器——来帮助它实现一些类装载请求。

除启动类装载器以外的每一个类装载器,都有一个“双亲”类装载器,在某个特定的类装载器试图以常用方式装载类型以前,它会先默认地将这个任务“委派”给它的双亲——请求它的双亲来装载这个类型。这个双亲再依次请求它的双亲来装载这个类型。这个委派的过程一直向上继续,直到达到启动类装载器,通常启动类装载器是委派链中的最后一个类装载器。如果这个类装载器的双亲类装载器有能力来装载这个类型,则这个类装载器返回这个类型。否则,这个类装载器试图自己来装载这个类型。

设想在运行Java应用程序的过程中,类装载器发出一个装载Bag类的请求,类装载器必须先询问它的双亲——类路径装载器——来查找并装载这个类。这个类装载器依次将向它的双亲发出同样的请求,它的双亲为已安装扩展的类装载器。这个类装载器首先将这个请求委派给它自己的双亲——启动类装载器。

类装载器的工作背景:
假设Bag类不是Java API的一部分,也不是一个已安装扩展的一部分,也不在类路径上。当类路径装载器回答:它和它的双亲都不能装载Bag类时,你的类装载器开始尝试装载这个类。

假设某一刻Bag类的一个方法首次被调用,并且这个方法引用了Java API中的类java.util.HashMap,因为这个引用是首次被运行的程序使用,所以虚拟机会请求你的类装载器(装载Bag的类装载器)来装载java.util.HashMap。和之前一样,你的类装载器首先将请求传递给它的双亲装载器,然后一直委派给启动类装载器。这一次启动类装载器可以将java.util.HashMap类返回给你的类装载器(启动类装载器可以找到这个类,所以已安装扩展的类装载器、类路径装载器不必再查找这个类型),同样你的类装载器也不必从网上下载这个类。所有的类装载器只需要返回由启动类装载器返回的类java.util.HashMap。从这一时刻开始,不管何时Bag类引用名为java.util.HashMap的类,虚拟机就可以直接使用这个java.util.HashMap类了。

如何使用类装载器来保护可信任类库:

类装载器的体系结构是通过剔除装作被信任的不可靠类,来保护可信任类库的边界的。

在有双亲委派模式的情况下,启动类装载器可以抢在标准扩展类装载器之前去装载类;而标准扩展类装载器可以抢在类路径装载器之前去装载那个类;类路径装载器又可以抢先在网络装载器之前去装载它。

所以,如果网络类装载器试图装载java.lang.Integer,它将不能成功(java.lang.Integer的class文件已经在Java API中存在)。网络类装载器只能使用它的双亲返回的类。

用这种方法,类装载器的体系结构就可以防止不可靠的代码用它们自己的版本来替代可信任的类。

但是,如果这个移动代码不是去替换一个被信任的类,而是想在一个被信任的包中插入一个名为全新的类型呢?

eg:要求网络类装载器装载一个名为java.lang.Virus的类,像以前一样,这个请求一路委派给启动类装载器,启动类装载器负责装载核心Java API的class文件,包括名为java.lang的包,但它无法在包java.lang中找到名为Virus的成员。假设这个类在已安装扩展以及本地类路径中也找不到,你的类装载器将试图从网上下载这个类。

假设你的类装载器成功下载并定义了这个名为java.lang.Virus的类,Java允许在同一个包中的类拥有彼此访问的特殊权限,而这个包外的类则没有这个权限。因为你的类装载器装载了一个名为java.lang.Virus的类,暗示这个类是Java API的一部分,
它将得到访问java.lang中被信任类的特殊访问权限,并且可以使用这个特殊访问权限达到不可告人的目的。

类装载器机制可以防止这个代码得到访问java.lang包中被信任类的访问权限,因为Java虚拟机只把彼此访问的特殊权限授予由同一个类装载器装载到同一个包中的类型。

因为Java API的java.lang包中的被信任的类是由启动类装载器装载的,而恶意的java.lang.Virus是由网络类装载器装载的,所以这些类型不属于同一个运行时包。

运行时包:指由同一个类装载器装载的、属于同一个包的、多个类型的集合。

在允许两个类型之间对包内可见的成员(声明为受保护的或包访问的成员)进行访问之前,虚拟机不但要确定这两个类型属于同一个包,还必须确认它们属于同一个运行时包——它们必须是由同一个类装载器装载的。

这样,因为java.lang.Virus和来自核心Java API的java.lang的成员不属于同一个运行时包,java.lang.Virus就不能访问Java API的java.lang包中的类型和包内可见的成员。

运行时包的概念,动机之一是使用不同的类装载器装载不同的类。

启动类装载器装载核心Java API的class文件,这些class文件是最可信的。已安装扩展的类装载器来自于任何已安装扩展的class文件,已安装扩展是非常可信的,但这个可信度是在一定程度上的可信度,它们不能简单地通过将新类型插入到Java API的包中来获得对包内可见成员的访问权,这是因为已安装扩展是由不同于核心API的类装载器装载的。同样,由类路径装载器在类路径中发现的代码不能访问已安装扩展和Java API中的包内可见成员。

保护被信任类库的边界的另一种方法:
类装载器可以用另一种方法来保护被信任的类库的边界,只需要通过简单的拒绝装载特定的禁止类型就可以了。
假设已经创建了一个名为absolutepower的包,并且将它安装在了本地类路径中的某个地方,能装载来自absolutepower包中任何的类。在这种情况下,编写自己的类装载器,让它做的第一件事就是确认被请求的类不是absolutepower包中的一个成员,如果这样的类被请求装载,你的类装载器将抛出一个安全异常,而不是将这个类的名字传递给双亲类装载器。
类装载器要知道一个类是否来源于一个被禁止的包,eg:absolutepower,只有一个办法,就是通过他的类名来检测。类名absolutepower.FancyClassLoader指明了它是包absolutepower的一部分,而包absolutepower被列在禁止的包列表中,所以你的类装载器应该立即抛出一个安全异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值