Java双亲委派机制

Java中类的加载相关的问题

先了解一下几个类:

1.Class:这个是表示在一个运行的Java应用中,它的实例可以代表类,以及接口,特别地,枚举属于类,注解属于接口,每一个数组属于被表示为被所有有着相同元素类型和数据规模的数组共享的Class对象。(这个也是我们在使用反射的时候需要用到的对象)

/* 私有构造器,只有JVM可以生成Class对象,这个构造方法不使用,并是为了防止默认的构造方法会生成(没有构造方法的时候,会有一个空餐的构造方法)
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being generated.
*/
private Class(ClassLoader loader) {
   // Initialize final field for classLoader. The initialization value of non-null
   // prevents future JIT optimizations from assuming this final field is null.
   classLoader = loader;
}
/**
* Returns the class loader for the class. Some implementations may use
* null to represent the bootstrap class loader. This method will return
* null in such implementations if this class was loaded by the bootstrap
* class loader.返回这个类的类加载器,某些执行过程中可能使用Null来代替bootstrap
* 为null的话,就直接返回null,否则会往下走,一直涉及权限相关的东西(这个过程会修改ClassLoader,
* 所以不是它不为null的时候立即就返回ClassLoader)
*/
@CallerSensitive
public ClassLoader getClassLoader() {
   ClassLoader cl = getClassLoader0();
   if (cl == null)
       return null;
   SecurityManager sm = System.getSecurityManager();
   if (sm != null) {
       ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
  }
   return cl;
}

2.ClassLoader:将一些实体类,接口等转化为虚拟机中存储的对象(Class),完成这个任务的角色就是ClassLoader,翻译过来就是类加载器,ClassLoader在java中也有对应的抽象类,我们平时所说的三个类加载器的UML图如下(BootStrap是源代码中提了一下,并没有对应的Java类,是Cpp/C写的) image-20220310181726907

需要注意的是,平时有些博客在写到双亲委派机制的时候,总是说父类加载器,其实他们想说的是是加载器中的属性parent,很多一知半解的小伙伴就会以为是他的父类,以为是"父类"加载器


// "父”类加载器代理声明:VMXXX,因此,所有的新属性都必须在它之后添加
   private final ClassLoader parent;


// binary name:例如"java.lang.String" "javax.swing.JSpinner$DefaultEditor"
protected Class<?> loadClass(String name, boolean resolve)
       throws ClassNotFoundException
  {
       synchronized (getClassLoadingLock(name)) {
           // 查看该类是否已经被加载了,底层调用了native方法findLoadClass0(name),就是参数中的name
           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) {
               
              }

               if (c == null) {
                   // 还是为null的话,会调用自己的findClass()方法,这就是由上往下加载的关键
                   long t1 = System.nanoTime();
                   c = findClass(name);

              }
          }
           if (resolve) {
               resolveClass(c);
          }
           return c;
      }
  }

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");
                       //这个就是非BootStrap能够加载此类的关键
                       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;
  }

//查看各个类加载器的Parent属性是什么
@Test
public void printNormalClassLoadersParentField() {
   //AppClassLoader的parent属性是:Ext
   System.out.println(User.class.getClassLoader().getParent());
   //ExtClassLoader的parent属性是: null
   System.out.println(User.class.getClassLoader().getParent().getParent());
}

//查看各个类加载器是什么类加载器加载的
@Test
public void printNormalClassLoadersClassLoader() {
   //AppClassLoader是由BootStrap加载的
   System.out.println(User.class.getClassLoader().getClass().getClassLoader());
   //ExtClassLoader是由BootStrap加载的
   System.out.println(User.class.getClassLoader().getParent().getClass().getClassLoader());

}

BootStrap:没有对应的Java类,加载java的核心类库 ExtClassLoader:扩展类加载器 AppClassLoader:系统类加载器

//查看常见的三种类加载器
@Test
public void printNormalClassLoader() {
   //null,表示是BootStrap进行的加载
   System.out.println(String.class.getClassLoader());
   //这个是ExtClassLoader
   System.out.println(sun.security.ec.SunEC.class.getClassLoader());
   //这个也是AppClassLoader
   System.out.println(User.class.getClassLoader());
}


//查看各个类加载器的加载路径
@Test
public void testNormalClassLoadersLoadPath() {
   //这个URL是java.net.URL,不是print包下的URL,打印的是BootStrap加载的类的路径
   URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
   for (URL url : urLs) {
       System.out.println(url.toExternalForm());
  }
   System.out.println("***************************");
   //这个打印的是ExtClassLoader加载的路径
   String extDirs = System.getProperty("java.ext.dirs");
   for (String path : extDirs.split(";")) {
       System.out.println(path);
  }
   System.out.println("***************************");
   //这个打印的是AppClassLoader加载的路径
   String classPath = System.getProperty("java.class.path");
   for (String path : classPath.split(";")) {
       System.out.println(path);
  }

}

 

双亲委派的过程(这个就是我们常见的图):

image-20220310134606290

 

调用AppClasssLoader的loadClass()方法的时候,会调用parent属性ExtCL的loadClass(),然后查看parent属性为null,随后调用调用findBootstrapClassOrNull()(native方法),就相当于一个递归的出口,举个实际的例子,比如我们自己新建的实体类User类,他的类加载过程就是这样,先在AppClassLoader中调用super.loadClass()方法,也就是ExtClassLoader的loadClass()方法,此时因为它的Parent是null,所以会调用那个findBootstrapClassOrNull方法,返回Null,然后抛出ClassNotFind,随后又调用ExtCL的findClass()(实际上是URLCL的findClass()方法),结果是抛出ClassNotFind(),给我们的AppCL,然后AppCL又调用findClass()(也是URLCL的),再调用defineClass,一直到底层native方法调用。最后才调用成功。 也即是,类的加载最终是在Native方法findBootstrapClass中完成,不然就是在findClass或者说是Native方法defineClass()方法中完成。 至于为什么从bootStrap往下找的时候,不会在extCL中的findClass()方法中加载完成呢->是由这个方法决定的

//返回的res为Null的时候,就不会进行defineClass()方法的调用
Resource res = ucp.getResource(path, false);

以上就是我本次分享的内容,有错误也欢迎指出,评论区一起讨论

posted on 2022-03-11 11:00  wecantstop  阅读(300)  评论(0)    收藏  举报