04_JVM学习笔记_ClassLoader源码分析

ClassLoader doc

/**
 * A class loader is an object that is responsible for loading classes. The
 * class ClassLoader is an abstract class.  Given the binary name of a class, a class loader should attempt to
 * locate or generate data that constitutes a definition for the class.  A
 * typical strategy is to transform the name into a file name and then read a
 * "class file" of that name from a file system.
 *
 * 

Every {@link Class Class} object contains a {@link * Class#getClassLoader() reference} to the ClassLoader that defined * it. * *

Class objects for array classes are not created by class * loaders, but are created automatically as required by the Java runtime. * The class loader for an array class, as returned by {@link * Class#getClassLoader()} is the same as the class loader for its element * type; if the element type is a primitive type, then the array class has no * class loader. * *

Applications implement subclasses of ClassLoader in order to * extend the manner in which the Java virtual machine dynamically loads * classes. * *

Class loaders may typically be used by security managers to indicate * security domains. * *

The ClassLoader class uses a delegation model to search for * classes and resources. Each instance of ClassLoader has an * associated parent class loader. When requested to find a class or * resource, a ClassLoader instance will delegate the search for the * class or resource to its parent class loader before attempting to find the * class or resource itself. The virtual machine's built-in class loader, * called the "bootstrap class loader", does not itself have a parent but may * serve as the parent of a ClassLoader instance. * *

Class loaders that support concurrent loading of classes are known as * parallel capable class loaders and are required to register * themselves at their class initialization time by invoking the * {@link * #registerAsParallelCapable ClassLoader.registerAsParallelCapable} * method. Note that the ClassLoader class is registered as parallel * capable by default. However, its subclasses still need to register themselves * if they are parallel capable.
* In environments in which the delegation model is not strictly * hierarchical, class loaders need to be parallel capable, otherwise class * loading can lead to deadlocks because the loader lock is held for the * duration of the class loading process (see {@link #loadClass * loadClass} methods). * *

Normally, the Java virtual machine loads classes from the local file * system in a platform-dependent manner. For example, on UNIX systems, the * virtual machine loads classes from the directory defined by the * CLASSPATH environment variable. * *

However, some classes may not originate from a file; they may originate * from other sources, such as the network, or they could be constructed by an * application. The method {@link #defineClass(String, byte[], int, int) * defineClass} converts an array of bytes into an instance of class * Class. Instances of this newly defined class can be created using * {@link Class#newInstance Class.newInstance}. * *

The methods and constructors of objects created by a class loader may * reference other classes. To determine the class(es) referred to, the Java * virtual machine invokes the {@link #loadClass loadClass} method of * the class loader that originally created the class. * *

For example, an application could create a network class loader to * download class files from a server. Sample code might look like: * *

 *   ClassLoader loader = new NetworkClassLoader(host, port);
 *   Object main = loader.loadClass("Main", true).newInstance();
 *        . . .
 * 
* *

The network class loader subclass must define the methods {@link * #findClass findClass} and loadClassData to load a class * from the network. Once it has downloaded the bytes that make up the class, * it should use the method {@link #defineClass defineClass} to * create a class instance. A sample implementation is: * *

 *     class NetworkClassLoader extends ClassLoader {
 *         String host;
 *         int port;
 *
 *         public Class findClass(String name) {
 *             byte[] b = loadClassData(name);
 *             return defineClass(name, b, 0, b.length);
 *         }
 *
 *         private byte[] loadClassData(String name) {
 *             // load the class data from the connection
 *              . . .
 *         }
 *     }
 * 
* *

Binary names

* *

Any class name provided as a {@link String} parameter to methods in * ClassLoader must be a binary name as defined by * The Java™ Language Specification. * *

Examples of valid class names include: *

 *   "java.lang.String"
 *   "javax.swing.JSpinner$DefaultEditor"
 *   "java.security.KeyStore$Builder$FileBuilder$1"
 *   "java.net.URLClassLoader$3$1"
 * 
* * @see #resolveClass(Class) * @since 1.0 */

一个类加载器是一个对象用于负责加载类。类ClassLoader是一个抽象类。给定了一个类的binary name(二进制名字),类加载器应该尝试的区定位或者生成构成类定义的数据。一种典型的策略是将给定的二进制名字转换为文件名,然后从文件系统读取该名字的二进制文件。

每一个Class对象都包含一个定义这个Class对象的ClassLoader引用。

数组类的Class对象不是由类加载器创建的,但是会根据Java运行时的需要由java虚拟机自动创建。对于数组类的类加载器来说,由Class.getClassLoader()返回的类加载器与其元素类型的类加载器相同;如果元素类型是原始类型,那么数组类没有类加载器。

示例代码

package com.leofight.jvm.classloader;

public class MyTest15 {

