欢迎访问 生活随笔!

尊龙游戏旗舰厅官网

当前位置: 尊龙游戏旗舰厅官网 > 编程资源 > 编程问答 >内容正文

编程问答

类加载器、双亲委派模型 -尊龙游戏旗舰厅官网

发布时间:2024/10/14 编程问答 25 豆豆
尊龙游戏旗舰厅官网 收集整理的这篇文章主要介绍了 类加载器、双亲委派模型 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

目录

1.简介

2.类和类加载器

3.双亲委派模型

3.1 启动类加载器:

3.2 扩展类加载器

3.3应用程序类加载器

3.4  类加载器的双亲委派模型(parents delegation model)

双亲委派模型的工作过程:

好处:

实现:

4.破坏双亲委派模型


虚拟机设计团队把类加载阶段的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作。实现这个动作的代码模块称为“类加载器”。

 

它是在jvm外部实现的,以便让应用程序自己决定如何获取所需要的类。

对于任何一个类,都需要由加载它的类加载器和这个类本身一同确立其在jvm中的唯一性。

每一个类加载器,都有一个独立的类名称空间。即,比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使两个类来源于同一个class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类就必定不相等。

  • “相等”:包括代表类对象的equals()方法、isassignablefromo()方法、isinstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。
  • 如果没有注意到类加载器的影响,在某些情况下,可能会产生具有迷惑性的结果,如下代码:
package csdnpattern;import java.io.ioexception; import java.io.inputstream;/* *类加载器与instanceof关键字演示 */ public class test{public static void main(string[] args) throws exception{classloader myloader=new classloader() {@overridepublic class loadclass(string name) throws classnotfoundexception{try {string filename=name.substring(name.lastindexof(".") 1) ".class";inputstream is=getclass().getresourceasstream(filename);if(is==null) {return super.loadclass(name);}byte[] b=new byte[is.available()];is.read(b);return defineclass(name,b,0,b.length);}catch(ioexception e) {throw new classnotfoundexception(name);}}};object obj=myloader.loadclass("csdnpattern.test").newinstance();system.out.println(obj.getclass());system.out.println(obj instanceof csdnpattern.test);} }

输出:

上述代码构造了一个简单的类加载器。

  • 可加载与自己同一路径下的class文件。
  • 使用这个类加载器加载了一个名为“csdnpattern.test”的类,并实例化了这个类的对象。
  • 输出结果中,第一句表明这个类确实是“csdnpattern.test”;但第二句发现这个类和“csdnpattern.test”做所属类型检查的时候却返回了false。
    • 因为虚拟机中存在了两个test类,一个室友系统应用程序类加载器加载的,一个是由自定义的类加载器加载的。
    • 虽然都是来自同一个class文件,但是是两个独立的类,做对象所属类型检查时结果自然为false。

从jvm角度来看,只存在两种不同的类加载器:

  • 启动类加载器(bootstrap clasloader):使用c 实现,是虚拟机自身的一部分;
  • 所有其他的类加载器:由java实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.object。

从java开发人员角度,还可以分的更细,绝大部分java程序都会用到一下3种系统提供的类加载器:

3.1 启动类加载器:

  • 负责将存放在\lib目录里的,或者被-xbootclasspath参数指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。
  • 它无法被java程序直接引用。
  • 用户在编写自定义类加载器时,如果需要把加载请求为派给引导类加载器,纳智捷使用null代替即可。如下代码所示(提示错)
package csdnpattern;/** return the class loader for the class.* some implementations may use null to present the bootstrap class loader.* this method will return null in such implementations * if this class was loaded by the bootstrap class loader.*/ public class test{public classloader getclassloader() {classloader cl=getclassloader();if(cl==null)return null;securitymanager sm=system.getsecuritymanager();if(sm!=null) {classloader ccl=classloader.getcallerclassloader();if(ccl!=null&&ccl!=cl&&!cl.isancestor(ccl))sm.checkpermission(securityconstants.get_classloader_permission);}}return cl; }

3.2 扩展类加载器

extension classloader

负责加载\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以使用扩展类加载器。

3.3应用程序类加载器

application classloader

它是classloader中的getsystemclassloader()放的返回值,所有一般也称为系统类加载器。

它负责加载用户类路径(classpath)上所指定的类库,开发者可以直接使用这个类加载器。

如果应用程序中没有自定义过自己的类加载器,一般情况系下这个就是程序中的默认的类加载器。

 

我们的应用陈香菇都是由这3种类加载器相互配合进行加载的。

如有必要,还可以加入自定义的类加载器。

