zoukankan      html  css  js  c++  java
  • Java类加载器( 死磕 4)

    【正文】Java类加载器(  CLassLoader ) 死磕 之4: 

    神秘的双亲委托机制

    本小节目录

    4.1. 每个类加载器都有一个parent父加载器
    4.2. 类加载器之间的层次关系
    4.3. 类的加载次序
    4.4 双亲委托机制原理与沙箱机制
    4.5. forName方法和loadClass方法的关系
    4.6. 使用组合而不用继承
    4.7. 各种不同的类加载途径


    4.1.每个类加载器都有一个parent父加载器


    每个类加载器都有一个parent父加载器,比如加载SystemConfig.class是由AppClassLoader完成,那么AppClassLoader也有一个父加载器,怎么样获取呢?很简单,通过getParent方法。

    这里写了一个公共的函数,来取得一个类的加载器的双亲树。代码如下:

        /**
    
         * 迭代,显示class loader 和 父加载器
         */
    
        public static void showLoaderTree(ClassLoader loader) {
    
            while (loader != null) {
    
                Logger.info(loader.toString());
    
                loader = loader.getParent();
    
            }
    
        }
    
    这个函数,不断循环,向上显示了父亲、父亲的父亲、父亲的父亲的父亲..,直到为空。

    这样,就展示了一棵类加载器的双亲树。

    使用上面的函数,演示代码如下:

     private static void loaderTreeDemo() throws ClassNotFoundException
    
        {
    
            String className = "com.crazymakercircle.config.SystemConfig";
    
            Class<?> aClass = Class.forName(className);
    
            ClassLoader aLoader=aClass.getClassLoader();
    
    Logger.info("加载器:"+aLoader.toString());
    
            ClassLoaderUtil.showLoaderTree(aLoader);
    
        }
    



    案例路径:com.crazymakercircle.classLoaderDemo.base.ParentTreeDemo

    案例提示:无编程不创客、无案例不学习。切记,一定要跑案例哦


    案例结果如下:

    loaderTreeDemo |>  加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
    
      showLoaderTree |>  sun.misc.Launcher$AppClassLoader@18b4aac2
    
      showLoaderTree |>  sun.misc.Launcher$ExtClassLoader@6fdb1f78
    


    这个说明,当前加载器类型为AppClassLoader,而AppClassLoader父加载器类是ExtClassLoader。ExtClassLoader的父加载器,又是谁呢? 没有了打印。

    parent为空表示什么意思呢?

    我们先来梳理一下加载器之间的层次关系。


    4.2. 类加载器之间的层次关系


    下面展示一下Bootstrap 启动类加载器、Extention标准扩展类加载器和App应用类加载器三者之间的关系。

    大致整理了如下类似的一幅图片:

    wps612B.tmp

    每一个加载器看护一块自己的地盘。 启动Bootstrap 看护的是核心中的核心地盘。

    前面讲到,ExtClassLoader的父加载器为空。而上图中,ExtClassLoader的父加载器是Bootstrap 启动类加载器。

    实际上,如果一个加载器的parent为空,其父亲加载器就是Bootstrap 启动类加载器。

    如果没有特别的设置,自定义加载的parent,默认为App应用加载器。


    4.3. 类的加载次序


    loadClass 关键源代码,已经在前面有介绍。

    下面用一张图,对于类的加载次序,做进一步的介绍。

    ClassLoader 4.4.3

    一般场景下,加载一个类,是从AppClassLoader开始的。

    基本的步骤如下:

    (1)AppClassLoader查找资源时,不是首先查看自己的地盘是否有这个字节码文件,而是直接委托给父加载器ExtClassLoader。当然,这里有一个假定,就是在AppClassLoader的缓存中,没有找到目标class。比方说,第一次加载一个目标类,这个类是不会在缓存的。

    (2)ExtClassLoader查找资源时,也不是首先查看自己的地盘是否有这个字节码文件,而是直接委托给父加载器BootstrapClassLoader。

    (3)如果父加载器BootstrapClassLoader在其地盘找到,并且加载成功,则直接返回了;反过来,如果在JVM的核心地盘——%sun.boot.class.path% 中没有找到。则回到ExtClassLoader查找其地盘。

    (4)如果父加载器ExtClassLoader在自己的地盘找到,并且加载成功,也直接返回了;反过来,如果在ExtClassLoader的地盘——%java.ext.dirs% 中没有找到。则回到AppClassLoader自己的地盘。

    (5)于是乎,逗了一大圈,终于回到了自己的地盘。还附带了两条件,就是前面的老大们没有搞定,否则也没有AppClassLoader啥事情了。

    (6)AppClassLoader在自己的地盘找到,这个地盘就是%java.class.path%路径下查找。找到就返回。

    (7)最终,如果没有找到,就抛出异常了。

    这个过程,就是一个典型的双亲委托机制的一次执行流程。

    什么是双亲委托机制呢?


    4.4. 双亲委托机制原理与沙箱机制


    双亲委派模型的的原理是:

    如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完全这个加载请求时,子加载器才会尝试自己去加载。

    为什么要使用这种双亲委托模式呢?

    因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。

    双亲委托机制,也就构成了JVM 的类的沙箱机制。

    沙箱机制是由基于双亲委派机制上采取的一种JVM的自我保护机制,假设你要写一个java.lang.String 的类,由于双亲委派机制的原理,此请求会先交给Bootstrap试图进行加载,但是Bootstrap在加载类时首先通过包和类名查找rt.jar中有没有该类,有则优先加载rt.jar包中的类,因此就保证了java的运行机制不会被破坏。


    4.5. forName方法和loadClass方法的关系


    说到这里,顺便回答一下前面提出的一个问题。

    前面提到,explicit显式方式,又分两种方式:

    一是:java.lang.Class的forName()方法;

    二是:java.lang.ClassLoader的loadClass()方法。

    二者的区别和联系是什么呢?

    首先看联系:

    Class.forName使用的是调用者的类加载器loadClass()方法来加载类的。

    其次看区别。

    当调用Class.forName(String)载入class时执行,会完整的完成前面提到的五步工作,就是加载、验证、准备、解析、初始化。

    如果调用ClassLoader.loadClass(String)载入class时,会执行加载、验证、准备、解析的前面四步,并不会执行第五步——初始化。这是,类的static块没有被执行。需要在第一次实例化时执行,比如第一次执行 Class.newInstance() 操作时。


    4.6. 使用组合而不用继承


    4.7. 各种不同的类加载途径


    Java类不是一次性加载的,而是动态被加载到内存。这是java的一大特点,也称为运行时绑定,或动态绑定。

    除了通过Java内置的三大加载器,从JVM中系统属性中设置的三大地盘加载Java类,还存在以下的获取Class文件途径: 

    (1)从ZIP包中读取。很常见,最终成为日后JAR,WAR,EAR格式的基础。

    (2)从网络中获取。这种场景典型的就是Applet。

    (3)运行时计算生成。典型的情景就是java动态代理技术。

    (4)从其他文件中生成。典型场景是JSP应用,即由JSP文件生成对应的Class类。

    (5)从不方便加入到%java.class.path%其他的文件目录获取。

    如何实现以上的途径呢?

    具体的方法是:通自定义的类加载器,去加载其他途径的类。



    源码:


    代码工程:  classLoaderDemo.zip

    下载地址:在疯狂创客圈QQ群文件共享。


    疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
    QQ群链接:
    疯狂创客圈QQ群


    无编程不创客,无案例不学习。 一定记得去跑一跑案例哦


    类加载器   全目录

    1.导入

    2. JAVA类加载器分类

    3. 揭秘ClassLoader抽象基类

    4. 神秘的双亲委托机制

    5. 入门案例:自定义一个文件系统的自定义classLoader

    6. 基础案例:自定义一个网络类加载器

    7. 中级案例:设计一个加密的自定义网络加载器

    8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理

    9. 高级案例2:上下文加载器原理和案例

  • 相关阅读:
    15_门面模式
    14_责任链模式
    13_观察者模式
    12_状态模式
    11_策略模式
    10_命令模式
    09_适配器模式
    08_装饰者模式
    07_代理模式
    linux邮件服务器postfix配置实例
  • 原文地址:https://www.cnblogs.com/crazymakercircle/p/9824121.html
Copyright © 2011-2022 走看看