动态加载基础一

类加载器

包括BootStrap、Extension、App Classloader以及自定义类加载器。

BootStrap ClassLoader

称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,根类加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,它并没有继承java.lang.ClassLoader类。可通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件:

System.getProperty("sun.boot.class.path");
或者
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
    System.out.println(urls[i].toExternalForm());
} 

Extension ClassLoader

称为扩展类加载器,负责加载Java的扩展类库,它从java.ext.dirs系统属性所指定的目录中加载类库。如果把用户创建的JAR文件放在这个目录下,也会自动由扩展类加载器加载。扩展类加载器是纯Java类。

App ClassLoader

称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类,它是用户自定义的类加载器的默认父加载器。

自定义ClassLoader

除了Java默认提供的三个ClassLoader之外,用户还可以根据需要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类。Java提供的另外二个ClassLoader:Extension ClassLoader和App ClassLoader也是继承ClassLoader类),但是Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

类加载器的关系:

根类加载器 <– 扩展类加载器 <– 系统类加载器 <– 用户自定义加载器

在父亲委托机制中,各个加载器按照父子关系形成了树形结构,除了根类加载器以外,其余的类加载器都有且只有一个父加载器。

加载器之间的父子关系实际上指的是加载器对象之间的包装关系,而不是类之间的继承关系。一对父子加载器可能是同一个加载器类的两个实例,也可能不是。在子加载器对象中包装了一个父加载器对象。

ClassLoader loader1 = new MyClassLoader();
//参数loader1将作为loader2的父加载器
ClassLoader loader2 = new MyClassLoader(loader1);
Class sampleClass = loader2.loadClass("Sample");

loader2首先从自己的命名空间中查找Sample类是否已经被加载,如果已经加载,就直接返回代表Sample类的Class对象的引用。

如果Sample类还没有被加载,loader2首先请求loader1代为加载,loader1再请求系统类加载器代为加载,系统类加载器再请求扩展类加载器代为加载,扩展类加载器在请求根类加载器代为加载。若根加载器和扩展加载器都不能加载,则系统类加载器尝试加载,若能加载成功,则将Sample类所对应的Class对象的引用返回给loader1,loader1再将引用返回给loader2,从而成功将Sample类加载进虚拟机。若系统类加载器不能加载Sample类,则loader1尝试加载Sample类,若loader1也不能成功加载,则loader2尝试加载。若所有的父加载器及laoder2本身都不能加载,则抛出ClassNotFoundException异常。

当生成一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器就将成为该类加载器的父加载器。

命名空间以及安全性

每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类;在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

父委托机制的优点是能够提高软件系统的安全性。因为在此机制下,用户自定义的类加载器不可能加载应该由父加载器加载的可靠类,从而防止不可靠甚至恶意的代码代替由父加载器加载的可靠代码。例如,java.lang.Object类总是由根类加载器加载,其他任何用户自定义的类加载器都不可能加载含有恶意代码的java.lang.Object类。

由同一类加载器加载的属于相同包的类组成了运行时包。决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看定义类加载器是否相同。只有属于同一运行时报的类才能相互访问包可见(即默认访问级别)的类和成员。这样的限制能避免用户自定义的类冒充核心类库的类,去访问核心类库的包可见成员。假设用户自己定义了一个java.lang.Spy,并用用户自定义的类加载器加载,由于java.lang.Spy和核心类库java.lang.*由不同的加载器加载,它们属于不同的运行时包,所以java.lang.Spy不能访问核心类库java.lang包中的包可见成员。

代码:

// From: sun.tools.javac.BatchEnvironment
// The env.class.path property is the user's CLASSPATH
// environment variable, and it set by the wrapper (ie,
// javac.exe).
String classPathString = System.getProperty("env.class.path");
String sysClassPathString = System.getProperty("sun.boot.class.path");
String extDirsString = System.getProperty("java.ext.dirs");

System.out.println("classPathString: " + classPathString);
System.out.println("sysClassPathString" + sysClassPathString);
System.out.println("extDirsString" + extDirsString);

URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
    System.out.println(urls[i].toExternalForm());
}   

打印结果

classPathString: null
sysClassPathString/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/classes
extDirsString/Users/wangshenglong/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/resources.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/rt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/sunrsasign.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jsse.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jce.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/charsets.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/lib/jfr.jar
file:/Library/Java/JavaVirtualMachines/jdk1.7.0_76.jdk/Contents/Home/jre/classes/

测试:

public static void test() {
    Class c;
    ClassLoader cl;
    cl = ClassLoader.getSystemClassLoader();
    System.out.println(cl);
    while (cl != null) {
        cl = cl.getParent();
        System.out.println(cl);
    }
    try {
        c = Class.forName("java.lang.Object");
        cl = c.getClassLoader();
        System.out.println("java.lang.Object's loader is  " + cl);
        c = Class.forName("org.wangliang.asm.LoaderTest");
        cl = c.getClassLoader();
        System.out.println("LoaderTest's loader is  " + cl);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

结果:

    sun.misc.Launcher$AppClassLoader@530f243b
    sun.misc.Launcher$ExtClassLoader@713c817
    null
    java.lang.Object's loader is null
    LoaderTest's loader is sun.misc.Launcher$AppClassLoader@530f243b

    // 第一行表示,系统类装载器实例化自类sun.misc.Launcher$AppClassLoader
    // 第二行表示,系统类装载器的parent实例化自类sun.misc.Launcher$ExtClassLoader
    // 第三行表示,系统类装载器parent的parent为bootstrap
    // 第四行表示,核心类java.lang.Object是由bootstrap装载的
    // 第五行表示,用户类LoaderSample1是由系统类装载器装载的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值