双亲委派机制

  1. 什么是双亲委派

    1. java虚拟机中,任何一个类由加载他的类加载器和这个类一同来确立起唯一性
    2. JVM对类的唯一标识,可以简单理解为由ClassLoader id + PackageName + ClassName组成,因此在一个运行程序中有可能存在两个包名和类名完全一致的类,但是如果这两个类不是由一个ClassLoader加载,会被视为两个不同的类,此时无法将一个类的实例强制转化为另外一个类,这就是类加载器的隔离性
    3. 为了解决类加载器的隔离问题,JVM引入了双亲委派模型
  2. 双亲委派模式

    1. 任何一个类加载器在接到一个类的加载请求时,都会让其父类进行加载,只有父类无法加载(或者没有父亲)的情况下,才尝试自己加载

    2. 在这里插入图片描述

    3. 使用双亲委派机制,可以保证,每一个类只会有一个类加载器

      1. 例如:java最基础的Object类,他存放在rt.jar中,这是Bootstrap的职责范围,当向上委派到Booststrap时就会被加载
    4. 如果没有使用双亲委派模式,可以任由自定义加载器进行加载的话,java这些核心类的API就会被随意篡改,无法做到一致性加载效果

  3. JDK中ClassLoader.loadClass()类加载器中的加载类的方法,源码如下

    1. public Class<?> loadClass(String name) throws ClassNotFoundException {
          return loadClass(name, false);
      }
      
    2. protected Class<?> loadClass(String name, boolean resolve)
              throws ClassNotFoundException
      {
          // 1.首先要保证线程安全
          synchronized (getClassLoadingLock(name)) {
              // 2.先判断这个类是否被加载过,如果加载过,直接跳过
              Class<?> c = findLoadedClass(name);
              if (c == null) {
                  long t0 = System.nanoTime();
                  try {
                      // 3.有父类,优先交给父类尝试加载;如果为空,使用BootstrapClassLoader类加载器
                      if (parent != null) {
                          c = parent.loadClass(name, false);
                      } else {
                          c = findBootstrapClassOrNull(name);
                      }
                  } catch (ClassNotFoundException e) {
                      // 父类加载失败,这里捕获异常,但不需要做任何处理
                  }
      
                  // 4.没有父类,或者父类无法加载,尝试自己加载
                  if (c == null) {
                      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;
          }
      }
      
  4. 如何自定义类加载器

    1. 针对某些场景,比如通过网络来传输JAVA类的字节码文件,为保证安全性,这些字节码经过了加密处理,这时系统提供的类加载器就无法对其进行加载,此时需要我们可以自定义一个类加载器来完成文件的加载

    2. 自定义类加载器需要继承classLoader类

    3. public class CustomClassLoader extends ClassLoader {
      
          private String classPath;
      
          public CustomClassLoader(String classPath) {
              this.classPath = classPath;
          }
      
          @Override
          protected Class<?> findClass(String name) throws ClassNotFoundException {
              Class<?> c = findLoadedClass(name);
              if (c == null) {
                  byte[] data = loadClassData(name);
                  if (data == null) {
                      throw new ClassNotFoundException();
                  }
                  return defineClass(name, data, 0, data.length);
              }
              return null;
          }
      
          protected byte[] loadClassData(String name) {
              try {
                  // package -> file folder
                  name = name.replace(".", "//");
                  FileInputStream fis = new FileInputStream(new File(classPath + "//" + name + ".class"));
                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  int len = -1;
                  byte[] b = new byte[2048];
                  while ((len = fis.read(b)) != -1) {
                      baos.write(b, 0, len);
                  }
                  fis.close();
                  return baos.toByteArray();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return null;
          }
      }
      
    4. 测试类如下

      public class ClassLoaderTest {
      
          public static void main(String[] args) {
              ClassLoader loader = Thread.currentThread().getContextClassLoader();
              System.out.println("current loader:" +  loader);
          }
      }
      
    5. 将ClassLoaderTest.java源文件放在指定的目录下,并通过javac命令编译成ClassLoaderTest.class,最后进行测试

    6. public class CustomClassLoaderTest {
      
          public static void main(String[] args) throws Exception {
              String classPath = "/Downloads";
              CustomClassLoader customClassLoader = new CustomClassLoader(classPath);
              Class<?> testClass = customClassLoader.loadClass("com.example.ClassLoaderTest");
              Object obj = testClass.newInstance();
              System.out.println(obj.getClass().getClassLoader());
          }
      }
      
      输出结果:com.example.CustomClassLoader@60e53b93
      
    7. 在实际使用过程中,最好不要重写loadClass方法,避免破坏双亲委派模型

  5. 总结

    1. 双亲委派,指的是在接受类加载请求时,会让父类加载器试图加载该类,只有在父类加载器无法加载该类或者没有父类时,才会尝试从自己的类路径中加载该类
    2. 针对某些场景,如果要实现类的隔离,可以自定义类加载器来实现特定类的加载
  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值