自定义类加载器
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位,标识主机上传送要到达的应用程序。 源端,目…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
