原创

Java中ClassLoader类加载器详解

1. 类加载器种类

  • Bootstrap ClassLoader:最顶层的加载类,主要加载核心类库,$JRE_HOME$/lib下的jar包和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。
  • Extention ClassLoader: 扩展的类加载器,加载目录$JRE_HOME/lib/ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
  • Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。

2. 类加载器流程

2.1 加载顺序

Bootstrap Classloder -> Extention ClassLoader -> AppClassLoader。

2.2 源码解析

Bootstrap ClassLoader是JVM级别的,由C++撰写;Extension ClassLoader、App ClassLoader都是java类,都继承自URLClassLoader超类。 Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher ,sun.misc.Launcher初始化Extension ClassLoader、App ClassLoader

2.2.1 Bootstrap CLassloder

sun.misc.Launcher是一个java虚拟机的入口应用,简洁源码如下:

package sun.misc;

public class Launcher {

    // 获取BootstrapClassLoader加载的jar包路径。
    private static String bootClassPath = System.getProperty("sun.boot.class.path");

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            // 加载ExtClassLoader类加载器
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            // 加载AppClassLoader类加载器
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }

        // 设置AppClassLoader为线程上下文类加载器,这个文章后面部分讲解
        Thread.currentThread().setContextClassLoader(this.loader);
   }

   public ClassLoader getClassLoader() {
        return loader;
    }

    static class ExtClassLoader extends URLClassLoader {}

    static class AppClassLoader extends URLClassLoader {}
}

查看System.getProperty("sun.boot.class.path")内容:

public class Test {

    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));
    }
}

运行结果:

/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/resources.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/sunrsasign.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jsse.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jce.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/charsets.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/jfr.jar:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/classes

2.2.2 Extention ClassLoader

static class ExtClassLoader extends URLClassLoader {

    static {
        ClassLoader.registerAsParallelCapable();
    }

    public static ExtClassLoader getExtClassLoader() throws IOException
    {
        final File[] dirs = getExtDirs();

        try {
            // Prior implementations of this doPrivileged() block supplied
            // aa synthesized ACC via a call to the private method
            // ExtClassLoader.getContext().

            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<ExtClassLoader>() {
                    public ExtClassLoader run() throws IOException {
                        int len = dirs.length;
                        for (int i = 0; i < len; i++) {
                            MetaIndex.registerDirectory(dirs[i]);
                        }
                        return new ExtClassLoader(dirs);
                    }
                });
        } catch (java.security.PrivilegedActionException e) {
            throw (IOException) e.getException();
        }
    }

    private static File[] getExtDirs() {
        // 加载ExtClassLoader文件
        String s = System.getProperty("java.ext.dirs");
        File[] dirs;
        if (s != null) {
            StringTokenizer st =
                new StringTokenizer(s, File.pathSeparator);
            int count = st.countTokens();
            dirs = new File[count];
            for (int i = 0; i < count; i++) {
                dirs[i] = new File(st.nextToken());
            }
        } else {
            dirs = new File[0];
        }
        return dirs;
    }
}

查看System.getProperty("java.ext.dirs")内容:

public class Test {

    public static void main(String[] args) {
        System.out.println(System.getProperty("java.ext.dirs"));
    }
}

输出结果:

/Users/weilai/Library/Java/Extensions:
/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:
/Network/Library/Java/Extensions:
/System/Library/Java/Extensions:
/usr/lib/java

2.2.3 AppClassLoader

static class AppClassLoader extends URLClassLoader {


    public static ClassLoader getAppClassLoader(final ClassLoader extcl)
        throws IOException
    {
        // AppClassLoader类加载器路径
        final String s = System.getProperty("java.class.path");
        final File[] path = (s == null) ? new File[0] : getClassPath(s);


        return AccessController.doPrivileged(
            new PrivilegedAction<AppClassLoader>() {
                public AppClassLoader run() {
                URL[] urls =
                    (s == null) ? new URL[0] : pathToURLs(path);
                return new AppClassLoader(urls, extcl);
            }
        });
    }
}

查看System.getProperty("java.class.path")内容:

public class Test {

