- Java类加载机制
- tomcat类加载机制
- 上下文类加载器
- 类加载器选择
Java类加载 https://blog.csdn.net/cnahyz/article/details/82219210
tomcat类加载 https://www.cnblogs.com/aspirant/p/8991830.html
上下文类加载器 https://blog.csdn.net/zhoudaxia/article/details/35897057
1、Java类加载机制
-
类加载过程
类主要的 生命周期有:
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序进行,而解析阶段则不一定,它在某些情况下可能在初始化阶段后在开始,因为java支持运行时绑定。
具体的加载过程可以参考:https://blog.csdn.net/zhaocuit/article/details/93038538 -
类加载器
BootStrap ClassLoader: 非Java语言实现
一般由C++语言实现、负责加载java基本类主要是<JAVA_HOME>/lib/rt.jar
Extension ClassLoader: ExtClassLoader
负责加载java的一些扩展类主要是<JAVA_HOME>/lib/ext目录下的java包
sun.misc.Launcher$ExtClassLoader
static class ExtClassLoader extends URLClassLoader {
Application ClassLoader:AppClassLoader
负责加载自己定义的类以及第三方框架的类库 也叫系统类加载器 System ClassLoader
static class AppClassLoader extends URLClassLoader {
父子委派关系:加载的时候首先交给父加载器[们]加载,所有为null才自己加载会出现对应的class对象调用getClassLoader
返回的可能不是调用 loadClass("classname")
的类加载器
双亲委派模型(一般)
双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码。
判断是否加载,若是加载了直接返回,否则交给父类,成功返回,不成功再自己尝试加载
优势:避免重复加载,防止核心API库被随意篡改。核心包访问权限,否则抛出异常: java.lang.SecurityException: Prohibited package name: java.lang
例外(违反双亲委派):
- 自定义加载顺序,但以java开头的类不能被自定义的了加载器加载,这是由java安全机制保证的,避免混乱
- 网状加载顺序:OSGI和java9模块化系统中,一个模块加载的类可能是自己加载也可能是委派给其他类加载器
- 父加载器委派给子加载器:线程上下文类加载器
2、tomcat类加载机制
Tomcat的类加载机制 是 违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托。
对于JVM来说:因此,按照这个过程可以想到,如果同样在CLASSPATH指定的目录中和自己工作目录中存放相同的class,会优先加载CLASSPATH目录中的文件。
实际类继承关系UML类图:
3、上下文类加载器
线程上下文类加载器(context class loader)是从 JDK 1.2 开始引入的。类 java.lang.Thread中的方法 getContextClassLoader()
和 setContextClassLoader(ClassLoader cl)
用来获取和设置线程的上下文类加载器。如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。
解决:Java 提供了很多服务提供者接口(Service Provider Interface,SPI)(Service Provider Interface,SPI)第三方API,SPI 实现的 Java 类一般是由系统类加载器来加载的。启动类加载器是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给系统类加载器,因为它是系统类加载器的祖先类加载器。也就是说,类加载器的代理模式无法解决这个问题。
此时Java提供了线程上下文类加载器,用来打破原有的双亲委派机制,让父加载器(启动类加载器)可以代理给子加载器(系统类加载器)让其加载,即不走双亲委派,子加载器先加载。
4、类加载器选择
一般有:系统类加载器、当前类加载器、上下文加载器
1、系统类加载器 通常不会使用。此类加载器处理启动应用程序时classpath指定的类,可以通过ClassLoader.getSystemClassLoader()来获得。所有的ClassLoader.getSystemXXX()接口也是通过这个类加载器加载的。一般不要显式调用这些方法,应该让其他类加载器代理到系统类加载器上。
2、一般来说,上下文类加载器要比当前类加载器更适合于框架编程,而当前类加载器则更适合于业务逻辑编程。
Java的使用:
(1)JNDI使用线程上下文类加载器。
(2)Class.getResource()和Class.forName()使用当前类加载器。
(3)JAXP使用上下文类加载器。
(4)java.util.ResourceBundle使用调用者的当前类加载器。
(5)URL协议处理器使用java.protocol.handler.pkgs系统属性并只使用系统类加载器。
(6)Java序列化API缺省使用调用者当前的类加载器。