一、类加载器与双亲委派模型
前面我们已经提到了类加载机制,了解了类加载的整个流程,但具体由什么加载器加载类我们并不知道。下面,我们就只要讲讲类加载器内容。完成“将类的全限定名描述的类转为二进制字节流”这个动作的代码,称之为类加载器(Class Loader)。绝大多数Java程序都会被以下三个系统提供的加载器来加载,分别是启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)、应用程序类加载器(Application Class Loader)。用户还可以根据需要,自己定义类加载器,称为用户类加载器(User Class Loader)。下面具体展开说明这四种类加载器:
-
启动类加载器(Bootstrap Class Loader): 由C/C++编写,用来加载java核心类库,无法被java程序直接引用。
-
扩展类加载器(Extension Class Loader):它用来加载 Java 的扩展库,通常以javax开头的包属于扩展类库。
-
应用类加载器(Application Class Loader):又称系统类加载器,其主要负责加载程序开发者自己编写的java类。它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。
-
用户自定义类加载器 User Class Loader):用户通过继承 java.lang.ClassLoader类的方式自行实现的类加载器。
各种加载器之间并不是孤立的,它们之间存在着层次关系,这种层次关系称为双亲委派模型,如下图所示:
双亲委派工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载器无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
优点:避免类的重复加载,确保类的全局唯一性。也能保证程序安全,防止核心代码被篡改。
二、双亲委派机制的破坏
双亲委派模型并不是一个强制性约束的模型,大部分加载器都遵循这个模型,但也有例外的情况。历史上有三次对双亲委派机制的破坏:
-
第一次破坏是由于要保证Java的往下兼容,双亲委派模型在JDK 1.2之后才被引入,但是类加载器的概念和抽象类 java.lang.ClassLoader则在Java的第一个版本中就已经存在。为了向下兼容旧代码,所以无法以技术手段避免loadClass()被子类覆盖的可能性,只能在JDK 1.2之后的java.lang.ClassLoader中添加一个新的 protected方法findClass(),并引导用户编写的类加载逻辑时尽可能去重写这个方法,而不是在 loadClass()中编写代码。
-
第二次破坏是由这个模型自身的缺陷导致的,如果有基础类型又要调用回用户的代码,就需要父类加载器(BootStrap Class Loader)委托应用程序类加载器加载(Application Class Loader),这就破坏了双亲委派模型。JDK定义的接口规范,需要加载各大厂商spi实现,jdbc就是这种情况。
-
第三次破坏是由于用户对模块化的追求导致的,热替换、热部署等。为了实现热插拔,热部署,模块化,意思是添加一个功能或减去一个功能不用重启,只需要把这模块连同类加载器一起换掉就实现了代码的热替换。类加载器不再是双亲委派模型中的树状结构,而是进一步发展为网状结构。
想要破坏双亲委派模型,只需要继承ClassLoader类并重写loadClass()方法即可,一般不推荐主动破坏双亲委派机制,推荐重写findClass()方法。