【Java虚拟机11】线程上下文类加载器
前言
目前学习到的类加载的知识,都是基于【双亲委托机制】的。那么JDK难道就没有提供一种打破双亲委托机制的类加载机制吗?
答案是否定的。
JDK为我们提供了一种打破双亲委托模型的机制:线程上下文类加载器。
1.引出【线程上下文类加载器】
我们来复习一下JDBC执行SQL的代码【画外音:感觉回到了大学自学Java的时候】:
Connection conn = DriverManager.getConnection(url, user, password);
String sql = "insert into user (name, pwd) values(?,?)";
Statement st = conn.createStatement();
st.executeQuery(sql);
重点关注一下接口Connection,Statement接口,大家都很清楚,这2个接口是JDK提供给各个数据库厂商的通过JAVA访问数据库的接口。
而这些接口是在包java.sql里面,而这个包是在rt.jar这个包中。所以这些JDK所提供的标准接口的Class文件是被启动类加载器所加载的。
而数据库厂商:如MySQL/Oracle根据Java语言的标准所提供的驱动包,如:mysql-connector-java-5.1.38.jar,这些驱动包一般在项目中都仅仅是在classpath目录下,
并不能被启动类加载器加载,只能被系统类加载器加载。
那么问题来了:Connection conn = DriverManager.getConnection(url, user, password);
方法返回的实例其实是一个com.mysql.jdbc.ConnectionImpl(应该被AppClassLoader加载),
而方法返回值的定义为java.sql.Connection(应该被BootstrapClassLoader加载)。
根据前面学习的命名空间的逻辑:“由父类加载器加载的类不能看见子加载器加载的类”,按这一规则来说,获取Connection的代码是不能执行成功的。
其实JDK已经想到并帮我们处理了这个问题。
它使用了一种叫做线程上下文类加载器的CLassLoader机制,来打破双亲委托机制在某些场景无法满足扩展的需求,实现这种场景的应用。
2.概念
2.1当前类加载器(Current Class Loader)
每个类都会使用自己的类加载器(即加载自身的类加载器)去加载其他类(指的是所依赖的类),
如果ClassX引用了ClassY,那么ClassX的类加载器就会先去加载ClassY(前提是ClassY尚未被加载过)。
2.2线程上下文类加载器(Thread Context Class Loader)
线程上下文类加载器是从JDK1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader cl)分别用来获取和设值上下文类加载器。
如果没有通过setContextClassLoader(ClassLoader cl)进行设置的话,线程将继承其父线程的上下文类加载器。
JAVA应用运行时的初始线程的上下文类加载器是系统类加载器(AppClassLoader)。在线程中运行的代码可以通过该类加载器来加载类与资源。
线程上下文类加载器就是当前线程的Current Class Loader
2.3线程上下文类加载器是如何打破双亲委托模型的
SPI (Service Provider Interface)
父classloader可以使用当前线程Thread.currentThread().getContextLoader()所指定的classloader加载的类。
这就改变了父classloader不能使用子classloader或是其他没有直接父子关系的classloader加载类的情况,即改变了双亲委托模型。
在双亲委托模型下,类加载是由下至上的,即下层的加载器会委托上层进行加载,但是对于SPI来说,有些接口是JAVA核心库所提供的。
而JAVA核心库是由启动类加载器来加载的,而这些接口的实现是来自于不同的jar包(厂商的提供),JAVA的启动类加载器是不会加载其他来源的jar包。
这样传统的双亲委托模型就无法满足SPI的要求。
而通过给当前线程设置上下文类加载器就可以由设置的上下文类加载器来实现对接口实现类的加载。(打破双亲委托模型限制)
3.默认的线程上下文类加载器
3.1 Launcher实例化时,默认设置线程上下文类加载器
在上次的文章【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析第二步中,简要介绍了Launcher实例的初始化过程,其中有一句代码当时是一笔带过的,这次拿出来再看看。
就是在Launcher类构造方法,首先初始化了ExtClassLoader,然后初始化了AppClassLoader,之后调用了
public Launcher() {
//balabala.....
Thread.currentThread().setContextClassLoader(this.loader);
//balabala.....
}
3.2 ClassLoader.getSystemClassLoader()时,默认设置线程上下文类加载器
依然在上一篇文章【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析第四步中,也存在一个设置默认的线程上下文类加载器的代码。
可以看出,虚拟机默认的线程上下文类加载器为系统类加载器(AppClassLoader)
4.线程上下文类加载器的一般使用模式
线程上下文类加载器的一般使用模式为:
ClassLoader originClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(myClassLoader);
myMethod();
//myMethod里面则调用了Thread.currentThread().getContextClassLoader(),用外面设置的加载器(myClassLoader)去完成一些自己希望完成的事情。
} finally {
Thread.currentThread().setContextClassLoader(originClassLoader);
}
5.总结
- 如果一个类由类加载器A加载,那么这个类的依赖类也会被类加载器A加载(前提是这个依赖类尚未被加载过)。
- ThreadContextClassLoader的存在就是为了破坏双亲委托模型。
- 当高层提供了统一的接口让低层去实现,同时又要在高层去加载(或实例化)低层的类的时候,就必须用ThreadContextClassLoader来帮助高层的ClassLoader来找到并加载低层类。
【Java虚拟机11】线程上下文类加载器的更多相关文章
- 深入理解Java类加载器(二):线程上下文类加载器
摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...
- 线程上下文类加载器ContextClassLoader内存泄漏隐患
前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器Cont ...
- JVM 线程上下文类加载器
当前类加载器(Current ClassLoader) 每个类都会使用自己的类加载器(即加载自身的类加载器)来去加载其他类(指所依赖的类) 如果ClassX引用了ClassY,那么ClassX的类加载 ...
- 【JVM学习笔记】线程上下文类加载器
有许多地方能够看到线程上下文类加载的设置,比如在sun.misc.Launcher类的构造方法中,能够看到如下代码 先写一个例子建立感性认识 public class Test { public st ...
- 7. 通过JDBC源码来分析线程上下文类加载器以及SPI的使用
目录 1. 什么是全盘负责委托机制 2. 为什么需要有线程上下文类加载器 2.1 使用JDBC的例子,分析为什么双亲委托机制不能实现要求 2.2 线程上下文类加载器的作用 3. 线程上下文类加载器的使 ...
- 线程上下文类加载器(Context ClassLoader)
1.线程上下文类加载器是从jdk1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader c1),分别用来 ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- 通过JDBC驱动加载深刻理解线程上下文类加载器机制
关于线程上下文类加载器已经在之前学得比较透了,作为一个收尾,这里用平常J2EE开发时JDBC连接Mysql数据库常见的一段代码通过分析它的底层进一步加深对线程上下文类加载器的理解,所以先来将连接应用代 ...
- 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...
随机推荐
- Walker
emmm.......随机化. 好吧,我们不熟. 考虑随机选取两组数据高斯消元消除结果后带入检验,能有超过1/2正确就输出. 其实方程就四个,手动解都没问题. 只是要注意看sin与 ...
- Intel® QAT加速卡之性能简介
Intel QuickAssist Adapter 8950 设备简介 支持英特尔QuickAssist技术的英特尔QuickAssist适配器提供加密加速和压缩加速服务. 1. Key featur ...
- Servlet体系结构
一.使用HttpServlet 其中,HttpServlet在重写的service()方法中对http请求的共7中提交方式进行了判断,所以只要我们只要重写对应的请求方式处理逻辑方法 doGet()和d ...
- Stream流方法引用
一.对象存在,方法也存在,双冒号引用 1.方法引用的概念: 使用实例: 1.1先定义i一个函数式接口: 1.2定义一个入参参数列表有函数式接口的方法: 1.3调用这个入参有函数式接口的方法: lamb ...
- vue el-transfer新增拖拽排序功能---sortablejs插件
<template> <!-- target-order="unshift"必须设置,如果不设置的话后台穿的value值得顺序会被data重置 - --> ...
- 理解MySQL回表
回表就是先通过数据库索引扫描出数据所在的行,再通过行主键id取出索引中未提供的数据,即基于非主键索引的查询需要多扫描一棵索引树. 因此,可以通过索引先查询出id字段,再通过主键id字段,查询行中的字段 ...
- 2020ICPC沈阳站C题 Mean Streets of Gadgetzan
大致题意 原题链接 翻译 \(有n个逻辑变量 请你分别对它们赋值 使其满足m个命题\) \(命题有四种格式:\) 单独数字x 表示第x个逻辑变量为真 ! + 数字x 表示第x个逻辑变量为假 若干个数字 ...
- 机器学习——支持向量机SVM
前言 学习本章节前需要先学习: <机器学习--最优化问题:拉格朗日乘子法.KKT条件以及对偶问题> <机器学习--感知机> 1 摘要: 支持向量机(SVM)是一种二类分类模型, ...
- 离散化模板题 I ——重复元素离散化后的数字相同
离散化模板题 I --重复元素离散化后的数字相同 题目描述 现有数列A1, A2, ⋯, An,数列中可能有重复元素. 现在要求输出该数列的离散化数列,重复元素离散化后的数字相同. 输入 第一行,一 ...
- Php实现简易购物商城系统
实现功能: 1.系统功能模块包括: 1)登陆注册模块 包括验证码.找回密码.注册模块中要使用Ajax判断用户名是否已经存在,使用正则表达式判断电子邮件.手机号和用户密码的格式是否合法. 2)用户管理模 ...