JAVA-JVM 自定义类加载器

一、sun.misc.Launcher (ExtClassLoader 与 AppClassLoader 的创建)

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

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

    Thread.currentThread().setContextClassLoader(this.loader);
    String var2 = System.getProperty("java.security.manager");
    if (var2 != null) {
        SecurityManager var3 = null;
        if (!"".equals(var2) && !"default".equals(var2)) {
            try {
                var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
            } catch (IllegalAccessException var5) {
            } catch (InstantiationException var6) {
            } catch (ClassNotFoundException var7) {
            } catch (ClassCastException var8) {
            }
        } else {
            var3 = new SecurityManager();
        }

        if (var3 == null) {
            throw new InternalError("Could not create SecurityManager: " + var2);
        }
        System.setSecurityManager(var3);
    }
}
View Code

二、自定义类加载器(继承 ClassLoader 类,重写 findClass 方法,不推荐重写 loadClass 方法,会破坏委派机制)

测试加载类,使用 javac 把 .java 文件编译成 .class 文件

package com;

public class Hello {
    static {
        System.out.println("Hello !");
    }
    
    public void sayHi(String name){
        System.out.println("Hello !" + name);
    }
}

类加载器,注意要加载类的路径名与包名

package com;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Method;

public class ClassLoaderTest extends ClassLoader {

    private final static String filePathSuffix = ".class";
    private String filePathPrefix;

    public ClassLoaderTest(String filePathPrefix) {
        this.filePathPrefix = filePathPrefix;
    }

    @Override
    protected Class<?> findClass(String name) {
        String fileName = name.split("\\.")[name.split("\\.").length - 1];
        byte[] bytes = loadClassData(filePathPrefix + fileName + filePathSuffix);
        return defineClass(name, bytes, 0, bytes.length);
    }

    private byte[] loadClassData(String filePath) {
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(new File(filePath));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }

    public static void main(String[] args) throws Exception {
        ClassLoaderTest clt = new ClassLoaderTest("D:/");
        Class c = clt.loadClass("com.Hello");
        System.out.println(c.getClassLoader());
        System.out.println(c.getClassLoader().getParent());
        System.out.println(c.getClassLoader().getParent().getParent());
        System.out.println(c.getClassLoader().getParent().getParent().getParent());
        Method sayHi = c.getMethod("sayHi", String.class);
        // 无参实例化
        Object o = c.newInstance();
        // 调用方法
        sayHi.invoke(o, "zhangsan");
    }
}

三、Class.forName() 和 ClassLoader.loadClass()

调用了 forName0,第二个参数为 true,默认会初始化,可使用其重载方法指定为 false

@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

调用了 loadClass 的重载方法,默认不会链接,就不会初始化了

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

以上面的 Hello 类为例,在 com 包下新建同样的文件,命名为 Hello1

public static void main(String[] args) throws Exception {
    // 加载,链接,初始化
    Class.forName("com.Hello1");
    System.out.println("==========================================");
    // 加载,链接
    Class.forName("com.Hello1", false,ClassLoader.getSystemClassLoader());
    System.out.println("==========================================");
    // 加载
    ClassLoader.getSystemClassLoader().loadClass("com.Hello1");
}

四、线程上下文类加载器(ThreadContextClassLoader)

https://mp.weixin.qq.com/s/4FJbRLUcg8FmOqP1uz3f2A

java.lang.Thread 中的方法 getContextClassLoader() 和 setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器。

如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。

Java 应用运行的初始线程的上下文类加载器是系统类加载器。

Thread thread = new Thread(()->{
    try {
        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass("com.Hello");
        System.out.println(aClass.getClassLoader());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
});
thread.setContextClassLoader(new ClassLoaderTest("D:/"));
thread.start();

Thread.sleep(1000);

thread = new Thread(()->{
    try {
        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass("com.Hello1");
        System.out.println(aClass.getClassLoader());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
});
thread.start();


https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.3.2

https://www.cnblogs.com/editice/p/5420712.html

猜你喜欢

转载自www.cnblogs.com/jhxxb/p/10921945.html