当前位置: 首页 > news >正文

自定义类加载器

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中自定义类加载器&#xff0c;并将双亲委派改为逆向双亲委派 自定义类加载器JarLoader&#xff1a; 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

在前几篇文章中&#xff0c;我们详细介绍了 Redis 的一些功能特性以及主流的 java 客户端 api 使用方法。 在当前流行的微服务以及分布式集群环境下&#xff0c;Redis 的使用场景可以说非常的广泛&#xff0c;能解决集群环境下系统中遇到的不少技术问题&#xff0c;在此列举几…...

Vlan和Trunk

文章目录 一、VLAN的定义与背景1. 传统以太网的问题&#xff08;广播域&#xff09;2. 用VLAN隔离广播域3. VLAN的优点与应用 二、VLAN的转发过程举例三、802.1Q标签&#xff1a;帧格式与作用四、VLAN工作原理交换机端口类型AccessTrunkHybrid PVID&#xff08;Port VLAN ID&am…...

java 批量下载将多个文件(minio中存储)压缩成一个zip包

我的需求是将minio中存储的文件按照查询条件查询出来统一压成一个zip包然后下载下来。 思路&#xff1a;针对这个需求&#xff0c;其实可以有多个思路&#xff0c;不过也大同小异&#xff0c;一般都是后端返回流文件前端再处理下载&#xff0c;也有少数是压缩成zip包之后直接给…...

nnUNet v2数据准备及格式转换 (二)

如果你曾经使用过nnUNet V1&#xff0c;那你一定明白数据集的命名是有严格要求的&#xff0c;必须按照特定的格式来进行命名才能正常使用。 这一节的学习需要有数据&#xff0c;如果你有自己的数据&#xff0c;可以拿自己的数据来实验&#xff0c;如果没有&#xff0c;可以用十…...

ant-vue1.78版监听a-modal遮罩层的滚动事件

监听a-modal遮罩层的滚动事件 我们开发过程中经常有遇到监听页面滚动的事件需求&#xff0c;去做一些下拉加载或者是下拉分页的需求&#xff0c;我们直接在vue的生命周期中去绑定事件监听非常的方便&#xff0c;但如果是弹框的遮罩层的滚动监听呢&#xff1f;页面的监听完全是…...

MATLAB中residue函数用法

目录 语法 说明 示例 求解具有实根的部分分式展开式 展开具有复数根和同次分子及分母的分式 展开分子次数高于分母次数的分式 residue函数的功能是部分分式展开&#xff08;部分分式分解&#xff09;。 语法 [r,p,k] residue(b,a) [b,a] residue(r,p,k) 说明 [r,p…...

攻防世界-Caesar

原题 解题思路 没出现什么特殊字符&#xff0c;可能是个移位密码。凯撒密码加密解密。偏移12位就行。...

嵌入式开发-lin总线介绍 一.概述

1.1lin总线定义和历史 LIN总线&#xff08;Local Interconnect Network&#xff09;是一种基于UART/SCI&#xff08;Universal Asynchronous Receiver-Transmitter/Serial Communication Interface&#xff09;的低成本串行通信协议。它主要用于汽车、家电、办公设备等多种领域…...

羊城杯-2023-Crypto

文章目录 Danger_RSA题目描述&#xff1a;题目分析&#xff1a; Easy_3L题目描述&#xff1a;题目分析&#xff1a; XOR贯穿始终题目描述&#xff1a;题目分析&#xff1a; MCeorpkpleer题目描述&#xff1a;题目分析&#xff1a; SigninCrypto题目描述&#xff1a;题目分析&am…...

RabbitMQ快速上手及讲解

前言&#xff1a;在介绍RabbitMQ之前&#xff0c;我们先来看下面一个场景&#xff1a; 1.1.1.1 异步处理 场景说明&#xff1a; 用户注册后&#xff0c;需要发注册邮件和注册短信&#xff0c;传统的做法有两种 1.串行的方式 (1)串行方式&#xff1a;将注册信息写入数据库后&a…...

使用多线程std::thread发挥多核计算优势(解答)

使用多线程std::thread发挥多核计算优势&#xff08;题目&#xff09; 单核无能为力 如果我们的电脑只有一个核&#xff0c;那么我们没有什么更好的办法可以让我们的程序更快。 因为这个作业限制了你修改算法函数。你唯一能做的就是利用你电脑的多核。 使用多线程 由于我们…...

MySQL分页查询详解:优化大数据集的LIMIT和OFFSET

最近在工作中&#xff0c;我们遇到了一个需求&#xff0c;甲方要求直接从数据库导出一个业务模块中所有使用中的工单信息。为了实现这一目标&#xff0c;我编写了一条SQL查询语句&#xff0c;并请求DBA协助导出数据。尽管工单数量并不多&#xff0c;只有3000多条&#xff0c;但…...

解构赋值、函数默认值

暂时性死区&#xff0c;TDZ&#xff08;Temporal Dead Zone&#xff09; var x 1 {let x x//此处声明了x&#xff0c;但是没有对x赋值&#xff0c;相当于在赋值之前引用x&#xff0c;所以会造成报错console.log(x)//报错x is not defined&#xff0c;暂时性死区&#xff0c;…...

【已解决】Mybatis 实现 Group By 动态分组查询