    public static void main(String[] args) {
        System.out.println(System.getProperty("java.class.path"));
    }
}

运行结果其实就是代码编译生成class文件的路径:

/Users/weilai/Documents/java/demo/target/classes:

3. 类加载器

3.1 每个类加载器都有一个父加载器

  • 每个类加载器都有一个父加载器
  • 加载代码Test.class是由AppClassLoader完成
  • AppClassLoader的父加载器是ExtClassLoader
  • ExtClassLoader的父加载器为null
  • int.class、Integer.class、String.class是由Bootstrap ClassLoader加载的,父加载器也为null
  • 父加载器不是父类

测试代码:

public class Test {

    public static void main(String[] args) {
        ClassLoader cl = Test.class.getClassLoader();
        System.out.println("ClassLoader is:"+cl.toString());
        System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());
        System.out.println(Integer.class.getClassLoader());
        System.out.println(int.class.getClassLoader());
        System.out.println(String.class.getClassLoader());
        System.out.println("ClassLoader\'s grand father is:"+cl.getParent().getParent().toString());
    }
}

运行结果:

ClassLoader is:sun.misc.Launcher$AppClassLoader@135fbaa4
ClassLoader's parent is:sun.misc.Launcher$ExtClassLoader@2503dbd3
null
null
null
Exception in thread "main" java.lang.NullPointerException
    at com.weilai.webflux.Test.main(Test.java:19)

3.2 父加载器

AppClassLoader与ExtClassLoader等的关系如下。

static class ExtClassLoader extends URLClassLoader {}
static class AppClassLoader extends URLClassLoader {}

file

AppClassLoader与ExtClassLoader等的getParent()方法实现实际是在ClassLoader抽象类中,具体重要代码如下。

public abstract class ClassLoader {

    private final ClassLoader parent;

    // 如果不指定,为getSystemClassLoader()方法获取的默认类加载器
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

    // 如果指定,为传入构造函数中的类加载器
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        ......
    }

    // getParent()方法
    public final ClassLoader getParent() {
        if (parent == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Check access to the parent class loader
            // If the caller's class loader is same as this class loader,
            // permission check is performed.
            checkClassLoaderPermission(parent, Reflection.getCallerClass());
        }
        return parent;
    }

我们可以看到getParent()实际上返回的就是一个ClassLoader对象parent,parent的赋值是在ClassLoader对象的构造方法中,它有两个情况:

  1. 由外部类创建ClassLoader时直接指定一个ClassLoader为parent。
  2. 由getSystemClassLoader()方法生成,也就是在sun.misc.Laucher通过getClassLoader()获取,也就是AppClassLoader。直白的说,一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader。

3.2.1 验证ExtClassLoader的父加载器为null

调用方法创建ExtClassLoader

public class Launcher {
    private static URLStreamHandlerFactory factory = new Launcher.Factory();
    private static Launcher launcher = new Launcher();
    private static String bootClassPath = System.getProperty("sun.boot.class.path");
    private ClassLoader loader;
    private static URLStreamHandler fileHandler;

    public static Launcher getLauncher() {
        return launcher;
    }

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            // 调用方法创建ExtClassLoader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }
        ......
    }
}

调用super方法,设置父加载器classLoader为null(第二个参数)

static class ExtClassLoader extends URLClassLoader {
    public ExtClassLoader(File[] var1) throws IOException {
        super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
        SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
    }
}

调用super,设置传递过来的classLoader

public class URLClassLoader extends SecureClassLoader implements Closeable {
    public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
        super(parent);
        ......
    }
}

调用super,设置传递过来的classLoader

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

调用ClassLoader构造函数,设置传递过来的父加载器为null。

public abstract class ClassLoader {
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }
}

3.2.2 验证AppClassLoader的父加载器为ExtClassLoader

调用方法创建AppClassLoader,传入参数ExtClassLoader

public class Launcher {
    private static URLStreamHandlerFactory factory = new Launcher.Factory();
    private static Launcher launcher = new Launcher();
    private static String bootClassPath = System.getProperty("sun.boot.class.path");
    private ClassLoader loader;
    private static URLStreamHandler fileHandler;