    public static void main(String[] args) {
        String[] strings = new String[2];

        System.out.println(strings.getClass().getClassLoader());

        System.out.println("--------");

        MyTest15[] myTest15s = new MyTest15[2];

        System.out.println(myTest15s.getClass().getClassLoader());

        System.out.println("-------");

        int[] ints = new int[2];
        System.out.println(ints.getClass().getClassLoader());
    }
}

输出

null
--------
sun.misc.Launcher$AppClassLoader@14dad5dc
-------
null

String是由根类加载器加载的所以输出null
MyTest是由系统加载器加载的说输出sun.misc.Launcher$AppClassLoader@14dad5dc
int是原生数据类型,所以输出null表示没有类加载器。

应用程序实现ClassLoader的子类以扩展Java虚拟机动态加载类的方式。

类加载器通常可以被安全管理器用来指示安全域。

ClassLoader类使用委托模型来搜索类和资源,ClassLoader的每个实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例会将对类或资源的搜索委托给其父类加载器在自身尝试查找类或资源之前。虚拟机内置的类加载器称为“启动类加载器/根类加载器”,本身没有父类,但可以作为ClassLoader实例的父类。

类加载器支持并发加载称之为并发类加载器,并且需要在它们的类初始化时通过调用ClassLoader.registerAsParallelCapable方法来注册自己。, 请注意,ClassLoader类默认情况下已注册为并发。但是,它的子类仍然需要注册自己,如果它们要并发加载的。
在委托模型不严格层次的情况下,类加载器需要具有并发加载,否则类加载器会死锁,因为类加载器锁持有类加载过程的持续时间。

通常,Java虚拟机以平台相关的方式从本地文件系统加载类,例如,在UNIX系统上,虚拟机从CLASSPATH 环境变量定义的目录加载类。

但是,某些类可能不是源自文件,他们可能来自其他来源,如网络,或者它们可以由应用程序构建出来的。defineClass方法将字节数组转换为class类的实例。这个新定义的类的实例可以使用Class.newInstance来创建。

由类加载器创建的对象的方法和构造函数可以引用其他类,为了确定引用的类,Java 虚拟机会调用最初创建该类的类加载器的loadClass方法来创建。

例如,一个应用程序可以创建一个网络类加载器来从服务器上下载类文件。, 示例代码可能如下所示:

ClassLoader loader = new NetworkClassLoader(host,port);
Object main = loader.loadClass("Main", true).newInstance();
...

网络类加载器子类必须定义方法findClass 和loadClassData从网络中加载一个类。一旦下载了组成该类的字节,它就应该使用方法defineClass创建一个类实例。
示例如下:

 class NetworkClassLoader extends ClassLoader {
    String host;
    int port;

    public Class findClass(String name) {
        byte[] b = loadClassData(name);
        return defineClass(name, b, 0, b.length);
    }

    private byte[] loadClassData(String name) {
        // load the class data from the connection
                . . .
    }
}
   

binary name
以字符串参数向ClassLoader方法提供类的名字,必须是一个二进制的名字,由Java语言规范规定的。

有效类名的示例
java.lang.String
javax.swing.JSpinner$DefaultEditor
java.security.KeyStore$Builder$FileBuilder$1
ava.net.URLClassLoader$3$1

自定义类加载器

示例代码

package com.leofight.jvm.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class MyTest16 extends ClassLoader {

    private String classLoaderName;

    private final String fileExtension = ".class";

    public MyTest16(String classLoaderName) {
        super();//将系统类加载器当做该类加载器的父加载器
        this.classLoaderName = classLoaderName;

    }

    public MyTest16(ClassLoader parent, String classLoaderName) {
        super(parent);//显示指定该类加载器的父加载器
        this.classLoaderName = classLoaderName;
    }

    @Override
    public String toString() {
        return "[" + this.classLoaderName + "]";
    }

    @Override
    protected Class findClass(String className) throws ClassNotFoundException {
        byte[] data = this.loadClassData(className);

        return this.defineClass(className, data, 0, data.length);
    }

    private byte[] loadClassData(String name) {

        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            this.classLoaderName = this.classLoaderName.replace(".", "/");

            is = new FileInputStream(new File(name + this.fileExtension));
            baos = new ByteArrayOutputStream();

            int ch = 0;
            while (-1 != (ch = is.read())) {
                baos.write(ch);
            }

            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        return data;
    }

    public static void test(ClassLoader classLoader) throws Exception {
        Class clazz = classLoader.loadClass("com.leofight.jvm.classloader.MyTest1");
        Object object = clazz.newInstance();

        System.out.println(object);
    }

    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");
        test(loader1);
    }
}


输出

com.leofight.jvm.classloader.MyTest1@43556938

你可能感兴趣的:(04_JVM学习笔记_ClassLoader源码分析)