当前位置: 首页 > 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;目…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...