类加载器是通过类的全限定名,来获取类的二进制字节流的代码。
类加载机制:JVM把Class数据加载到内存,并对数据进行验证、准备、解析、初始化,最终形成可以被虚拟机使用的Java类。
类的默认加载器,通过双亲委派机制进行类的加载。
加载类的加载器和类本身一起确定类的唯一性,若同一个Class文件,被不同的加载器加载,则是不同的类。
当一个加载器收到加载一个类的请求时,会先把该请求委派给自己的父类加载器执行加载,故所有的类加载都会被委派到启动类加载器中,若父类加载器加载失败,才会自己尝试加载。
在ClassLoader类中的loadClass方法中实现
以上源代码的逻辑为:
通过构造器注入parent
自定义一个类加载器,继承抽象类ClassLoader,重写loadClass方法,使其不进行双亲委派。
1、第1次是JDK1.2之前,那时已经有了类加载器和ClassLoader类,但是不是双亲委派机制的
2、第2次是模型自身的缺陷导致的,双亲委派机制使得越基础的类越由上层的加载器进行加载,正常情况下,用户代码继承、调用基础类,双亲委派机制加载没有问题。但是若从基础类中调回用户代码,则在上层加载器中,是无法找到下层的应用代码的,此时就需要破坏双亲委派机制,Java中由基础类调用SPI接口的地方都会如此。
SPI接口:Service Provider Interface,如JNDI、JDBC等。其本质是面向接口编程,具体实现或扩展由第三方在应用程序中实现。
在上层类加载器中无法加载到应用程序中实现的具体类,如何解决这个问题?
通过线程上下文类加载器实现ContextClassLoader。由Thread类的setContextClassLoader方法设置,若未设置,则会继承父线程的类加载器。在应用程序中,若没有设置过这个值,则默认为应用程序类加载器。
因此,在调用SPI接口时,可以通过getContextClassLoader获取线程上下文加载器,通过应用程序加载器去加载所需的SPI服务类。这是一种父类加载器去请求子类加载器完成类加载的过程,破坏了双亲委派机制。如DriverManager中的实现
3、第3次破坏双亲委派机制是为了追求程序的动态性,如代码的热替换,程序的热部署等,即代码替换或模块替换后,不需要重启即可生效。
这种实现是基于自定义类加载器实现的,此时加载器不再是有上下层级的树状结构,而是一个网状结构,每一个模块都有一个自定义的加载器,每当要替换掉一个模块时,就会将模块和类加载器都一起替换掉,以实现代码的热替换。
参考书籍:《深入理解Java虚拟机》第3版,作者:周志明
原文:https://www.cnblogs.com/a-candy/p/14824055.html