&#x1f389;工作中遇到这样一个需求场景&#xff1a;实现一个统计查询&#xff0c;要求可以根据用户在前端界面筛选的字段进行动态地分组统计。也就是说&#xff0c;后端在实现分组查询的时候&#xff0c;Group By 的字段是不确定的&#xff0c;可能是一个字段、多个字段或者…...

Android修改默认gradle路径

Android Studio每次新建项目&#xff0c;都会默认在C盘生成并下载gradle相关文件&#xff0c;由于C盘空间有限&#xff0c;没多久C盘就飘红了&#xff0c;于是就需要把gradle相关文件转移到其他盘 1、到C盘找到gradle文件 具体路径一般是&#xff1a;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的缩写&#xff0c;也就是网络附属存储。可以使用自己已经不怎么使用的笔记本搭建一台NAS服务器。 fdisk -l可以看一下各个磁盘的状态。 可以看到有sda、sdb、sdc和sdd等四块硬盘。 lvs、vgs和pvs结合起来看&#xff0c;sdb和sdc没有被使用。 …...

分享码云上8个宝藏又有价值的开源图片编辑器

如果你需要高效地处理图片&#xff0c;那么这8款实用工具是可以尝试的&#xff01; 它们能够进行一键抠图、放大、拼接、转矢量图、图标自动生成以及等操作&#xff0c;让你的工作效率飞升&#xff01; 在Gitee这个最有价值的开源项目计划是Gitee综合评定出的优秀开源项目的展示…...

TCP Header都有啥?

分析&回答 源端口号&#xff08;Source Port&#xff09; &#xff1a;16位&#xff0c;标识主机上发起传送的应用程序&#xff1b; 目的端口&#xff08;Destonation Port&#xff09; &#xff1a;16位&#xff0c;标识主机上传送要到达的应用程序。 源端&#xff0c;目…...

2026年,本地精准营销高性价比服务商来袭,你还不了解一下?

在本地商业竞争日益激烈的2026年&#xff0c;实体店面临着诸多挑战&#xff0c;引流难、成本高、复购率低等问题困扰着众多商家。而中粤&#xff08;广州&#xff09;信息科技有限公司作为本地精准营销的高性价比服务商&#xff0c;正以其独特的优势和卓越的服务&#xff0c;为…...

长期使用Taotoken聚合服务对项目月度账单的可预测性提升

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 长期使用Taotoken聚合服务对项目月度账单的可预测性提升 在AI驱动的项目开发与运营中&#xff0c;成本控制与预算规划是团队管理者…...

内存占用3KB!极致瘦身释放MCU无限可能

极致小体积&#xff0c;给工业领域带来了无限的可能&#xff1a;更低硬件成本&#xff0c;更小芯片体积&#xff0c;更低功耗&#xff0c;更高可靠性&#xff0c;让每一颗小MCU都拥有大系统的完整能力。 https://www.bilibili.com/video/BV1eZLi6PEjc/?spm_id_from333.1387.ho…...

股票买卖最佳时机:LeetCode121题解

题目LeetCode121给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取…...

DragonBones与Godot集成:骨骼动画的可编程化实践

1. 为什么在Godot里用DragonBones不是“锦上添花”&#xff0c;而是“绕不开的刚需” 去年上线一个横版动作手游Demo时&#xff0c;美术团队交来一套20个角色、每个角色含8套动画&#xff08;待机/跑动/跳跃/攻击/受击/死亡/闪避/必杀&#xff09;的Spine资源。我兴冲冲导入God…...

随机森林算法在儿童出行方式预测中的实战应用与优化

1. 项目概述&#xff1a;用随机森林预测孩子怎么上学做城市交通规划或者做家长接送方案的时候&#xff0c;你肯定想过一个问题&#xff1a;孩子们到底是怎么上学的&#xff1f;是走路、骑车、坐公交还是家长开车送&#xff1f;这个问题看似简单&#xff0c;背后却牵扯到城市规划…...

CTF出题人视角:从NewStarCTF 2023的WEB题,聊聊PHP特性与Flask Debug的那些‘坑’

CTF出题艺术&#xff1a;从PHP特性到Flask Debug的攻防博弈 当一道精心设计的CTF题目被成功破解时&#xff0c;出题人与解题者之间往往存在一场无声的思维交锋。作为NewStarCTF 2023 WEB方向的出题人&#xff0c;我想通过复盘"Begin of PHP"和"ErrorFlask"…...

如何用免费工具解锁QQ音乐、网易云音乐等加密格式:3分钟解决音乐播放限制

如何用免费工具解锁QQ音乐、网易云音乐等加密格式&#xff1a;3分钟解决音乐播放限制 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库&#xff1a; 1. https://github.com/unlock-music/unlock-music &#xff1b;2. https://git.unlock-music.dev/um/web…...

5分钟掌握m4s-converter:将B站缓存视频无损转换为MP4的终极指南

5分钟掌握m4s-converter&#xff1a;将B站缓存视频无损转换为MP4的终极指南 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾在B站缓存了…...

【Sora 2 HDR生成黄金公式】:曝光补偿系数×动态范围压缩阈值×时域一致性权重=可商用HDR帧率(附Python验证脚本)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Sora 2 HDR视频生成黄金公式的提出与商业意义 Sora 2 的HDR视频生成能力不再依赖传统多曝光融合或后期调色管线&#xff0c;而是通过一个端到端可微分的物理感知渲染公式实现原生高动态范围建模。该公式被业界…...