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)

在 Java 中存在着很多的服务提供者接口 SPI,全称 Service Provider Interface,是Java 提供的一套用来被第三方实现或者扩展的API,这些接口一般由第三方提供实现,常用 SPI 有 JDBC、JNDI 等。

这些 SPI 的接口(比如 JDBC 中的 java.sql.Driver)属于核心类库,一般存在 rt.jar 包中,由根类加载器加载。

而第三方实现的代码一般作为依赖 jar 包存放在 classpath 路径下,由于 SPI 接口中的代码需要加载具体的第三方实现类并调用其相关方法,SPI 的接口类是由根类加载器加载的,Bootstrap 类加载器无法直接加载位于 classpath 下的具体实现类。

由于委派模式的存在,Bootstrap 类加载器也无法反向委托 AppClassLoaser 加载 SPI 的具体实现类。

在这种情况下,Java 提供了上下文类加载器用于解决以上问题。这种加载类的方式破坏了委托模型,但它使得 Java 类加载器变得更加灵活。

 

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

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

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

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

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

posted @ 2019-05-27 15:23  江湖小小白  阅读(1239)  评论(0编辑  收藏  举报