protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader }
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); } return c; } }
由 Java 虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。前面已经介绍过,Java 虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java 虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载类的 Class 对象,因此这些 Class 对象始终是可触及的
而由用户自定义的类加载器所加载的类是可以被卸载的
1.5类加载器命名空间深度解析
通过一个例子来分析
MyCat
1 2 3 4 5
public class MyCat { public MyCat() { System.out.println("MyCat is loaded by: " + this.getClass().getClassLoader()); } }
MySample
1 2 3 4 5 6
public class MySample { public MySample () { System.out.println("MySample is loaded by:" + this.getClass().getClassLoader()); new MyCat (); } }
MySample is loaded by: com.cuzz.jvm.classloader.MyClassLoader@16d3586 MyCat is loaded by: sun.misc.Launcher$AppClassLoader@dad5dc
我们知道 MySample 是我们自定义类加载加载出来的,MyCat 是有系统类加载加载的
1 2 3 4 5 6 7
public class MySample { public MySample () { System.out.println("MySample is loaded by: " + this.getClass().getClassLoader()); new MyCat (); System.out.println(MyCat.class); } }
输出
1 2 3
MySample is loaded by: com.cuzz.jvm.classloader.MyClassLoader@16d3586 MyCat is loaded by: sun.misc.Launcher$AppClassLoader@dad5dc class com.cuzz.jvm.classloader.MyCat
说明自定义类加载加载的类,可以访问系统类加载加载的类
如果我们在系统类加载的类中访问自定义类加载器加载的类
1 2 3 4 5 6
public class MyCat { public MyCat() { System.out.println("MyCat is loaded by: " + this.getClass().getClassLoader()); System.out.println(MySample.class); } }
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
MySample is loaded by: com.cuzz.jvm.classloader.MyClassLoader@16d3586 MyCat is loaded by: sun.misc.Launcher$AppClassLoader@dad5dc Exception in thread "main" java.lang.NoClassDefFoundError: com/cuzz/jvm/classloader/MySample at com.cuzz.jvm.classloader.MyCat.<init>(MyCat.java:6) at com.cuzz.jvm.classloader.MySample.<init>(MySample.java:6) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:422) at java.lang.Class.newInstance(Class.java:442) at com.cuzz.jvm.classloader.MyClassLoader.main(MyClassLoader.java:68) Caused by: java.lang.ClassNotFoundException: com.cuzz.jvm.classloader.MySample at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 8 more
Exception in thread "main" MySample is loaded by: sun.misc.Launcher$AppClassLoader@dad5dc java.lang.NoClassDefFoundError: com/cuzz/jvm/classloader/MyCat at com.cuzz.jvm.classloader.MySample.<init>(MySample.java:6) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:422) at java.lang.Class.newInstance(Class.java:442) at com.cuzz.jvm.classloader.MyClassLoader.main(MyClassLoader.java:68) Caused by: java.lang.ClassNotFoundException: com.cuzz.jvm.classloader.MyCat at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 7 more
private static Launcher launcher = new Launcher(); private static String bootClassPath = System.getProperty("sun.boot.class.path"); public static Launcher getLauncher() { return launcher; } private ClassLoader loader; public Launcher() { // Create the extension class loader ClassLoader extcl; try { extcl = ExtClassLoader.getExtClassLoader(); } catch (IOException e) { throw new InternalError( "Could not create extension class loader"); } // Now create the class loader to use to launch the application try { loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader"); } // Also set the context class loader for the primordial thread. Thread.currentThread().setContextClassLoader(loader); // Finally, install a security manager if requested String s = System.getProperty("java.security.manager"); ...... } /* * Returns the class loader used to launch the main application. */ public ClassLoader getClassLoader() { return loader; }
public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class<?> caller = null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { // Reflective call to get caller class is only needed if a security manager // is present. Avoid the overhead of making this call otherwise. // 获取调用 forName 方法的的那个类 caller = Reflection.getCallerClass(); if (sun.misc.VM.isSystemDomainLoader(loader)) { ClassLoader ccl = ClassLoader.getClassLoader(caller); if (!sun.misc.VM.isSystemDomainLoader(ccl)) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader, caller); }
5. 线程上下文类加载器分析与实现
5.1 前言
看一个程序来一下感性的认识:
1 2 3 4 5 6
public class MyTest24 { public static void main(String[] args) System.out.println(Thread.currentThread().getContextClassLoader()); System.out.println(Thread.class.getClassLoader()); } }
driver: class com.mysql.jdbc.Driverloader: sun.misc.Launcher$AppClassLoader@dad5dc driver: class com.mysql.fabric.jdbc.FabricMySQLDriverloader: sun.misc.Launcher$AppClassLoader@dad5dc 当前线程上下文类加载器: sun.misc.Launcher$AppClassLoader@dad5dc ServiceLoader的类加载器: null
我们可以看到 ServiceLoader 找到了 mysql 的两个驱动,这两个驱动都是由系统类加载器加载的,当前线程的上下文加载器默认也是系统类加载器,ServiceLoader是由启动类加载器加载,但是程序是怎样找到 mysql 的两个驱动的呢?我们没有在程序里边设置任何的属性或者路径之类的东西让程序能找到 mysql 的驱动,那么我们只能研究一下 ServiceLoader 的源码和文档看一下他们的原理: