ClassLoader类加载源码解析

Java类加载器

1、BootClassLoader: 用于加载Android Framework层class文件。
2、PathClassLoader: 用于Android应用程序类加载器。可以加载指定的dex,jar、zip、zpk中的classes.dex
3、DexClassLoader:加载指定的dex,以及jar、zip、apk中的classes.dex

源码解析

1.ClassLoader中提供loadClass用于加载指定类

//ClassLoader.java
public Class<?> loadClass(String name) throws ClassNotFoundException {
 //该处调用了两个参数的重载方法
    return loadClass(name, false);
  }

 protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
  {
   //先查一下该类是否已经加载过了
      Class<?> c = findLoadedClass(name);
      if (c == null) {
        try {
          //双亲委托机制,先让爸爸去找
          if (parent != null) {
            c = parent.loadClass(name, false);
          } else {
            //如果parent为null,则用BootClassLoader进行加载
            c = findBootstrapClassOrNull(name);
          }
        } catch (ClassNotFoundException e) {
          // ClassNotFoundException thrown if class not found
          // from the non-null parent class loader
        }

        if (c == null) {
          //如果都找不到就自己去找,此方法在子类BaseDexClassLoader类中有重写
          c = findClass(name);
        }
      }
      return c;
  }

2.BaseDexClassLoader类中对findClass有重写,也是实际会使用执行的

//BaseDexClassLoader.java
//查找class
  @Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    ...
    //这里通过pathList变量来查找,而pathList是在BaseDexClassLoader的构造方法中初始化的
    Class c = pathList.findClass(name, suppressedExceptions);
    ...
    return c;
  }

private final DexPathList pathList;
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
      String librarySearchPath, ClassLoader parent, boolean isTrusted) {
    super(parent);
    //构造方法中初始化pathList变量
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
  }

3.BaseDexClassLoader中是通过调用DexPathList中的findClass来实现的,那么接下来我们分析一下DexPathList是怎么实现的

//DexPathList.java
//是一个Element数组,一个element中包含一个 DexFile,DexFile就代表一个Dex文件,里面的native(C/C++)函数来进行Dex的加载工作
  private Element[] dexElements;

public Class<?> findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
     //此处调用Element的findClass来实现,
      Class<?> clazz = element.findClass(name, definingContext, suppressed);
      if (clazz != null) {
        return clazz;
      }
    }
    return null;
  }
// Element为DexPathList的内部类
static class Element {
 private final File path;
    //一个DexFile就代表一个Dex文件
    private final DexFile dexFile;
    //有多个构造方法,但都仅是将值传过来,让Element来持有一个DexFile
    public Element(DexFile dexFile) {
  this.dexFile = dexFile;
      this.path = null;
 }

    public Class<?> findClass(String name, ClassLoader definingContext,
        List<Throwable> suppressed) {
        //通过DexFile来加载类
      return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
          : null;
    }
 }

DexPathList(ClassLoader definingContext, String dexPath,
      String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
      //通过makeDexElements方法为dexElements初始化
 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                      suppressedExceptions, definingContext, isTrusted);
}
//腾讯系的热修复,诸如微信tinker、qq空间qfix原理便是反射此方法,将修复后的类打包成dex,通过反射该方法来将文件转化为Element,并将新生成的element放到dexElements前面,这样下次系统再去寻找某个class时,会先从修复后的dex中来找class,找到后便不再继续查找,从而修复该class,此方式便为插桩
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
      List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
   Element[] elements = new Element[files.size()];
   ...
   for (File file : files) {
  if (name.endsWith(DEX_SUFFIX)) {
        //以 .dex 结尾的
         // Raw dex file (not inside a zip/jar).
         //加载dex文件
           dex = loadDexFile(file, optimizedDirectory, loader, elements);
           if (dex != null) {
             elements[elementsPos++] = new Element(dex, null);
           }
       }
 }
     ...
   return elements;
  }

private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
                    Element[] elements)
      throws IOException {
    if (optimizedDirectory == null) {
      return new DexFile(file, loader, elements);
    } else {
      String optimizedPath = optimizedPathFor(file, optimizedDirectory);
      return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
    }
  }

