在编写Java程序时须要使用javac命令将.java后缀名的文件编译成.class文件,而后JVM经过执行.class文件来运行咱们写的程序,那么JVM怎么才能执行.class文件呢?
这就须要类加载器了。java
在jdk中找到sun.misc.Launcher文件,这个文件是JVM中的启动器实例,在该类内部有getLauncher()
方法app
public static Launcher getLauncher() { return launcher; }
它返回的是launcher
实例ide
private static Launcher launcher = new Launcher();
在构造方法调用时会初始化 ExtClassLoader和AppClassLoader(BootstrapClassLoader会在Java虚拟机建立以后进行加载,因此是在此以前)。测试
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); // 非核心的就去掉了 }
经过点击 getXxxClassLoader()
跟到最后能够看到this
private ClassLoader(Void unused, ClassLoader parent) { // 经过这行可让加载器之间创建父子级关系 this.parent = parent; if (ParallelLoaders.isRegistered(this.getClass())) { parallelLockMap = new ConcurrentHashMap<>(); package2certs = new ConcurrentHashMap<>(); assertionLock = new Object(); } else { // no finer-grained lock; lock on the classloader instance parallelLockMap = null; package2certs = new Hashtable<>(); assertionLock = this; } }
注意: 咱们查看ExtClassLoader时会发现这里的parent是null,而查看AppClassLoader时传入的parent是ExtClassLoader的对象实例。因此这里也就印证了上面画的图。spa
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) { // 若是父加载器存在则使用父加载器进行加载 c = parent.loadClass(name, false); } else { // 若是父加载器不存在就是用BootstrapClassLoader加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // 若是父加载器和BootstrapClassLoader都没有加载到的话 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); // 本身进行加载 // 这个方法默认是一个空实现,须要本身重写 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); } // 返回已加载到的类或null return c; } }
加载步骤:3d
parent == null
,这时会使用 BootstrapClassLoader 加载findClass()
方法来加载目标类。找到某个ClassLoader,这里以AppClassLoader为例code
static class AppClassLoader extends URLClassLoader {
在AppClassLoader中搜索findClass()
方法是找不到的,因此找到每一个加载器共同的父类URLClassLoader
,在这里能够看到findClass()
方法。对象
protected Class<?> findClass(final String name) throws ClassNotFoundException { final Class<?> result; try { result = AccessController.doPrivileged( new PrivilegedExceptionAction<Class<?>>() { public Class<?> run() throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); Resource res = ucp.getResource(path, false); if (res != null) { try { return defineClass(name, res); } catch (IOException e) { throw new ClassNotFoundException(name, e); } } else { return null; } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } if (result == null) { throw new ClassNotFoundException(name); } return result; }
findClass
方法的做用是从指定的路径找到传入的文件名称的.class文件,并返回回去,该方法的主要逻辑在run()
方法中。blog
从前面能够知道,要想实现classLoader只须要继承ClassLoader类并重写 findClass
和 loadClass
方法便可。
在桌面建立了一个名为Test的Java文件,并使用javac命令进行编译。
public class Test { public void sayHi() { System.out.println("hi!"); } public static void main(String[] args) { System.out.println("Hello World!"); } }
/** * Created by luyi on 2021/2/16. * 描述:自定义ClassLoader */ public class MyClassLoader extends ClassLoader { public String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } /** * 经过输入流读取文件 * * @param name 文件全限定类名 */ public byte[] loadByte(String name) throws IOException { if (name == null || name.length() == 0) { return new byte[1024]; } name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } /** * 将指定的路径中的指定的文件进行加载 * * @param name 全限定类名 * @return * @throws ClassNotFoundException */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { if (name == null || name.length() == 0) { return null; } try { byte[] data = loadByte(name); return defineClass(name, data, 0, data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } public static void main(String[] args) throws Exception { MyClassLoader loader = new MyClassLoader("/Users/luyi/Desktop"); Class<?> clazz = loader.loadClass("Test", false); Object instance = clazz.newInstance(); Method sayHi = clazz.getDeclaredMethod("sayHi", null); sayHi.invoke(instance, null); System.out.println(clazz.getClassLoader().getClass().getName()); } }
运行结果:
从运行结果能够看出,自定义的类加载器已经完成了对指定文件的加载,并正确的执行了方法。