java classLoader 体系结构

vm classLoader architecture

  1. Bootstrap ClassLoader/启动类加载器 
    主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作。
  2. Extension ClassLoader/扩展类加载器 
    主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。
  3. System ClassLoader/系统类加载器 
    主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。
  4. User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类) 
    在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

类加载器的特性:

  1. 每个ClassLoader都维护了一份自己的名称空间, 同一个名称空间里不能出现两个同名的类。
  2. 为了实现java安全沙箱模型顶层的类加载器安全机制, java默认采用了 " 双亲委派的加载链 " 结构。

 

类图中, BootstrapClassLoader是一个单独的java类, 其实在这里, 不应该叫他是一个java类。因为,它已经完全不用java实现了。它是在jvm启动时, 就被构造起来的, 负责java平台核心库。

自定义类加载器加载一个类的步骤

 

ClassLoader 类加载逻辑分析,以下逻辑是除 BootstrapClassLoader 外的类加载器加载流程:

// 检查类是否已被装载过  
Class c = findLoadedClass(name);  
if (c == null ) {  
     // 指定类未被装载过  
     try {  
         if (parent != null ) {  
             // 如果父类加载器不为空, 则委派给父类加载  
             c = parent.loadClass(name, false );  
         } else {  
             // 如果父类加载器为空, 则委派给启动类加载加载  
             c = findBootstrapClass0(name);  
         }  
     } catch (ClassNotFoundException e) {  
         // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其  
         // 捕获, 并通过findClass方法, 由自身加载  
         c = findClass(name);  
     }  
}  

 

线程上下文类加载器

java默认的线程上下文类加载器是 系统类加载器(AppClassLoader)。

// Now create the class loader to use to launch the application  
try {  
    loader = AppClassLoader.getAppClassLoader(extcl);  
} catch (IOException e) {  
    throw new InternalError(  
"Could not create application class loader" );  
}   
// Also set the context class loader for the primordial thread.  
Thread.currentThread().setContextClassLoader(loader);  

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

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

    java动态载入class的两种方式:

     

     

    1. mplicit隐式,即利用实例化才载入的特性来动态载入class

    2. explicit显式方式,又分两种方式:

    • java.lang.Class的forName()方法
    • java.lang.ClassLoader的loadClass()方法

     

     

     

    用Class.forName加载类

    Class.forName使用的是被调用者的类加载器来加载类的。
    这种特性, 证明了java类加载器中的名称空间是唯一的, 不会相互干扰。
        即在一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的。

    public static Class forName(String className)  
         throws ClassNotFoundException {  
         return forName0(className, true , ClassLoader.getCallerClassLoader());  
    }   
      
    /** Called after security checks have been made. */  
    private static native Class forName0(String name, boolean initialize,  
    ClassLoader loader)  
         throws ClassNotFoundException;  

     上面中 ClassLoader.getCallerClassLoader 就是得到调用当前forName方法的类的类加载器。

    static块在什么时候执行?

    • 当调用forName(String)载入class时执行,如果调用ClassLoader.loadClass并不会执行.forName(String,false,ClassLoader)时也不会执行。
    • 如果载入Class时没有执行static块则在第一次实例化时执行.比如new ,Class.newInstance()操作。
    • static块仅执行一次。

    各个java类由哪些classLoader加载?

    • java类可以通过实例.getClass.getClassLoader()得知。
    • 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()获得实例)载入。
    • ClassLoader类由bootstrap loader载入

    NoClassDefFoundError和ClassNotFoundException

    • NoClassDefFoundError:当java源文件已编译成.class文件,但是ClassLoader在运行期间在其搜寻路径load某个类时,没有找到.class文件则报这个错。
    • ClassNotFoundException:试图通过一个String变量来创建一个Class类时不成功则抛出这个异常。

    参考:

    其它相关 classloader:

     

     

     

     

  2. 考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值