书接上回

在 Java自定义ClassLoader实现插件类隔离加载文章中,我们通过

自定义ClassLoader + 插件独立打包引入的方式,实现了同依赖不同版本的隔离加载

这次咱们来分析下具体实现原理

打破双亲委派机制

首先,双亲委派机制不会自己去尝试加载类,而是把请求委托给父加载器去完成,依次向上

其次,思考一个问题

以前使用Tomcat部署项目时,webapp目录下可能部署多个项目的war包

这些war包中可能包含同一个类,类全限定名一样,但是实现不一样

那么Tomcat如何保证他们不会冲突呢?

Tomcat正是使用了打破双亲委派机制,给每一个应用创建独立的类加载器实例WebAppClassLoader,并重写loadClass核心方法,使得优先加载当前应用目录下的类

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded(首先,检查类是否已经加载)

Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 委托父加载器加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
} if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 查找Class资源
c = findClass(name); // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

先分析一波loadClass的源码

第一行用synchronized关键字做了对象锁处理,这里说点额外话题,为啥对象锁用的getClassLoadingLock(name),而不是直接用name作为对象锁,推荐一篇博文讲的非常详细:https://www.cnblogs.com/thisiswhy/p/15892044.html

进入锁以后,通过findLoadedClass方法,先找已经加载过的Class

已经加载过的Class中找不到的话,再通过parent.loadClass(name, false)委托父加载器加载

这段逻辑就是双亲委派的处理

类加载规则

如果一个类由类加载器A加载,那么这个类的依赖类也是由「相同的类加载器」加载。

protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
// AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许
// doPrivileged方法用来终端没有权限不被允许的操作
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
// 获取Class路径
String path = name.replace('.', '/').concat(".class");
// 加载Class资源
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
// 加载Class资源,定义Class类
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}

总结

我们通过自定义ClassLoader,清空了父加载器,使得打破了双亲委派机制,不会通过缓存加载到AppClassLoader中已加载过的类

加载的Class类中,其它的依赖类,也是经过我们自定义的ClassLoader进行的加载

因此,我们加载不同的插件包时,可以实现类的隔离加载

Java自定义ClassLoader实现插件类隔离加载 - 原理篇的更多相关文章

  1. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  2. java 27 - 1 反射之 类的加载器

    说到反射,首先说类的加载器. 类的加载: 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载: 就是指将class文件读入内存,并为之 ...

  3. Java温故而知新(10)类的加载机制

    类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指导开发者采取更有效的措施配合程序执行. 研究类加载机制的第二个目的是让程序能动态的控制类加载,比如热部署等,提高程序的灵活性 ...

  4. 透过现象看本质:Java类动态加载和热替换

    摘要:本文主要介绍类加载器.自定义类加载器及类的加载和卸载等内容,并举例介绍了Java类的热替换. 最近,遇到了两个和Java类的加载和卸载相关的问题: 1) 是一道关于Java的判断题:一个类被首次 ...

  5. java类的加载机制

    什么是类装载器ClassLoader ClassLoader是一个抽象类 ClassLoader的实例将读入Java字节码将类装载到JVM中 ClassLoader可以定制,满足不同的字节码流获取方式 ...

  6. java类的加载、链接、初始化

    JVM和类的关系 当我们调用JAVA命令运行某个java程序时,该命令将会启动一条java虚拟机进程,不管该java程序有多么复杂,该程序启动了多少个线程,它们都处于该java虚拟机进程里.正如前面介 ...

  7. java 类的加载、连接和初始化

    JVM和类 调用Java命令运行Java程序时,该命令将会启动一条Java虚拟机进程,不管该Java程序启动了多少条线程,创建了多少个变量,它们都处于该Java虚拟机进程里,共享该JVM进程的内存区. ...

  8. Java类的加载的一个小问题

    前言 之前写了一篇文章专门介绍了一下类的加载和对象的创建流程,然后收到了一个博友的疑问,觉得蛮好的,在这里和大家分享下. 博文地址:[Java基础]Java类的加载和对象创建流程的分析 疑问 类在加载 ...

  9. <JVM中篇:字节码与类的加载篇>04-再谈类的加载器

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  10. 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】

    目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...

随机推荐

  1. Android Studio Giraffe安装与gradle配置

    本机环境:win10专业版,64位,16G内存. 原先用的AS2.2,是很早之前在看<第一行代码Android(第2版)>的时候,按书里的链接下载安装的,也不用怎么配置.(PS:第一行代码 ...

  2. 解决git出现fatal: detected dubious ownership in repository at XXXXX的错误

    在window环境下,使用git命令时报错fatal: detected dubious ownership in repository at XXXXXX,图片如下 解决方法如下 添加一行代码 gi ...

  3. python flask 简单应用开发

    转载请注明出处: Flask 是一个基于 Python 的微型 Web 框架,它提供了一组简洁而强大的工具和库,用于构建 Web 应用程序.Flask 的主要作用是帮助开发者快速搭建轻量级的.灵活的 ...

  4. 【AI绘画模型汇总】分享5个国内实用的AI绘画模型网站-C站AI模型平替网站

    鉴于大家未必会有魔法工具访问civitai(C站)下载AI模型,这里我搜集整理了5个实用的国内版AI模型素材库,无障碍访问下载Stable diffusion模型. 1.LiblibAI 访问速度快, ...

  5. 为什么要使用API接口,他能带来哪些便利

    API接口是程序员进行应用程序开发时不可或缺的工具之一.以下是使用API接口的一些优点: 数据交换:使用API接口可以使不同的应用程序.网站或服务之间交换数据更为便捷,减少人工输入数据的时间和风险. ...

  6. LeetCode--1039

    Smiling & Weeping ----我总是躲在梦与季节的身处, 听花与黑夜唱尽梦魇, 唱尽繁华,唱断所有记忆的来路. 题目链接:1039. 多边形三角剖分的最低得分 - 力扣(Leet ...

  7. 【译】在 Visual Studio 2022 中安全地在 HTTP 请求中使用机密

    在 Visual Studio 2022 的17.8 Preview 1版本中,我们更新了 HTTP 文件编辑器,使您能够外部化变量,从而使跨不同环境的 Web API 测试更容易.此更新还包括以安全 ...

  8. MongoDB 中使用 explain 分析创建的索引是否合理

    MongoDB 中如何使用 explain 分析查询计划 前言 查询计划 explain explain 1.queryPlanner 2.executionStats 3.allPlansExecu ...

  9. 一文带你实现云上部署轻量化定制表单Docker

    本文分享自华为云社区 <[华为云云耀云服务器L实例评测|云原生]自定制轻量化表单Docker快速部署云耀云服务器 | 玩转华为云>,作者:计算机魔术师. 华为云的云耀云服务器L实例备受推崇 ...

  10. 解密网络通信的关键技术(下):DNS、ARP、DHCP和NAT,你了解多少?

    引言 在上一章中,我们详细介绍了域名系统(DNS)和地址解析协议(ARP)的工作原理,从而对域名解析和介质访问控制(MAC)地址寻址有了更深入的了解.在今天的章节中,我们将继续探讨动态主机配置协议(D ...