【小家Java】从原理层面理解Java中的类加载器:ClassLoader、双亲委派模型、线程上下文类加载器(下)

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 【小家Java】从原理层面理解Java中的类加载器:ClassLoader、双亲委派模型、线程上下文类加载器(下)

线程上下文类加载器


该加载器十分的重要,也十分的优雅。在Tomcat和Spring中有大量的应用。作为补充,它可以补充JDK提供的三种加载器不能实现的功能,使之更为灵活。


双亲委派模型痛点场景:


Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。常见的 SPI 有 JDBC(Java官方并不提供具体实现,而是由各自的数据库厂商去实现)、JCE、JNDI、JAXP 和 JBI 等。


SPI接口均由Java核心库来提供,而实现代码都为其余厂商提供(一般都在我们引入的第三方jar包里面)。所以问题就来了:SPI接口中的代码经常需要加载具体的实现类,也就是说我再加载JDBC的时候就需要有实现类。 但是SPI接口是Bootstrap Classloader来加载的,而实现类在类路径由AppClassLoader来加载,所以SPI加载的时候铁定就加载不到实现类了。(因为违反了层级委托关系嘛)


解决方案:JDK1.2提供了上下文类加载器来解决此问题。它破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。看了很多博文,我一直都不理解它具体是如何打破“双亲委派模型”呢?知道我看到了JDBC驱动的加载过程,才彻底的了解了里面的原因~


写个案例:


    public static void main(String[] args) throws SQLException {
        //Class.forName("com.mysql.jdbc.Driver");
        Connection conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost:3306/jedi", "name", "password");
        System.out.println(conn); //com.mysql.jdbc.JDBC4Connection@15d0c81b
    }


细心的朋友会发现,我把平时我们认为必须要写的Class.forName("com.mysql.jdbc.Driver");这句代码去掉了,但程序还是能正常运行获取到链接。

这是为什么呢?这是因为从Java1.6开始自带的jdbc4.0版本已支持SPI服务加载机制,只要mysql的jar包在类路径中,就可以注册mysql驱动。


那到底是在哪一步自动注册了mysql driver的呢?重点就在DriverManager.getConnection()中。我们都是知道调用类的静态方法会初始化该类静态代码块,so玄机就在这个DriverManager的静态代码块里。


当然里面玄机还有很多,但核心原理就是利用到了上下文加载器来实现加载,具体各位可以下面博文,它比我说得好~

Java上线文加载器加载JDBC驱动

image.png


URLClassLoader


位于java.net包。从JDK源码上来看其实是URLClassLoader继承了ClassLoader,也就是说URLClassLoader把ClassLoader扩展了一下,所以可以理解成URLClassLoader功能要多点。


ClassLoader只能加载classpath下面的类,而URLClassLoader可以加载**任意路径**下的类。他

们的继承关系如下:

public class URLClassLoader extends SecureClassLoader {}
public class SecureClassLoader extends ClassLoader {}


URLClassLoader提供了这个功能,它让我们可以通过以下几种方式进行加载:

* 文件: (从文件系统目录加载)

* jar包: (从Jar包进行加载)

* Http: (从远程的Http服务进行加载)


在Java7的Build 48版中,URLClassLoader提供了close()这个方法,可以将打开的资源全部释放掉,这个给开发者节省了大量的时间来精力来处理这方面的问题。

image.png


URLClassLoader 是AppClassLoader和ExtClassLoader的父类,它既可以从本地 文件系统获取二进制加载类,也可以从远程主机获取文件来加载类。


URLClassLoader 动态加载远程jar的代码实现:

借助URLClassLoader 来读取外部的jar包内的class文件,参考下面这个链接:

java中使用URLClassLoader访问外部jar包的java类


总结


以上是关于类加载器的一些介绍和工作原理。知道委托、可见性以及单一性原理,这些对于调试类加载器相关问题时至关重要。这些对于Java高级程序员和架构师来说都是必不可少的知识。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
4月前
|
存储 缓存 监控
什么是线程池?它的工作原理?
我是小假 期待与你的下一次相遇 ~
303 1
|
4月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
272 2
|
4月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
251 1
|
4月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
267 1
|
5月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
223 0
|
5月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
414 16
|
6月前
|
数据采集 消息中间件 并行计算
Python多线程与多进程性能对比:从原理到实战的深度解析
在Python编程中,多线程与多进程是提升并发性能的关键手段。本文通过实验数据、代码示例和通俗比喻,深入解析两者在不同任务类型下的性能表现,帮助开发者科学选择并发策略,优化程序效率。
516 1
|
6月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
6月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践