您好,登录后才能下订单哦!
Java类加载器的作用有哪些?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
Java的类加载器结构为下图所示
关于三层类加载器、双亲委派机制,本文不再板书,读者可自行百度。
那么在JDK的源码中,三层结构的具体实现是怎么样的呢?
Bootstrap ClassLoader(引导类加载器)
引导类加载器是由C++实现的,并非Java代码实现,所以在Java代码中是无法获取到该类加载器的。
一般大家都称类加载器分为四种(引导类、扩展类、系统类以及用户自定义的类加载器),但其实在JVM虚拟机规范中的支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader),所以扩展类和系统类也可以统称为自定义类加载器。
Extension ClassLoader(扩展类加载器)和Appclass Loader(系统类加载器)
扩展类加载器和系统类加载器都是由Java语言编写,具体实现为sum.misc.Launcher中的两个内部类ExtClassLoader和AppClassLoader实现,我们进入到LaunchLacher这个类中看看(这个类在oracle jdk是没有公开源码的,需要看具体源码的读者可以下载open jdk中查看具体源码,笔者这里就只是使用IDEA反编译后生成的代码进行解析):
首先是Laucncher的构造方法:
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); } }
可以看到在Launcher的构造方法中定义了一个Launcher.ExtClassLoader类型的局部变量var1(这里是反编译后的变量名),并调用Launcher.ExtClassLoader.getExtClassLoader()方法给该局部变量赋值,以及调用Launcher.AppClassLoader.getAppClassLoader(var1);给实例变量(类型为Launcher.AppClassLoader)赋值,需要注意的是,在给系统类加载器赋值时,将扩展类加载器作为参数传入到了方法中。
同时,在构造方法中,将系统类加载器设置为了当前线程的上下文类加载器,关于上下文类加载器,主要用于基础类型调用回用户代码时方法父类加载器区请求子类加载器完成类加载的行为,主要用于JDBC、JNDI等SPI服务提供者接口,这里不详细展开。
上述源码中的**getExtClassLoader()与getAppClassLoader()**方法源码如下:
getExtClassLoader()是Launcher中的内部类ExtClassLoader(扩展类加载器)的一个静态方法:
// 这是ExtClassLoader类内部的定义 private static volatile Launcher.ExtClassLoader instance;// 单例模式实例对象 public static Launcher.ExtClassLoader getExtClassLoader() throws IOException { // 从这里可以看出,ExtClassLoader是一个由double-checking形成的懒汉式单例对象 if (instance == null) { Class var0 = Launcher.ExtClassLoader.class; synchronized(Launcher.ExtClassLoader.class) { if (instance == null) { instance = createExtClassLoader(); // 创建ExtClassLoader } } } return instance; } // createExtClassLoader()方法 private static Launcher.ExtClassLoader createExtClassLoader() throws IOException { try { return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() { public Launcher.ExtClassLoader run() throws IOException { File[] var1 = Launcher.ExtClassLoader.getExtDirs(); int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { MetaIndex.registerDirectory(var1[var3]); } return new Launcher.ExtClassLoader(var1); // 调用构造方法 } }); } catch (PrivilegedActionException var1) { throw (IOException)var1.getException(); } } // ExtClassLoader的构造方法 public ExtClassLoader(File[] var1) throws IOException { // 此处第二个参数需要格外注意!!,我们进入父类的构造方法查看该参数是什么 super(getExtURLs(var1), (ClassLoader)null, Launcher.factory); SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this); } // 父类URLClassLoader的构造方法 // 此处的第二个参数是父类构造器的引用,也就解释了为什么在调用获得ExtClassLoader的 public URLClassLoader(URL[] urls, ClassLoader parent, getParent()方法获取父类构造器为null URLStreamHandlerFactory factory) { super(parent); // this is to make the stack depth consistent with 1.1 SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } acc = AccessController.getContext(); ucp = new URLClassPath(urls, factory, acc); }
getAppClassLoader()是Launcher中的内部类AppClassLoader(系统类加载器)的一个静态方法:
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException { final String var1 = System.getProperty("java.class.path"); final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1); return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() { public Launcher.AppClassLoader run() { URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2); return new Launcher.AppClassLoader(var1x, var0); // 与扩展类加载器不同的是,系统类加载器并不是单例模式的 } }); } // AppClassLoader的构造方法 AppClassLoader(URL[] var1, ClassLoader var2) { // 这里的var2 对应上述getAppClassLoader()方法中的var0,而var0对应的就是Launcher的构造方法中获取到的ExtClassLoader // 在ExtClassLoader源码的分析中,我们知道这个var2代表的就是父类构造器,所以此处就是将AppClassLoader的父类设置为ExtClassLoader super(var1, var2, Launcher.factory); this.ucp.initLookupCache(this); }
通过上述两个方法,就可以解释为什么在获取扩展类加载器的父类时为null(即引导加载器),以及不同类加载器看似是继承(Inheritance)关系,实际上是包含关系。在下层加载器中,包含着上层加载器的引用。
ClassLoader抽象类
上述的ExtClassLoader和AppClassLoader均继承于ClassLoader类,ClassLoader抽象类也是类加载机制的基石,接下来我们就进入到该类中,看看它的一些主要方法。
public final classLoader getParent()
返回该类加载器的超类加载器
public Class<?>loadclass(String name) throws ClassNotFoundException
加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返ClassNotFoundException异常。该方法中的逻辑就是双亲委派模式的实现。
protected class<?> findClass(string name)throws ClassNotFoundException
protected final Class<?> defineClass(String name, byte[] b, int off,int len)
protected final void resoiveClass(class<?> c)
protected final Class<?> findLoadedClass(String name)
private final ClassLoader parent;
关于这些方法,不一一展开,主要看一下loadClass()和findClass()。
loadClass()
public Class<?> loadClass(String name) throws ClassNotFoundException { // loadClass调用重载含有两个参数的loadClass,其中第二个参数表示在加载时是否解析,默认为false return loadClass(name, false); } // 含有两个参数的重载loadClass方法 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{// resolve:true->加载class的同时进行解析操作 synchronized (getClassLoadingLock(name)) {// 同步操作,保证只能加载一次 // 首先在缓存中判断是否已经加载同名的类 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); // 此处就是双亲委派机制的具体实现,其实就是让父类加载器先去加载。 try { // 获取当前类加载器的父类加载器 if (parent != null) { // 如果存在父类加载器,则调用父类加载器的loadClass进行加载(双亲委派) c = parent.loadClass(name, false); } else { // parent == null:父类加载器是引导类加载器 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } // 当前类加载器的父类加载器未加载此类 or 当前类加载器未加载此类 if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); // 调用当前类加载器的findClass() 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; } }
findClass()
//在ClassLoader中的findClass()方法 rotected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
可以看到,在ClassLoader中的findCLass()方法直接抛出异常,所以具体的实现是由子类进行重写实现了;在ClassLoader的子类SecureClassLoader的子类URLClassLoader中对该方法进行了重写。
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);// 获得class源文件 if (res != null) { try { // 调用defineClass()方法获得要加载的类对应的Class对象, // defineClass()的作用就是根据给定的class源文件返回一个对应的Class对象 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; }
最后补充一点关于数组类加载的细节
数组类的Class对象,不是由类加载器去创建的,而是在Java运行期JVM根据需要自动创建的。对于数组类的类加载器来说,是通过Class.getClassLoader()返回的,与数组当中元素类型的类加载器是一样的,如果数组当中的元素类型是基本数据类型,数组类是没有类加载器的。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。