3.4  类加载器的双亲委派模型(parents delegation model)

这些类加载器的关系一般如下图所示:

图示的这种类加载器之间的这种层次关系,称为类加载器的双亲委派模型(parents delegation model)

双亲委派模型中要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。

这里的类加载器之间的父子关系一般不会以继承(inheritance)的关系来实现,而是使用组合(composition)关系来复用父加载器的代码。

类加载器的双亲委派模型在jdk1.2期间被引入并被广泛应用于之后几乎所有的java程序中。

但它并不是一个强制性的约束模型,而是java设计者推荐给开发者的一种类加载器实现方式。

3.4.1 双亲委派模型的工作过程

  • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求为派给父类加载器去完成;
  • 每一个层次的类加载器都是如此;
  • 因此所有的加载请求最终都应该传送到顶层的启动类加载器中;
  • 只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
  • 3.4.2 好处

    java类随着它的类加载器一起举杯了一种带有优先级的层次关系。

    例如,类java.lang.object,它存放在rt.jar中,无论哪一个类加载器都要加载这个类,最终都是为派给处于模型最顶端的启动类加载器进行加载。因此object类在程序的各个类加载器环境中都是同一个类。

    相反,如果没有使用这个模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.object的类,并放在程序classpath中,那系统将会出现多个不同的object类。java类型体系中最基础的行为也就无法保证,应用程序也变得一片混乱。

    3.4.3 实现

    实现这个模型的代码都集中在在java.lang.classloader的loadclass()中。如下代码所示:

    {//首先检查请求的类是否已经被加载过了class c=findloadedclass(name);if(c==null) {try {if(parent!=null)c=parent.loadclass(name,false);elsec=findbootstrapclassornull(name);}catch(classnotfoundexception e) {//如果父类加载器 抛出classnotfoundexception//说明父类加载器无法完成加载请求}if(c==null) {//在父类加载器无法完成加载的时候//再调用本身的findclass方法来进行类加载c=findclass(name);}}if(resolve) {resolveclass(c);} }

    逻辑:

  • 先检查是否已经被加载过,
  • 若没有,则调用父类加载器的loadclass()方法;
  • 若父类加载器为空,则默认使用启动类加载器作为父类加载器。
  • 若父类加载器失败,抛出classnotfoundexception异常后,再调用自己的findclass()进行加载。
  • 双亲委派模型主要出现过3次较大规模的“被破坏”情况;

  • 在双亲委派模型出现之前,即jdk1.2发布之前。
  • 为了向前兼容,jdk1.2之后的java.lang.classloader添加了一个新的protected方法findclass()。
  • jdk1.2之后不提倡用户再去覆盖loadclass(),而应当把自己的类加载器逻辑写到findclass()中。
  • 在loadclass()的逻辑中,若父类加载失败,则会调用自己的findclass()来完成加载。
  • 这样就可以保证新写出来的类加载器是符合双亲委派模型的。
  • 此模型自身的缺陷导致
  • 它很好地解决了各个类加载器的基础类统一问题(越基础的类由越上层的加载器进行加载)。
  • 基础类:基础的原因是它们总被作为用户代码调用的api。
  • 但是当基础类调用用户代码怎么办?比如jndi服务
  • jndi现在已是java的标准服务。
  • 目的就是对资源进行集中种管理和查找。
  • 需要调用由独立厂商实现并部署在应用程序的classpath下的jndi接口的提供者(spi, service provider interface)的代码。
  • 引入了线程上下文类加载器(thread context classloader)来解决启动类加载器不认识这些代码的问题。
  • 它可以通过国java.alng.thread类的setcontextclassloader()进行设置。
  • 如果创建时还未设置,它将会从父线程中继承一个;
  • 如果在应用程序的全局范围内都没有设置过的话,那这个类加载器就是应用程序类的加载器。
  • java中所有涉及spi的加载动作基本上都采用这种方式:jndi/jdbc/jce/jaxb/jbi等
  • 由于用户对程序动态性的追求导致
  • 动态性:代码热替换、模块热部署等,就是希望应用程序能够像计算机外设一样,接上鼠标,u盘,不用重启机器就能立即使用。
  • 整理自《深入理解java虚拟机——jvm高级特性与最佳实践》

    总结

    以上是尊龙游戏旗舰厅官网为你收集整理的类加载器、双亲委派模型的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得尊龙游戏旗舰厅官网网站内容还不错,欢迎将尊龙游戏旗舰厅官网推荐给好友。

    • 上一篇:
    • 下一篇:
    网站地图