第三步:引导类加载器先查找已经加载的类中是否有这个类,有则返回,没有就去加载这个类。这时候, 我们都知道, Math类是我自己定义的, 引导类加载器中不可能有, 加载失败,所以, 他就会去加载这个类。回去扫描/lib/jar包中有没有这个类,发现没有,于是让扩展类加载器去加载, 扩展类加载器会去扫描扩展包lib/jar/ext包,里面有没有呢? 当然也没有, 于是委托应用程序类加载器, ok, 应用程序类加载器是有的, 于是就可以加载了, 然后返回这个类。
【通过分析,我们可以得出,双亲委派机制的实现使用的是责任链设计模式。】
那么, 这里有一个问题, 那就是, 由应用程序类加载器首先加载, 然后最后又回到了应用程序类加载器. 绕了一圈又回来了, 这样是不是有些多此一举呢, 循环了两次? 为什么一定要从应用程序类加载器加载呢? 直接从引导类加载器加载不好么?只循环一次啊....
其实, 对于我们的项目来说, 95%的类都是我们自己写的, 因此, 而我们自己写的类是有应用程序类加载器加载. 其实,应用程序类加载器只有在第一次的时候, 才会加载两次. 以后, 当再次使用到这个类的时候, 直接去问应用程序类加载器, 有这个类么? 已经有了, 就直接返回了.
二、 源码分析双亲委派机制还是从这张图说起,c++语言调用了sun.misc.Launcher.getLauncher()获取了launcher对象,Launcher类初始化的时候其构造器创建了ExtClassLoader和AppClassLoader。然后接下来调用launcher对象的getClassLoader()方法。
public ClassLoader getClassLoader() { return this.loader; }getClassLoader()返回了this.loader对象。 而loader对象是在Launcher初始化的时候进行了赋值, loadClass是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 { // loader的值是AppClassLoader this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } ...... }类加载器是如何加载类的呢?
调用了loader.loadClass("com.lxl.Math")方法.我们来看一下类加载器.类加载器主要调用的是classLoader.loadClass("com.lxl.Math") 这个方法来实现双亲委派机制的. 根据上面的分析, 我们知道, 在Launcher类初始化的时候, loadClass是AppClassLoader, 那么也就是说, 双亲委派机制的起点是AppClassLoader.
下面我们来看一下源码, 我们采用断点的方式来分析 。
2.1第一次向上查找 1. 从AppClassLoader加载目标类首先, 我们在Launcher的AppClassLoader的loadClass(String var1, boolean var2) 这个方法添加一个断点, 并将其赋值为我们的com.lxl.jvm.Math类
然后运行Math的main方法,我们来看一下这个类到底是如何被加载的
启动debug调试模式, 首先进入了Launch.AppClassLoader.loadClass(....)方法
我们来具体看看这个方法的实现
上面都是在做权限校验, 我们看重点代码.
Launcher.AppClassLoader.loadClass(...) public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException { int var3 = var1.lastIndexOf(46); if (var3 != -1) { SecurityManager var4 = System.getSecurityManager(); if (var4 != null) { var4.checkPackageAccess(var1.substring(0, var3)); } } // 缓存中是否有目标路径,如果有,说明之前已经加载过,直接调动findLoadedClass()从已经加载的类中查找,找到后直接返回。 if (this.ucp.knownToNotExist(var1)) { Class var5 = this.findLoadedClass(var1); if (var5 != null) { if (var2) { this.resolveClass(var5); } return var5; } else { throw new ClassNotFoundException(var1); } } else { // 缓存中没有,则调用loadClass加载类。 return super.loadClass(var1, var2); } }