ClassLoader class loader

Keywords: Java jvm

Let's start with an example:

public class ClassLoaderTest{
    public static void main(String[] args) {
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        System.out.println(cl);
        System.out.println(cl.getParent());
        System.out.println(cl.getParent().getParent());
        /**
         * List It's under rt.jar
         */
        System.out.println(List.class.getClassLoader());
    }
}

Output results:

sun.misc.Launcher$AppClassLoader@6fd7bd04
sun.misc.Launcher$ExtClassLoader@3cba8af9
null
null


The system defaults to three class loaders

BootStrap (root loader): The java core library (rt.jar), written in C/C++, prints null

ExtClassLoader: under the ext package

AppClassLoader: under classpath


The delegation mechanism of the class loader:

When the Java virtual machine loads the first class, which class loader does it send to load?

(1) First, the class loader of the current thread loads the first class in the thread (class loader of the current thread: there is a get/setContextClassLoader(ClassLoader cl) in the Thread class; method, you can get/specify the class loader in this thread)

(2) If class B is referenced in class A, the Java virtual machine will use the class loader that loads class A to load class B.

(3) ClassLoader.loadClass(String className) method can also be called directly to specify a class loader to load a class.

When each class loader loads a class, it delegates it to its superior class loader first. When all ancestor class loaders are not loaded into the class, they return to the initiator class loader and cannot load it, they throw ClassNotFoundException instead of looking for the son of the initiator class loader, because there is no getChild() method. For example, as shown in the figure above: MyClassLoader - > AppClassLoader - > ExtClassLoader - > BootStrap. Self-defined MyClassLoader 1 will first be delegated to AppClassLoader,AppClassLoader to ExtClassLoader, and ExtClassLoader to BootStrap, then BootStrap will be loaded. If the load is successful, it will be over. If the load fails, it is given to ExtClassLoader to load. If the ExtClassLoader load succeeds, it is over. If the load fails, it is loaded to AppClassLoader. If the load succeeds, it is over. If the load fails, it is loaded to the custom MyClassLoader 1 class loader. If the load fails, it will be reported. ClassNotFoundException exception, end.


Look at an example:

public class MyClassLoader extends ClassLoader{
    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
}

public class ClassLoaderTest2 {
    public static void main(String[] args) {
        MyClassLoader ml1 = new MyClassLoader();
        System.out.println(ml1.getParent());
        MyClassLoader ml2 = new MyClassLoader(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(ml2.getParent());
    }
}

Print results:

sun.misc.Launcher$AppClassLoader@2ac510e3
sun.misc.Launcher$ExtClassLoader@6fd7bd04

Explains that the parent loader of the custom class loader defaults to AppClassLoader


A custom class loader must inherit the abstract class ClassLoader

Generally, it only needs to rewrite the findClass method. In fact, there is a loadClass method and a defineClass method in it.

Look at the loadClass source code:

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    //Synchronization
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        //Check if the class has been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                //If there is a parent, let the parent load. If the class is not found,
                //Throw ClassNotFoundException and let the subloader load until the load is successful
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //If the parent of the custom loader is null, load it directly with the root loader
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                //Custom loader overrides this method
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            analysis class file
            resolveClass(c);
        }
        return c;
    }
}

//This method throws exceptions directly to override, so the custom loader only needs to override findClass, if override
// The load method also needs to rewrite the container's delegation logic, which is not necessary
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

DefneClass is a simple method to program the byte array of class file into a class object. This method can not be overridden. The internal implementation is implemented in C/C++ code.


Custom class loader:

public class MyClassLoader extends ClassLoader{
    private String base_dir;

    protected MyClassLoader(ClassLoader parent) {
        super(parent);
    }
    protected MyClassLoader() {
        super();
    }
    public MyClassLoader(ClassLoader parent,String base_dir) {
            super(parent);
            this.base_dir = base_dir;
        }

    public MyClassLoader(String base_dir) {
        super();
        this.base_dir = base_dir;
    }

    //Rewrite findClass
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String path = this.base_dir+"\\"+name+".class";
        byte[] b = new byte[0];
        try {
            b = toByteArray(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(b.length == 0){
            return null;
        }
        //Define class information and assemble class file bytecode information into class information of jvm
        return defineClass(null,b,0,b.length);
    }

    private byte[] toByteArray(String filename) throws IOException {

        File f = new File(filename);
        if (!f.exists()) {
            throw new FileNotFoundException(filename);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        MyClassLoader ml = new MyClassLoader("E:\\workspace\\mytest\\temp");
        try {
            Class helloClass = ml.loadClass("Hello");
            Object hello =  helloClass.newInstance();
            System.out.println(hello.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

Print results:

classloader.MyClassLoader@19ad0dd8

It's easy to print out AppClassLoader because the Hello.class file created by your local idea may exist in the project class directory, delete it and import it from other directories.

Now let's test the custom class loader:

public class Hello {
    public String sayHello(){
        return "hello classLoader";
    }
}

public class ClassLoaderTest3 {
    public static void main(String[] args) {
        //Specify the root path, which can be introduced according to the actual situation
        MyClassLoader ml = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"E:\\workspace\\mytest\\temp");
        try {
            //Load
            Class helloClass = ml.loadClass("Hello");
            //Initialization
            Object hello =  helloClass.newInstance();
            //Call the method of loading the class
            Object info = helloClass.getMethod("sayHello").invoke(hello);
            System.out.println(info);
            System.out.println(hello.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Print results:

hello classLoader
classloader.MyClassLoader@19ad0dd8






Posted by jstarkweather on Wed, 03 Jul 2019 14:15:50 -0700