4.这里通过 new DexFile 或者 loadDex方法来创建DexFile,两者类似,那我们拿new DexFile 来举例分析

//DexFile.java
private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
      DexPathList.Element[] elements) throws IOException {
    ...
    //此处调用openDexFile来实现
    mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
    ...
  }

private static Object openDexFile(String sourceName, String outputName, int flags,
      ClassLoader loader, DexPathList.Element[] elements) throws IOException {
    //此处通过调用 openDexFileNative来实现
    return openDexFileNative(new File(sourceName).getAbsolutePath(),
                 (outputName == null)
                   ? null
                   : new File(outputName).getAbsolutePath(),
                 flags,
                 loader,
                 elements);
  }
//openDexFileNative是一个native方法,是由C/C++来实现的
private static native Object openDexFileNative(String sourceName, String outputName, int flags,
      ClassLoader loader, DexPathList.Element[] elements);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java中ClassLoader类加载学习总结

    双亲委派模型 类加载这个概念应该算是Java语言的一种创新,目的是为了将类的加载过程与虚拟机解耦,达到"通过类的全限定名来获取描述此类的二进制字节流"的目的.实现这个功能的代码模块就是类加载器.类加载器的基本模型就是大名鼎鼎的双亲委派模型(Parents Delegation Model).听上去很牛掰,其实逻辑很简单,在需要加载一个类的时候,我们首先判断该类是否已被加载,如果没有就判断是否已被父加载器加载,如果还没有再调用自己的findClass方法尝试加载.基本的模型就是这样(盗图

  • classloader类加载器_基于java类的加载方式详解

    基础概念 Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中.与普通程序不同的是.Java程序(class文件)并不是本地的可执行程序.当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader. JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现

  • 详解Android类加载ClassLoader

    基本知识 Java的类加载设计了一套双亲代理的模式,使得用户没法替换系统的核心类,从而让应用更安全.所谓双亲代理就是指,当加载类的时候首先去Bootstrap中加载类,如果没有则去Extension中加载,如果再没有才去AppClassLoader中去加载.从而实现安全和稳定. Java ClassLoader BootstrapClassLoader 引导类加载器 ,用来加载Java的核心库.通过底层代码来实现的,基本上只要parent为null,那就表示引导类加载器. 比如:charsets

  • Java运行时环境之ClassLoader类加载机制详解

    背景:听说ClassLoader类加载机制是进入BAT的必经之路. ClassLoader总述: 普通的Java开发其实用到ClassLoader的地方并不多,但是理解透彻ClassLoader类的加载机制,无论是对我们编写更高效的代码还是进BAT都大有裨益:而从"黄埔军校"出来的我对ClassLoader的理解都是借鉴了很多书籍和博客,站在了各大博主的肩膀上,感谢你们!上菜,Classloader最主要的作用就是将Java字节码文件(后缀为.class)加载到JVM中,JVM在启动时

  • Android开发中类加载器DexClassLoader的简单使用讲解

    简介 "类装载器"(ClassLoader),顾名思义,就是用来动态装载class文件的.标准的Java SDK中有个ClassLoader类,借助此类可以装载需要的class文件,前提是ClassLoader类初始化必须制定class文件的路径. import关键字引用的类文件和ClassLoader动态加载类的区别: import引用类的两个特点: 1.必须存在于本地,当程序运行该类时,内部类装载器会自动装载该类. 2.编译时必须在现场,否则编译过程会因找不到引用文件而不能正常编译

  • ClassLoader类加载源码解析

    Java类加载器 1.BootClassLoader: 用于加载Android Framework层class文件. 2.PathClassLoader: 用于Android应用程序类加载器.可以加载指定的dex,jar.zip.zpk中的classes.dex 3.DexClassLoader:加载指定的dex,以及jar.zip.apk中的classes.dex 源码解析 1.ClassLoader中提供loadClass用于加载指定类 //ClassLoader.java public C

  • Java源码解析之ClassLoader

    一.前言 一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常.而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它cl

  • Tomcat的类加载机制流程及源码解析

    目录 前言 1.Tomcat 的类加载器结构图: 2.Tomcat 的类加载流程说明: 3.源码解析: 4.为什么tomcat要实现自己的类加载机制: 前言 在前面 Java虚拟机:对象创建过程与类加载机制.双亲委派模型 文章中,我们介绍了 JVM 的类加载机制以及双亲委派模型,双亲委派模型的类加载过程主要分为以下几个步骤: (1)初始化 ClassLoader 时需要指定自己的 parent 是谁 (2)先检查类是否已经被加载过,如果类已经被加载了,直接返回 (3)若没有加载则调用父加载器 p

  • skywalking源码解析javaAgent工具ByteBuddy应用

    目录 前言 Agent模块源码分析 第一步,加载配置信息: 第二步,加载需要被Agent的插件: 第三步,加载Agent端所需要的服务: 第四步,使用ByteBuddy增强插件定义的所有class: javaAgent的应用 BYTEBUDDY应用 通过委托实现Instrumentation 实现方法级别的安全性 实现安全功能的JAVAAGENT 前言 关于skywalking请看我上一篇博文,skywalking分布式服务调用链路追踪APM应用监控 其使用javaAgent技术,使得应用接入监

  • Spring AOP实现声明式事务机制源码解析

    目录 一.声明式全局事务 二.源码 三.小结: 一.声明式全局事务 在Seata示例工程中,能看到@GlobalTransactional,如下方法示例: @GlobalTransactional public boolean purchase(long accountId, long stockId, long quantity) { String xid = RootContext.getXID(); LOGGER.info("New Transaction Begins: " +

  • Spring SpringMVC在启动完成后执行方法源码解析

    关键字:spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件) 应用场景:很多时候我们想要在某个类加载完毕时干某件事情,但是使用了spring管理对象,我们这个类引用了其他类(可能是更复杂的关联),所以当我们去使用这个类做事情时发现包空指针错误,这是因为我们这个类有可能已经初始化完成,但是引用的其他类不一定初始化完成,所以发生了空指针错误,解决方案如下: 1.写一个类继承spring的ApplicationListener监听,并监控ContextRefresh

  • springboot默认日志框架选择源码解析(推荐)

    背景: 今天新生成一个springboot项目,然而启动日志,还有mybatis的详细日志无法打印出来,自写程序中打印的日志可以输出:网上找了很多资料,都没法解决问题:于是决定跟一下源码,弄清springboot日志相关的逻辑. 环境配置:macbook: intellij idea community edition 2020.03 : gradle 6.8.3 jdk1.8 : gradle引用包如下: dependencies { compile "com.alibaba:fastjson

  • Tomcat源码解析之Web请求与处理

    前言 Tomcat最全UML类图 Tomcat请求处理过程: Connector对象创建的时候,会创建Http11NioProtocol的ProtocolHandler,在Connector的startInteral方法中,会启动AbstractProtocol,AbstractProtocol启动NioEndPoint进行监听客户端的请求,EndPoint接受到客户端的请求之后,会交给Container去处理请求.请求从Engine开始经过的所有容器都含有责任链模式,每经过一个容器都会调用该容

  • Spring源码解析之推断构造方法

    Spring推断构造方法 贴个测试代码直接开干,这只是个样例,其他情况自行分析 @Component public class OrderService { public OrderService() { System.out.println("无参构造方法"); } @Autowired(required = false) public OrderService(UserService userService) { System.out.println("一个参数的构造方法

  • Java Lambda 表达式源码解析

    Java Lambda 源码分析 问题: Lambda 表达式是什么?JVM 内部究竟是如何实现 Lambda 表达式的?为什么要这样实现? 一.基本概念 1.Lambda 表达式 下面的例子中,() -> System.out.println("1") 就是一个 Lambda 表达式.Java 8 中每一个 Lambda 表达式必须有一个函数式接口与之对应.Lambda 表达式就是函数式接口的一个实现. @Test public void test0() { Runnable

随机推荐