双亲委派模型
站在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap
ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另外一种就是其他所有
的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类
java.lang.ClassLoader。
自JDK 1.2以来,Java一直保持着三层类加载器、双亲委派的类加载架构。三层类加载器为(JDK9之前):启动类加载器,扩展类加载器,应用程序类加载器。
如果一个类加载器收到了加载类请求,它不会自己加载会将这个请求交由父类去加载,如过父类没加载成功,它才会去尝试加载。每一个层次的类加载器都是如此。
判断两个类是否一致的前提是他们必须是同一个类加载器加载的,否则即使他们是同一个类也不一致。
- 启动类加载器
加载<JAVA_HOME>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中。 - 扩展类加载器
它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。 - 应用程序类加载器
它负责加载用户类路径(ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
JDK9之后引入了模块化系统,所以扩展类加载器自然而然被平台类加载器(Platform Class Loader)取代。
在将类加载给父类之前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载,
破坏双亲委派模型
- 自定义一个类加载器,重写loadClass()即可。
- 如果在父类加载器中加载内容想要加载用户的代码资源,如何解决?
为了解决这个困境,Java的设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置。Java中涉及SPI(Service Provider Interface)的加载基本上都采用这种方式来完成,例如JNDI、JDBC、JCE、JAXB和JBI等。 - OSGi实现模块化热部署。OSGI类加载过程 将委派类和java.*类交由父类加载器加载,其他类在平级类加载器中查找是否能加载。
深入理解Java虚拟机(周志明)阅读笔记,理解有限,如有错误还望指出。