    public static Launcher getLauncher() {
        return launcher;
    }

    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
            // 调用方法创建ExtClassLoader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader", var10);
        }

        try {
            // 调用方法创建AppClassLoader,传入参数ExtClassLoader
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader", var9);
        }
        ......
    }
}

调用super方法,传入类加载器,类型为ExtClassLoader。

public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
        public Launcher.AppClassLoader run() {
            URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
            return new Launcher.AppClassLoader(var1x, var0);
        }
    });
}

AppClassLoader(URL[] var1, ClassLoader var2) {
    super(var1, var2, Launcher.factory);
    this.ucp.initLookupCache(this);
}

调用super,设置传递过来的classLoader

public class URLClassLoader extends SecureClassLoader implements Closeable {
    public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
        super(parent);
        ......
    }
}

调用super,设置传递过来的classLoader

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

调用ClassLoader构造函数,设置传递过来的父加载器为null。

public abstract class ClassLoader {
    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }
}

3.2.3 我们写的代码的父类加载器是AppClassLoader

ClassLoader cl = Test.class.getClassLoader();

Class构造函数是私有的,由jvm操作,上面我们得知,ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader。

public final class Class<T> {

    private final ClassLoader classLoader;

    private Class(ClassLoader loader) {
        classLoader = loader;
    }

    public ClassLoader getClassLoader() {
        ClassLoader cl = getClassLoader0();
        if (cl == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
        }
        return cl;
    }

    ClassLoader getClassLoader0() { return classLoader; }
}

4. 类加载器的双亲委托

4.1 简介

file

如果当前的类加载器没有查询到这个class对象已经加载就请求父加载器(不一定是父类)进行操作,然后以此类推。直到Bootstrap ClassLoader,上图黄色的向上委托方向。如果Bootstrap ClassLoader也没有加载过此class实例,那么它就会从它指定的路径中去查找,如果查找成功则返回,如果没有查找成功则交给子类加载器,也就是ExtClassLoader,这样类似操作直到终点,上图灰色的向下查找方向。

流程总结:

  1. 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
  2. 递归,重复第1部的操作。
  3. 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面,它首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找。
  4. Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找。
  5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。如果没有找到就让子类找,如果没有子类会怎么样?抛出各种异常。

4.2 相关重要方法

  • loadClass(String name, boolean resolve):加载class,resolve为true则解析class。
  • findLoadedClass(String name):去检测这个class是不是已经加载过了。

执行步骤:

  1. 执行findLoadedClass(String)去检测这个class是不是已经加载过了。
  2. 执行父加载器的loadClass方法。如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说Bootstrap ClassLoader是它的父加载器
  3. 如果向上委托父加载器没有加载成功,则通过findClass(String name)查找,findClass是一个抽象方法,由各自对应的类加载器实现,自定义classLoader时候也要重写findClass方法。
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 首先,检测是否已经加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //父加载器不为空则调用父加载器的loadClass
                    c = parent.loadClass(name, false);
                } else {
                    //父加载器为空则调用Bootstrap Classloader
                    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();
                //父加载器没有找到,则调用findclass
                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) {
            resolveClass(c);
        }
        return c;
    }
}

5. 自定义ClassLoader

不管是Bootstrap ClassLoader还是ExtClassLoader等,这些类加载器都只是加载指定的目录下的jar包或者资源。在某种情况下,我们需要动态加载一些东西,如某个文件夹加载一个class文件,或者从网络上下载class主内容然后再进行加载,这时需要我们自定义一个ClassLoader。

自定义步骤:

  1. 编写一个类继承ClassLoader抽象类
  2. 重写它的findClass()方法
  3. 在findClass()方法中调用defineClass(),它能将class二进制内容转换成Class对象,如果不符合要求的会抛出各种异常。

自定义演示类,并生成字节码放置在/Users/weilai/Documents/java目录下

package com.weilai.demo;

public class Test {

    public void say(String name){
        System.out.println(name + " Say Hello World");
    }
}

编写DiskClassLoader类继承ClassLoader抽象类,实现findClass方法并调用defineClass()。

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


