`

java之classloader体系结构(含hotswap)

    博客分类:
  • j2se
阅读更多

jvm classLoader architecture :

a, Bootstrap ClassLoader/启动类加载器
主要负责jdk_home/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作.

 

b, Extension ClassLoader/扩展类加载器
主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作

 

c, System ClassLoader/系统类加载器
主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作.

 

b, User Custom ClassLoader/用户自定义类加载器(java.lang.ClassLoader的子类)
在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性.

 

类加载器的特性:

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


如下图:
java class loader

Class Diagram:
classloader 类图

类图中, BootstrapClassLoader是一个单独的java类, 其实在这里, 不应该叫他是一个java类。
因为, 它已经完全不用java实现了。

 

它是在jvm启动时, 就被构造起来的, 负责java平台核心库。(具体上面已经有介绍)

启动类加载实现 (其实我们不用关心这块, 但是有兴趣的, 可以研究一下 ):
bootstrap classLoader 类加载原理探索

 

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

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

 

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

 

Java代码 复制代码
  1. // 检查类是否已被装载过   
  2. Class c = findLoadedClass(name);   
  3. if (c == null ) {   
  4.      // 指定类未被装载过   
  5.      try {   
  6.          if (parent != null ) {   
  7.              // 如果父类加载器不为空, 则委派给父类加载   
  8.              c = parent.loadClass(name, false );   
  9.          } else {   
  10.              // 如果父类加载器为空, 则委派给启动类加载加载   
  11.              c = findBootstrapClass0(name);   
  12.          }   
  13.      } catch (ClassNotFoundException e) {   
  14.          // 启动类加载器或父类加载器抛出异常后, 当前类加载器将其   
  15.          // 捕获, 并通过findClass方法, 由自身加载   
  16.          c = findClass(name);   
  17.      }   
  18. }  
// 检查类是否已被装载过
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);
     }
}

 

用Class.forName加载类
Class.forName使用的是被调用者的类加载器来加载类的.
这种特性, 证明了java类加载器中的名称空间是唯一的, 不会相互干扰.

即在一般情况下, 保证同一个类中所关联的其他类都是由当前类的类加载器所加载的.

 

Java代码 复制代码
  1. public static Class forName(String className)   
  2.      throws ClassNotFoundException {   
  3.      return forName0(className, true , ClassLoader.getCallerClassLoader());   
  4. }   
  5.     
  6. /** Called after security checks have been made. */  
  7. private static native Class forName0(String name, boolean initialize,   
  8. ClassLoader loader)   
  9.      throws ClassNotFoundException;  
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方法的类的类加载器

 

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

 

Java代码 复制代码
  1. // Now create the class loader to use to launch the application   
  2. try {   
  3.     loader = AppClassLoader.getAppClassLoader(extcl);   
  4. catch (IOException e) {   
  5.     throw new InternalError(   
  6. "Could not create application class loader" );   
  7. }   
  8.     
  9. // Also set the context class loader for the primordial thread.   
  10. Thread.currentThread().setContextClassLoader(loader);  
// 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);

 

以上代码摘自sun.misc.Launch的无参构造函数Launch()。

使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类.
典型的例子有, 通过线程上下文来加载第三方库jndi实现, 而不依赖于双亲委派.

大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
还有一些采用 hotswap 特性的框架, 也使用了线程上下文类加载器, 比如 seasar (full stack framework in japenese).

线程上下文从根本解决了一般应用不能违背双亲委派模式的问题.

使java类加载体系显得更灵活.

 

随着多核时代的来临, 相信多线程开发将会越来越多地进入程序员的实际编码过程中. 因此,
在编写基础设施时, 通过使用线程上下文来加载类, 应该是一个很好的选择.

 

当然, 好东西都有利弊. 使用线程上下文加载类, 也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,
防止因为不同的类加载器, 导致类型转换异常(ClassCastException).

 

自定义的类加载器实现
defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)
是java.lang.Classloader提供给开发人员, 用来自定义加载class的接口.

使用该接口, 可以动态的加载class文件.

 

例如,
在jdk中, URLClassLoader是配合findClass方法来使用defineClass, 可以从网络或硬盘上加载class.

而使用类加载接口, 并加上自己的实现逻辑, 还可以定制出更多的高级特性.

 

比如,

一个简单的hot swap 类加载器实现:

 

 

Java代码 复制代码
  1. import java.io.File;   
  2. import java.io.FileInputStream;   
  3. import java.lang.reflect.Method;   
  4. import java.net.URL;   
  5. import java.net.URLClassLoader;   
  6.     
  7. /**  
  8. * 可以重新载入同名类的类加载器实现  
  9. *  
  10.    
  11. * 放弃了双亲委派的加载链模式.  
  12. * 需要外部维护重载后的类的成员变量状态.  
  13. *  
  14. * @author ken.wu  
  15. * @mail ken.wug@gmail.com  
  16. * 2007-9-28 下午01:37:43  
  17. */  
  18. public class HotSwapClassLoader extends URLClassLoader {   
  19.     
  20.     public HotSwapClassLoader(URL[] urls) {   
  21.         super (urls);   
  22.     }   
  23.     
  24.     public HotSwapClassLoader(URL[] urls, ClassLoader parent) {   
  25.         super (urls, parent);   
  26.     }   
  27.     
  28.     public Class load(String name)   
  29.           throws ClassNotFoundException {   
  30.         return load(name, false );   
  31.     }   
  32.     
  33.     public Class load(String name, boolean resolve)   
  34.           throws ClassNotFoundException {   
  35.         if ( null != super .findLoadedClass(name))   
  36.             return reload(name, resolve);   
  37.     
  38.         Class clazz = super .findClass(name);   
  39.     
  40.         if (resolve)   
  41.             super .resolveClass(clazz);   
  42.     
  43.         return clazz;   
  44.     }   
  45.     
  46.     public Class reload(String name, boolean resolve)   
  47.           throws ClassNotFoundException {   
  48.         return new HotSwapClassLoader( super .getURLs(), super .getParent()).load(   
  49.             name, resolve);   
  50.     }   
  51. }   
  52.     
  53. public class A {   
  54.     private B b;   
  55.     
  56.     public void setB(B b) {   
  57.          this .b = b;   
  58.     }   
  59.     
  60.     public B getB() {   
  61.          return b;   
  62.     }   
  63. }   
  64.     
  65. public class B {}  
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
 
/**
* 可以重新载入同名类的类加载器实现
*
 
* 放弃了双亲委派的加载链模式.
* 需要外部维护重载后的类的成员变量状态.
*
* @author ken.wu
* @mail ken.wug@gmail.com
* 2007-9-28 下午01:37:43
*/
public class HotSwapClassLoader extends URLClassLoader {
 
    public HotSwapClassLoader(URL[] urls) {
        super (urls);
    }
 
    public HotSwapClassLoader(URL[] urls, ClassLoader parent) {
        super (urls, parent);
    }
 
    public Class load(String name)
          throws ClassNotFoundException {
        return load(name, false );
    }
 
    public Class load(String name, boolean resolve)
          throws ClassNotFoundException {
        if ( null != super .findLoadedClass(name))
            return reload(name, resolve);
 
        Class clazz = super .findClass(name);
 
        if (resolve)
            super .resolveClass(clazz);
 
        return clazz;
    }
 
    public Class reload(String name, boolean resolve)
          throws ClassNotFoundException {
        return new HotSwapClassLoader( super .getURLs(), super .getParent()).load(
            name, resolve);
    }
}
 
public class A {
    private B b;
 
    public void setB(B b) {
         this .b = b;
    }
 
    public B getB() {
         return b;
    }
}
 
public class B {}

 

 

这个类的作用是可以重新载入同名的类, 但是, 为了实现hotswap, 老的对象状态
需要通过其他方式拷贝到重载过的类生成的全新实例中来。(A类中的b实例)

而新实例所依赖的B类如果与老对象不是同一个类加载器加载的, 将会抛出类型转换异常(ClassCastException).

为了解决这种问题, HotSwapClassLoader自定义了load方法. 即当前类是由自身classLoader加载的, 而内部依赖的类

 

还是老对象的classLoader加载的.

 
Java代码 复制代码
  1. public class TestHotSwap {   
  2. public static void main(String args[]) {   
  3.     A a = new A();   
  4.     B b = new B();   
  5.     a.setB(b);   
  6.     
  7.     System.out.printf("A classLoader is %s n" , a.getClass().getClassLoader());   
  8.     System.out.printf("B classLoader is %s n" , b.getClass().getClassLoader());   
  9.     System.out.printf("A.b classLoader is %s n" ,   a.getB().getClass().getClassLoader());   
  10.     
  11.     HotSwapClassLoader c1 = new HotSwapClassLoader( new URL[]{ new URL( "file:\e:\test\")} , a.getClass().getClassLoader());   
  12.     Class clazz = c1.load(" test.hotswap.A ");   
  13.     Object aInstance = clazz.newInstance();   
  14.     
  15.     Method method1 = clazz.getMethod(" setB ", B.class);   
  16.     method1.invoke(aInstance, b);   
  17.     
  18.     Method method2 = clazz.getMethod(" getB "null);   
  19.     Object bInstance = method2.invoke(aInstance, null);   
  20.     
  21.     System.out.printf(" reloaded A.b classLoader is %s n", bInstance.getClass().getClassLoader());   
  22. }   
  23. }  
public class TestHotSwap {
public static void main(String args[]) {
    A a = new A();
    B b = new B();
    a.setB(b);
 
    System.out.printf("A classLoader is %s n" , a.getClass().getClassLoader());
    System.out.printf("B classLoader is %s n" , b.getClass().getClassLoader());
    System.out.printf("A.b classLoader is %s n" ,   a.getB().getClass().getClassLoader());
 
    HotSwapClassLoader c1 = new HotSwapClassLoader( new URL[]{ new URL( "file:\e:\test\")} , a.getClass().getClassLoader());
    Class clazz = c1.load(" test.hotswap.A ");
    Object aInstance = clazz.newInstance();
 
    Method method1 = clazz.getMethod(" setB ", B.class);
    method1.invoke(aInstance, b);
 
    Method method2 = clazz.getMethod(" getB ", null);
    Object bInstance = method2.invoke(aInstance, null);
 
    System.out.printf(" reloaded A.b classLoader is %s n", bInstance.getClass().getClassLoader());
}
}
 

 

输出

A classLoader is sun.misc.Launcher$AppClassLoader@19821f
B classLoader is sun.misc.Launcher$AppClassLoader@19821f
A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f
reloaded A.b classLoader is sun.misc.Launcher$AppClassLoader@19821f

分享到:
评论

相关推荐

    classloader体系结构(含hotswap)

    Java的类加载器(ClassLoader)体系结构是JVM(Java虚拟机)中至关重要的一部分,它负责将类的字节码转换为运行时的类实例。本文将深入探讨启动类加载器、扩展类加载器、系统类加载器以及用户自定义类加载器,同时还...

    破解java加密的ClassLoader.java,在classloader植入破解代码

    破解java加密的ClassLoader.java,在classloader植入破解代码

    Java ClassLoader定制实例

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作原理以及如何定制它,对于深入学习Java的运行机制和进行高级应用开发具有重要意义。本篇文章将...

    java中classLoader的使用

    Java中的类加载器(ClassLoader)是Java虚拟机(JVM)的一个重要组成部分,它负责将类的.class文件从文件系统或者网络中加载到内存中,并转换为对应的Class对象。类加载器的工作流程主要包括加载、验证、准备、解析...

    java classloader

    Java ClassLoader是一个核心的Java运行时组件,负责加载类到Java虚拟机(JVM)中。它是Java平台的独特特性,因为它允许动态加载类,增强了软件的可扩展性和灵活性。这篇博文(虽然链接不可用)可能深入探讨了...

    java classloader classpath 张孝祥

    ### Java ClassLoader与ClassPath详解 #### 一、概述 在Java编程中,类加载机制是十分关键的一个环节。类加载器(`ClassLoader`)负责将编译后的`.class`文件加载到Java虚拟机(JVM)中执行,而类路径(`ClassPath...

    java 体系结构图,不错的总结,复习的资料

    Java 体系结构图是理解Java编程语言及其生态系统的关键工具,尤其对于初学者而言,它能够清晰地展现Java的各个组成部分以及它们之间的关系。这个压缩包包含的“体系结构图”很可能是以图形形式展示了Java的核心概念...

    java ClassLoader机制及其在OSGi中的应用

    二、ClassLoader的体系结构 ClassLoader形成一个树状结构,BootstrapClassLoader是根节点,ExtensionClassLoader是其子节点,SystemClassLoader是ExtensionClassLoader的子节点。用户还可以自定义ClassLoader,插入...

    测试普通Java程序ClassLoader等级关系的Demo程序

    在Java的ClassLoader层次结构中,主要有以下几类加载器: 1. **Bootstrap ClassLoader**:这是最基础的类加载器,由JVM直接创建,负责加载JDK的根目录(rt.jar等)中的核心类库。 2. **Extension ClassLoader**:...

    探究java的ClassLoader及类变量初始化顺序

    在Java编程语言中,ClassLoader是核心组件之一,它负责加载类到JVM(Java虚拟机)中。理解ClassLoader的工作机制以及类变量初始化的顺序对于深入理解Java运行时环境至关重要。这篇博文将探讨这两个主题。 首先,让...

    理解Java ClassLoader机制

    Java ClassLoader机制是Java运行时环境中的核心组件之一,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。理解ClassLoader的工作原理对于优化应用性能、处理类加载问题以及实现自定义加载器至关重要。 首先...

    Java_ClassLoader详解

    在 Java 语言中,类加载器(ClassLoader)是 Java 运行时环境的核心组成部分之一,它负责将编译后的 `.class` 文件加载到 JVM 中执行。从 JDK 1.0 开始,随着 Java Applet 的出现以及网络应用的需求增加,类加载机制...

    Java ClassLoader原理

    ### Java ClassLoader原理详解 #### 摘要 本文探讨了Java虚拟机(JVM)中的一个重要特性:动态类加载(Dynamic Class Loading)。这一机制为Java平台提供了强大的能力,允许在运行时安装软件组件,例如从网络下载...

    java中ClassLoader

    ClassLoader 的层次结构是双亲委托模型,每个 ClassLoader 实例都有一个父类加载器的引用(不是继承关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它 ...

    Java实现热加载完整代码;Java动态加载class;Java覆盖已加载的class;Java自定义classloader

    让Java支持热加载是个不错的想法。如何做到的呢? 1. 定义好接口和实现类 2. 让代理类通过反射的方式调用实现类,对外暴露的是代理类。 3. 自定义URLClassLoader。检查实现类.class文件的...Java自定义classloader;

    Java ClassLoader学习总结

    Java ClassLoader学习总结 Java 类加载机制是 Java 中一个非常重要的机制,它负责加载 Class 文件到 JVM,以供程序使用。ClassLoader 是 Java 中的一个抽象类,它的主要作用是加载 Class 文件到 JVM 中。...

    java的ClassLoader类加载器机制

    Java的ClassLoader类加载器机制 在 Java 虚拟机(JVM)中,类加载器(ClassLoader)扮演着非常重要的角色。类加载器负责加载 Java 类,包括核心类和用户自定义类。在 JVM 运行过程中,类加载器会形成一个层次结构,...

Global site tag (gtag.js) - Google Analytics