自定义类加载器
java中自定义类加载器,并将双亲委派改为逆向双亲委派
自定义类加载器JarLoader:
package cn.ac.iscas.dmo.common.tools.core.classloader;import org.apache.commons.collections4.MapUtils;import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;/*** 提供Jar隔离的加载机制,会把传入的路径、及其子路径、以及路径中的jar文件加入到class path。* 破坏双亲委派机制,改为逆向** @author admin*/
@SuppressWarnings({"rawtypes", "unused", "unchecked"})
public class JarLoader extends URLClassLoader {@SuppressWarnings("AlibabaThreadLocalShouldRemove")private static final ThreadLocal<URL[]> THREAD_LOCAL = new ThreadLocal<>();@SuppressWarnings("FieldMayBeFinal")private URL[] allUrl;@SuppressWarnings("FieldMayBeFinal")private boolean useCache;@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "unused"})private String[] paths;@SuppressWarnings("FieldMayBeFinal")private String pathStr;private String dbType;/**缓存当前类加载器加载的类*/@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")private final Map<String, Class<?>> jarLoaderClasses = new ConcurrentHashMap<>();/*** 缓存对应类型的加载的类* */public static Map<String, Map<String, Class>> typeJarLoaderClasses = new ConcurrentHashMap<>();/**缓存对象*/private static final Map<String, Map<String, byte[]>> CLASS_BYTES = new ConcurrentHashMap<>();/**ProtectionDomain 缓存*/private static final Map<String, ProtectionDomain> PROTECTION_DOMAIN_MAP = new ConcurrentHashMap<>();public JarLoader(String[] paths, boolean useCache, String type) {this(paths, JarLoader.class.getClassLoader(), useCache);this.dbType = type;}public JarLoader(String[] paths, ClassLoader parent, boolean useCache) {super(getUrls(paths), parent);//暂时先这样allUrl = THREAD_LOCAL.get();this.useCache = useCache;this.paths = paths;pathStr = String.join(";", paths);}public JarLoader(String[] paths) {this(paths, JarLoader.class.getClassLoader(), false);}public JarLoader(String[] paths, ClassLoader parent) {this (paths, parent, false);}/*** 清除某个路径下的缓存,* 可适用于不想重启服务,但更新了外部插件的jar包的情况下调用* */public static void clearCache(String[] paths) {String pathStr = String.join(";", paths);CLASS_BYTES.remove(pathStr);}/*** 加载class文件,方便加载的方法* */public static Class<?> outerLoadClass(String name) throws ClassNotFoundException {return Thread.currentThread().getContextClassLoader().loadClass(name);}private static URL[] getUrls(String[] paths) {if (null == paths || 0 == paths.length) {throw new RuntimeException("jar包路径不能为空.");}List<File> jarFiles = new ArrayList<>();List<String> dirFiles = new ArrayList<>();for (String path : paths) {File file = new File(path);if (file.isFile()) {jarFiles.add(file);} else {dirFiles.add(path);}}List<String> dirs = new ArrayList<>();for (String path : dirFiles) {dirs.add(path);JarLoader.collectDirs(path, dirs);}List<URL> urls = new ArrayList<>();for (String path : dirs) {urls.addAll(doGetUrls(path));}for (File jarFile : jarFiles) {try {URL url = jarFile.toURI().toURL();urls.add(url);} catch (Exception e) {throw new RuntimeException("系统加载jar包出错", e);}}URL[] urls1 = urls.toArray(new URL[0]);THREAD_LOCAL.set(urls1);return urls1;}private static void collectDirs(String path, List<String> collector) {if (null == path || "".equalsIgnoreCase(path)) {return;}File current = new File(path);if (!current.exists() || !current.isDirectory()) {return;}for (File child : Objects.requireNonNull(current.listFiles())) {if (!child.isDirectory()) {continue;}collector.add(child.getAbsolutePath());collectDirs(child.getAbsolutePath(), collector);}}private static List<URL> doGetUrls(final String path) {if (null == path || "".equalsIgnoreCase(path)) {throw new RuntimeException("jar包路径不能为空.");}File jarPath = new File(path);if (!jarPath.exists() || !jarPath.isDirectory()) {throw new RuntimeException("jar包路径必须存在且为目录.");}/* set filter */FileFilter jarFilter = pathname -> pathname.getName().endsWith(".jar");/* iterate all jar */File[] allJars = new File(path).listFiles(jarFilter);assert allJars != null;List<URL> jarUrls = new ArrayList<>(allJars.length);for (File allJar : allJars) {try {jarUrls.add(allJar.toURI().toURL());} catch (Exception e) {throw new RuntimeException("系统加载jar包出错", e);}}return jarUrls;}/**破坏双亲委派模型,采用逆向双亲委派*/@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {//读取缓存
// Class<?> aClass = jarLoaderClasses.get(name);Class<?> aClass = null;if (typeJarLoaderClasses.containsKey(dbType)) {Map<String, Class> stringClassMap = typeJarLoaderClasses.getOrDefault(dbType, MapUtils.EMPTY_SORTED_MAP);aClass = stringClassMap.get(name);}if (aClass == null) {aClass = findClass(name);}if (aClass == null) {return super.loadClass(name);} else {// 放入缓存jarLoaderClasses.put(name, aClass);// 放入带数据库类型的缓存typeJarLoaderClasses.computeIfAbsent(dbType, key -> new ConcurrentHashMap<>(32)).put(name, aClass);}return aClass;}@Overridepublic Class<?> findClass(String name) {//如果开启了缓存,查看class文件对应字节数组有没有缓存起来,如果有缓存,直接使用缓存的字节数组if (useCache) {synchronized (name.intern()) {Map<String, byte[]> cacheMap = CLASS_BYTES.get(pathStr);if (MapUtils.isNotEmpty(cacheMap)) {byte[] bytes = cacheMap.get(name);if (bytes != null) {Class<?> aClassx = this.defineClass(name, bytes, 0, bytes.length, PROTECTION_DOMAIN_MAP.get(name));if (aClassx != null) {System.out.println("读取到缓存.....");return aClassx;}}}}}Class<?> aClass = null;if (allUrl != null) {String classPath = name.replace(".", "/");classPath = classPath.concat(".class");for (URL url : allUrl) {byte[] data;ByteArrayOutputStream baos = new ByteArrayOutputStream();InputStream is = null;try {File file = new File(url.toURI());if (file.exists()) {@SuppressWarnings("resource") JarFile jarFile = new JarFile(file);JarEntry jarEntry = jarFile.getJarEntry(classPath);if (jarEntry != null) {is = jarFile.getInputStream(jarEntry);int c;byte[] buff = new byte[4096];while (-1 != (c = is.read(buff))) {baos.write(buff, 0, c);}data = baos.toByteArray();CodeSource codeSource = new CodeSource(url, (Certificate[]) null); // 获取类的代码来源ProtectionDomain protectionDomain = new ProtectionDomain(codeSource, null, this, null);aClass = this.defineClass(name, data, 0, data.length, protectionDomain);synchronized (name.intern()) {if (useCache && aClass != null) {System.out.println("写入缓存---");Map<String, byte[]> classByteMap = CLASS_BYTES.get(pathStr);if (MapUtils.isEmpty(classByteMap)) {classByteMap = new ConcurrentHashMap<>(2);CLASS_BYTES.put(pathStr, classByteMap);}CLASS_BYTES.get(pathStr).put(name, data);PROTECTION_DOMAIN_MAP.put(name, protectionDomain);}}}}} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}baos.close();} catch (IOException e) {e.printStackTrace();}}}}return aClass;}}
类加载器切换工具类:
package cn.ac.iscas.dmo.common.tools.core.classloader;/**** 为避免jar冲突,比如hbase可能有多个版本的读写依赖jar包* 就需要脱离当前classLoader去加载这些jar包,执行完成后,又退回到原来classLoader上继续执行接下来的代码* @author admin*/
public final class ClassLoaderSwapper {private ClassLoader storeClassLoader = null;private ClassLoaderSwapper() {}public static ClassLoaderSwapper newCurrentThreadClassLoaderSwapper() {return new ClassLoaderSwapper();}/*** 保存当前classLoader,并将当前线程的classLoader设置为所给classLoader** @param classLoader 类加载器* @return ClassLoader*/@SuppressWarnings("UnusedReturnValue")public ClassLoader setCurrentThreadClassLoader(ClassLoader classLoader) {this.storeClassLoader = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(classLoader);return this.storeClassLoader;}/*** 将当前线程的类加载器设置为保存的类加载* @return ClassLoader*/@SuppressWarnings("UnusedReturnValue")public ClassLoader restoreCurrentThreadClassLoader() {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(this.storeClassLoader);return classLoader;}
}相关文章:
自定义类加载器
java中自定义类加载器,并将双亲委派改为逆向双亲委派 自定义类加载器JarLoader: package cn.ac.iscas.dmo.common.tools.core.classloader;import org.apache.commons.collections4.MapUtils;import java.io.*; import java.net.URL; import java.net.U…...
【Redis】Redis 的学习教程(七)之 SpringBoot 集成 Redis
在前几篇文章中,我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。 在当前流行的微服务以及分布式集群环境下,Redis 的使用场景可以说非常的广泛,能解决集群环境下系统中遇到的不少技术问题,在此列举几…...
Vlan和Trunk
文章目录 一、VLAN的定义与背景1. 传统以太网的问题(广播域)2. 用VLAN隔离广播域3. VLAN的优点与应用 二、VLAN的转发过程举例三、802.1Q标签:帧格式与作用四、VLAN工作原理交换机端口类型AccessTrunkHybrid PVID(Port VLAN ID&am…...
java 批量下载将多个文件(minio中存储)压缩成一个zip包
我的需求是将minio中存储的文件按照查询条件查询出来统一压成一个zip包然后下载下来。 思路:针对这个需求,其实可以有多个思路,不过也大同小异,一般都是后端返回流文件前端再处理下载,也有少数是压缩成zip包之后直接给…...
nnUNet v2数据准备及格式转换 (二)
如果你曾经使用过nnUNet V1,那你一定明白数据集的命名是有严格要求的,必须按照特定的格式来进行命名才能正常使用。 这一节的学习需要有数据,如果你有自己的数据,可以拿自己的数据来实验,如果没有,可以用十…...
ant-vue1.78版监听a-modal遮罩层的滚动事件
监听a-modal遮罩层的滚动事件 我们开发过程中经常有遇到监听页面滚动的事件需求,去做一些下拉加载或者是下拉分页的需求,我们直接在vue的生命周期中去绑定事件监听非常的方便,但如果是弹框的遮罩层的滚动监听呢?页面的监听完全是…...
MATLAB中residue函数用法
目录 语法 说明 示例 求解具有实根的部分分式展开式 展开具有复数根和同次分子及分母的分式 展开分子次数高于分母次数的分式 residue函数的功能是部分分式展开(部分分式分解)。 语法 [r,p,k] residue(b,a) [b,a] residue(r,p,k) 说明 [r,p…...
攻防世界-Caesar
原题 解题思路 没出现什么特殊字符,可能是个移位密码。凯撒密码加密解密。偏移12位就行。...
嵌入式开发-lin总线介绍 一.概述
1.1lin总线定义和历史 LIN总线(Local Interconnect Network)是一种基于UART/SCI(Universal Asynchronous Receiver-Transmitter/Serial Communication Interface)的低成本串行通信协议。它主要用于汽车、家电、办公设备等多种领域…...
羊城杯-2023-Crypto
文章目录 Danger_RSA题目描述:题目分析: Easy_3L题目描述:题目分析: XOR贯穿始终题目描述:题目分析: MCeorpkpleer题目描述:题目分析: SigninCrypto题目描述:题目分析&am…...
RabbitMQ快速上手及讲解
前言:在介绍RabbitMQ之前,我们先来看下面一个场景: 1.1.1.1 异步处理 场景说明: 用户注册后,需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 (1)串行方式:将注册信息写入数据库后&a…...
使用多线程std::thread发挥多核计算优势(解答)
使用多线程std::thread发挥多核计算优势(题目) 单核无能为力 如果我们的电脑只有一个核,那么我们没有什么更好的办法可以让我们的程序更快。 因为这个作业限制了你修改算法函数。你唯一能做的就是利用你电脑的多核。 使用多线程 由于我们…...
MySQL分页查询详解:优化大数据集的LIMIT和OFFSET
最近在工作中,我们遇到了一个需求,甲方要求直接从数据库导出一个业务模块中所有使用中的工单信息。为了实现这一目标,我编写了一条SQL查询语句,并请求DBA协助导出数据。尽管工单数量并不多,只有3000多条,但…...
解构赋值、函数默认值
暂时性死区,TDZ(Temporal Dead Zone) var x 1 {let x x//此处声明了x,但是没有对x赋值,相当于在赋值之前引用x,所以会造成报错console.log(x)//报错x is not defined,暂时性死区,…...
【已解决】Mybatis 实现 Group By 动态分组查询
🎉工作中遇到这样一个需求场景:实现一个统计查询,要求可以根据用户在前端界面筛选的字段进行动态地分组统计。也就是说,后端在实现分组查询的时候,Group By 的字段是不确定的,可能是一个字段、多个字段或者…...
Android修改默认gradle路径
Android Studio每次新建项目,都会默认在C盘生成并下载gradle相关文件,由于C盘空间有限,没多久C盘就飘红了,于是就需要把gradle相关文件转移到其他盘 1、到C盘找到gradle文件 具体路径一般是:C:\Users\用户\ .gradle …...
原生JS+canvas实现炫酷背景
原生JScanvas实现炫酷背景 可以在需要的背景页使用 <!doctype html> <html> <head> <meta charset"utf-8"> <title>HTML5 Canvas矩阵粒子波浪背景动画特效</title> <style> html,body { height:100%; } body { …...
Linux学习之NAS服务器搭建
NAS是Network Attached Storage的缩写,也就是网络附属存储。可以使用自己已经不怎么使用的笔记本搭建一台NAS服务器。 fdisk -l可以看一下各个磁盘的状态。 可以看到有sda、sdb、sdc和sdd等四块硬盘。 lvs、vgs和pvs结合起来看,sdb和sdc没有被使用。 …...
分享码云上8个宝藏又有价值的开源图片编辑器
如果你需要高效地处理图片,那么这8款实用工具是可以尝试的! 它们能够进行一键抠图、放大、拼接、转矢量图、图标自动生成以及等操作,让你的工作效率飞升! 在Gitee这个最有价值的开源项目计划是Gitee综合评定出的优秀开源项目的展示…...
TCP Header都有啥?
分析&回答 源端口号(Source Port) :16位,标识主机上发起传送的应用程序; 目的端口(Destonation Port) :16位,标识主机上传送要到达的应用程序。 源端,目…...
短视频创作新利器:Sonic数字人工作流生成口型自然的表情包视频
短视频创作新利器:Sonic数字人工作流生成口型自然的表情包视频 1. 数字人视频创作新趋势 在短视频内容爆炸式增长的今天,创作者们面临着一个共同挑战:如何高效产出高质量视频内容。传统视频制作需要专业设备、复杂后期和大量时间投入&#…...
高效开源输入法词库转换实战指南:30+格式无缝互转技巧
高效开源输入法词库转换实战指南:30格式无缝互转技巧 【免费下载链接】imewlconverter ”深蓝词库转换“ 一款开源免费的输入法词库转换程序 项目地址: https://gitcode.com/gh_mirrors/im/imewlconverter 深蓝词库转换是一款功能强大的开源输入法词库转换工…...
千问3.5-2B镜像免配置优势解析:supervisor自恢复+健康检查+7860端口标准化
千问3.5-2B镜像免配置优势解析:supervisor自恢复健康检查7860端口标准化 1. 千问3.5-2B镜像核心价值 千问3.5-2B是Qwen系列的小型视觉语言模型,专为图片理解与文本生成任务优化设计。这个开箱即用的镜像解决了传统AI模型部署中最让人头疼的三个问题&am…...
05. 微交互设计模式解析:让界面更有生命力
05. 微交互设计模式解析:让界面更有生命力 引言 微交互是用户与界面之间的小互动,它们虽然微小,却能给用户带来巨大的愉悦感。作为一名把代码当散文写的 UI 匠人,我始终认为:好的微交互不是简单的动画效果,…...
Sqitch 实战教程:如何在 PostgreSQL 中管理数据库变更
Sqitch 实战教程:如何在 PostgreSQL 中管理数据库变更 【免费下载链接】sqitch Sensible database change management 项目地址: https://gitcode.com/gh_mirrors/sq/sqitch Sqitch 是一款功能强大的数据库变更管理工具,专为 PostgreSQL 等数据库…...
UE5伤害系统避坑指南:Damage Type没用好?你的Apply Damage可能白写了
UE5伤害系统深度解析:如何用Damage Type构建高扩展性战斗机制 在虚幻引擎5的游戏开发中,伤害系统是战斗机制的核心支柱。许多开发者习惯性地将注意力集中在Damage Amount这个数值上,却忽视了Damage Type这个能够赋予游戏深度和多样性的强大工…...
数据库工具集成与自动化:awesome-db-tools 中的工作流优化终极指南
数据库工具集成与自动化:awesome-db-tools 中的工作流优化终极指南 【免费下载链接】awesome-db-tools Everything that makes working with databases easier 项目地址: https://gitcode.com/gh_mirrors/aw/awesome-db-tools awesome-db-tools 是一个社区驱…...
2026年,市面上正规SSL证书品牌众多,哪家才是真正专业之选?
在当今数字化时代,网络安全至关重要,SSL证书作为保障网站安全的关键工具,其重要性不言而喻。2026年,市面上正规的SSL证书品牌众多,企业在选择时往往会感到困惑。本文将为大家分析如何选择专业的SSL证书品牌,…...
从FPGA到ASIC:实战中如何为你的IP核选择合适的Wishbone互联拓扑?
从FPGA到ASIC:实战中如何为你的IP核选择合适的Wishbone互联拓扑? 在复杂SoC设计中,总线架构的选择往往决定了系统性能的上限。Wishbone作为轻量级片上总线协议,其灵活的互联拓扑为工程师提供了四种截然不同的设计范式:…...
知识蒸馏(Knowledge Distillation)完全指南:原理、实践与进阶
一句话概括:知识蒸馏是一种模型压缩技术,它让一个轻量级的“学生模型”模仿一个高性能的“教师模型”的输出行为,从而在保持小体积、低延迟的同时,获得接近大模型的能力。一、为什么需要知识蒸馏?—— 大模型的“奢侈”…...