public class DiskClassLoader extends ClassLoader {

    private String mLibPath;

    public DiskClassLoader(String path) {
        mLibPath = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        System.out.println(name);
        String fileName = getFileName(name);

        File file = new File(mLibPath, fileName);

        try {
            FileInputStream is = new FileInputStream(file);

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            try {
                while ((len = is.read()) != -1) {
                    bos.write(len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] data = bos.toByteArray();
            is.close();
            bos.close();

            return defineClass(name, data, 0, data.length);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    //获取要加载 的class文件名
    private String getFileName(String name) {
        int index = name.lastIndexOf('.');
        if (index == -1) {
            return name + ".class";
        } else {
            return name.substring(index + 1) + ".class";
        }
    }

}

Test.class成功载入并执行成功.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassLoaderTest {

    public static void main(String[] args) {
        //创建自定义classloader对象。
        DiskClassLoader diskLoader = new DiskClassLoader("/Users/weilai/Documents/java");
        try {
            //加载class文件
            Class<?> c = diskLoader.loadClass("com.weilai.demo.Test");

            if(c != null){
                try {
                    // 得到实例
                    Object obj = c.newInstance();
                    // 获取方法
                    Method method = c.getDeclaredMethod("say", String.class);
                    //通过反射调用Test类的say方法
                    method.invoke(obj, "weilai");
                } catch (InstantiationException | IllegalAccessException
                        | NoSuchMethodException
                        | SecurityException |
                        IllegalArgumentException |
                        InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}

运行结果:

com.weilai.demo.Test
weilai Say Hello World

6. 自定义ClassLoader应用加密解密协议

生成加密文件工具,将Test.class生成加密文件Test.classen放置于/Users/weilai/Documents/java目录下

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;


public class FileUtils {

    public static void test(String path){
        File file = new File(path);
        try {
            FileInputStream fis = new FileInputStream(file);
            FileOutputStream fos = new FileOutputStream(path+"en");
            int b = 0;
            int b1 = 0;
            try {
                while((b = fis.read()) != -1){
                    //每一个byte异或一个数字2
                    fos.write(b ^ 2);
                }
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

}

实现解密文件的类加载器

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


public class DeClassLoader extends ClassLoader {

    private String mLibPath;

    public DeClassLoader(String path) {
        mLibPath = path;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {

        String fileName = getFileName(name);

        File file = new File(mLibPath,fileName);

        try {
            FileInputStream is = new FileInputStream(file);

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len = 0;
            byte b = 0;
            try {
                while ((len = is.read()) != -1) {
                    //将数据异或一个数字2进行解密
                    b = (byte) (len ^ 2);
                    bos.write(b);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            byte[] data = bos.toByteArray();
            is.close();
            bos.close();

            return defineClass(name,data,0,data.length);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return super.findClass(name);
    }

    //获取要加载 的class文件名
    private String getFileName(String name) {
        int index = name.lastIndexOf('.');
        if(index == -1){
            return name+".classen";
        }else{
            return name.substring(index+1)+".classen";
        }
    }

}

编写代码测试,DeClassLoader类加载器可以正常执行,DiskClassLoader类加载器报错,因为没有正确解码class文件。

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassLoaderTest {

    public static void main(String[] args) {

        DeClassLoader diskLoader1 = new DeClassLoader("/Users/weilai/Documents/java");
        try {
            //加载class文件
            Class<?> c = diskLoader1.loadClass("com.weilai.demo.Test");

            if(c != null){
                try {
                    Object obj = c.newInstance();
                    Method method = c.getDeclaredMethod("say",String.class);
                    //通过反射调用Test类的say方法
                    method.invoke(obj, "weilai");
                } catch (InstantiationException | IllegalAccessException
                        | NoSuchMethodException
                        | SecurityException |
                        IllegalArgumentException |
                        InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //创建自定义classloader对象。
        DiskClassLoader diskLoader = new DiskClassLoader("/Users/weilai/Documents/java");
        try {
            //加载class文件
            Class<?> c = diskLoader.loadClass("com.weilai.demo.Test");

            if(c != null){
                try {
                    // 得到实例
                    Object obj = c.newInstance();
                    // 获取方法
                    Method method = c.getDeclaredMethod("say", String.class);
                    //通过反射调用Test类的say方法
                    method.invoke(obj, "weilai");
                } catch (InstantiationException | IllegalAccessException
                        | NoSuchMethodException
                        | SecurityException |
                        IllegalArgumentException |
                        InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

7. contextClassLoader 线程上下文类加载器

产生原因:

  • 双亲委托模型很好的解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器加载),基础类之所以为基础,因为他们总是被用户调用的api,但是基础类要想调用用户代码,则无法加载。
  • 当父子线程的类加载器不一致时,即父线程使用自定义加载时,子线程默认为AppClassLoader无法加载父线程类加载器中的类。

线程中的contextClassLoader。

public class Thread implements Runnable {

/* The context ClassLoader for this thread */
   private ClassLoader contextClassLoader;

   public void setContextClassLoader(ClassLoader cl) {
       SecurityManager sm = System.getSecurityManager();
       if (sm != null) {
           sm.checkPermission(new RuntimePermission("setContextClassLoader"));
       }
       contextClassLoader = cl;
   }

   public ClassLoader getContextClassLoader() {
       if (contextClassLoader == null)
           return null;
       SecurityManager sm = System.getSecurityManager();
       if (sm != null) {
           ClassLoader.checkClassLoaderPermission(contextClassLoader,
                                                  Reflection.getCallerClass());
       }
       return contextClassLoader;
   }
}

说明:

  • contextClassLoader只是一个成员变量,通过setContextClassLoader()方法设置,通过getContextClassLoader()设置,如果开启了子线程,不指定contextClassLoader的话就是AppClassLoader,可能加载不到自己的class。
  • 每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader除非子线程特别设置

下面例子说明:diskLoader1目录存在class,可以正确执行。新建子线程如不设置contextClassLoader则为AppClassLoader,diskLoader目录存在class,所以调用Thread.currentThread().setContextClassLoader(diskLoader);设置子线程的类加载器为diskLoader即可正确执行。

package com.weilai.demo;

/**
 * @author weilai
 * @email 352342845@qq.com
 * @date 2020/8/18 11:44 上午
 */

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassLoaderTest {

    public static void main(String[] args) {

        //创建自定义classloader对象。
        DiskClassLoader1 diskLoader1 = new DiskClassLoader1("/Users/weilai/Documents/test");
        Class<?> cls1 = null;
        try {
            //加载class文件
            cls1 = diskLoader1.loadClass("com.weilai.demo.SpeakTest");
            System.out.println(cls1.getClassLoader().toString());
            try {
                Object obj = cls1.newInstance();
                Method method = cls1.getDeclaredMethod("speak");
                //通过反射调用Test类的speak方法
                method.invoke(obj);
            } catch (InstantiationException | IllegalAccessException
                    | NoSuchMethodException
                    | SecurityException |
                    IllegalArgumentException |
                    InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        DiskClassLoader diskLoader = new DiskClassLoader("/Users/weilai/Documents/");
        System.out.println("Thread " + Thread.currentThread().getName() + " classloader: " + Thread.currentThread().getContextClassLoader().toString());
        new Thread(() -> {
            System.out.println("Thread " + Thread.currentThread().getName() + " classloader: " + Thread.currentThread().getContextClassLoader().toString());

            try {
                // 加载class文件,如果不设置子线程的类加载器则为AppClassLoader
                Thread.currentThread().setContextClassLoader(diskLoader);
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                Class<?> c = cl.loadClass("com.weilai.demo.SpeakTest");
                System.out.println(c.getClassLoader().toString());
                try {
                    Object obj = c.newInstance();
                    Method method = c.getDeclaredMethod("speak");
                    method.invoke(obj);
                } catch (InstantiationException | IllegalAccessException
                        | NoSuchMethodException
                        | SecurityException |
                        IllegalArgumentException |
                        InvocationTargetException e) {
                    e.printStackTrace();
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

参考文件:https://blog.csdn.net/briblue/article/details/54973413

正文到此结束
本文目录