一、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); } }
二、自定义类加载器(继承 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