范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Java类隔离应用多Jar包支持

  案例需求
  现在有一个"统一管理平台",用于统一对接三方平台,屏蔽相同业务三方平台的差异性,减少内部平台对接的成本。正常情况下三方平台提供的 SDK 是通用的(和内部平台无关),但是有一些比较特殊的三方(假如是三方平台 A),他提供的 SDK 是给内部平台定制的。
  这时就需要根据访问"统一管理平台"的内部平台类型,动态的选择使用哪个三方平台 A 的 Jar 包,比如内部平台 A 访问三方平台 A,就需要调用为 A 定制的 Jar 包。
  这个需求需要解决如下两个问题:如何在同一套环境中同时存在多个同平台不同版本的 Jar 包(这些 Jar 包中的类大部分相同,只有预设的配置参数不同)?如何根据内部平台类型,选择需要调用的 Jar 包?类加载
  我们知道如果想要使用一个类,那么这个类必须通过类加载器将其加载到内存中,在未自定义类加载器之前,JVM 是通过 ApplicationClassLoader、ExtensionClassLoader、BootstrapClassLoader 这三个类加载器基于双亲委派机制完成类的加载。这三个类加载器具有各自加载类的范围如下图所示:
  类隔离机制
  要想解决上面的第一个问题(多个同平台不同版本 Jar 包同时存在),就必须先了解一下类隔离机制。
  类隔离机制原理其实很简单,就是让每个三方平台 A 定制的 Jar 使用单独的类加载器来加载,这样每个 Jar 包之间相互隔离不会相互影响。这是因为即使同一个类使用不同的类加载器加载,对于 JVM 也是两个不同的类(虽然类的结构相同),在 JVM 中类的唯一标识是:类加载器 + 类名。
  要保证不同 Jar 包内的类隔离,还需要做到一点,就是 Jar 包中的某个类使用某个类加载器加载,那么其引用的类均使用该类加载器加载,这就是类加载传导规则 。
  代码实现
  使用 IDEA 创建三个 Maven 项目:third-party-A-for-A:三方平台 A 为内部平台 A 定制的 Jarthird-party-A-for-B:三方平台 A 为内部平台 B 定制的 Jarunified-management-platform:统一管理平台,用于通过访问的内部平台类型,动态选择调用三方平台 A 的 Jar
  项目:third-party-A-for-A
  pom.xml<?xml version="1.0" encoding="UTF-8"?>      4.0.0      com.thirdparty.A     third-party-A-for-A     1.0-SNAPSHOT     三方平台 A 为内部平台 A 定制 Jar 包               17         17                                 cn.hutool             hutool-all             5.8.5               
  定义两个类:TPAAccessService:用于提供给调用方的统一调用入口类SendRequestProvider:Jar 内部使用的类,用于提供向三方平台 A 发送请求的类,另外一个作用是验证"类加载传导规则"
  TPAAccessService.java/**  * TPA(Third Party A:三方平台 A 简称)  * 该类为调用方提供统一的方法调用入口,调用三方 A 只需要使用该类即可  *  * @since 2023/1/14 9:45  */ public class TPAAccessService {      public static void send() {         SendRequestProvider.send();     } }
  SendRequestProvider.javaimport cn.hutool.core.lang.Console;  /**  * 该类提供向三方平台 A 发送请求的方法  *  * @since 2023/1/14 9:48  */ class SendRequestProvider {      /**      * 三方平台 A 为内部平台 A 预设的密钥,用于加解密      */     private static final String SECRET_KEY = "AAAAAAAAAAA";       /**      * 发送请求到三方平台 A      */     public static void send() {         Console.log("[A -> TPA] 密钥:{}   ClassLoader:{}", SECRET_KEY, SendRequestProvider.class.getClassLoader());     } }
  项目:third-party-A-for-B
  与 third-party-A-for-A  基本相同,除了 SendRequestProvider.java  中的密钥不同,如下所示:class SendRequestProvider {      /**      * 三方平台 A 为内部平台 B 预设的密钥,用于加解密      */     private static final String SECRET_KEY = "BBBBBBBBBBB";       /**      * 发送请求到三方平台 A      */     public static void send() {         Console.log("[B -> TPA] 密钥:{}   ClassLoader:{}", SECRET_KEY, SendRequestProvider.class.getClassLoader());     } }
  项目:
  unified-management-platform
  pom.xml<?xml version="1.0" encoding="UTF-8"?>      4.0.0      com.ump     unified-management-platform     1.0-SNAPSHOT     统一管理平台               17         17                                 org.projectlombok             lombok             1.18.24                               cn.hutool             hutool-all             5.8.5               
  定义两个类:TPAClassLoader:自定义类加载器,用于加载为内部平台定制的相应 Jar 中类的类Main:测试内部平台调用效果
  TPAClassLoader.javaimport cn.hutool.core.lang.Console; import lombok.SneakyThrows;  import java.io.File; import java.io.FileNotFoundException; import java.net.URL; import java.net.URLClassLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;  /**  * 为加载三方平台 A 提供的 Jar 自定义的类加载器  *  * @since 2023/1/14 10:02  */ public class TPAClassLoader extends URLClassLoader {      /**      * 用于缓存相应平台的类加载器,防止重复创建和加载类,造成内存泄漏      */     private static final ConcurrentMap CLASS_LOADER_CACHE = new ConcurrentHashMap<>();       private TPAClassLoader(URL[] urls, ClassLoader parent) {         super(urls, parent);     }      /**      * 用于获取相应三方平台 Jar 包中的类,如果已经加载直接返回,未加载通过 TAPClassLoader 加载类,完成后返回      *      * @param internalPlatformCode 内部平台编码,例如:内部平台 A 的编码就是 A      * @param tapJarPath           为相应内部平台定制的三方平台 Jar 路径      * @param className            待获取类的全限定类名      * @return 类的 Class 对象      */     @SneakyThrows     public static Class<?> getClass(String internalPlatformCode, String tapJarPath, String className) {         TPAClassLoader classLoader = getInstance(internalPlatformCode, tapJarPath);         Console.log("获取内部平台 {} 的类:{}", internalPlatformCode, className);         return classLoader.loadClass(className);     }      /**      * 用于获取对应内部平台的类加载器,类加载器相对于内部平台是单例的,保证单例使用单例设计模式 DCL 的方式      *      * @param internalPlatformCode 内部平台编码,例如:内部平台 A 的编码就是 A      * @param tapJarPath           为相应内部平台定制的三方平台 Jar 路径      * @return 内部平台对应的类加载器      */     private static TPAClassLoader getInstance(String internalPlatformCode, String tapJarPath) throws Exception {         final String key = buildKey(internalPlatformCode, tapJarPath);         TPAClassLoader classLoader = CLASS_LOADER_CACHE.get(key);         if (classLoader != null) {             return classLoader;         }         synchronized (TPAClassLoader.class) {             classLoader = CLASS_LOADER_CACHE.get(key);             if (classLoader != null) {                 return classLoader;             }              File jarFile = new File(tapJarPath);             if (!jarFile.exists()) {                 throw new FileNotFoundException("未找到三方平台 A Jar 包文件:" + tapJarPath);             }             classLoader = new TPAClassLoader(new URL[]{jarFile.toURI().toURL()}, getSystemClassLoader());             Console.log("为内部平台 {} 创建类加载器:{}", internalPlatformCode, classLoader);             CLASS_LOADER_CACHE.put(key, classLoader);             return classLoader;         }     }      /**      * 用于生成缓存对应内部平台类加载器的 Key      *      * @param internalPlatformCode 内部平台编码,例如:内部平台 A 的编码就是 A      * @param tapJarPath           为相应内部平台定制的三方平台 Jar 路径      * @return 缓存 Key      */     private static String buildKey(String internalPlatformCode, String tapJarPath) {         return internalPlatformCode.concat("::").concat(tapJarPath);     } }
  Main.javaimport cn.hutool.core.lang.Console; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.ReflectUtil; import lombok.SneakyThrows;  import java.util.HashMap; import java.util.Map;  /**  * Main  *  * @author ZhaoHaichun  * @since 2023/1/14 10:34  */ public class Main {      /**      * 该 Map 只是测试使用,用于临时保持三方平台 A 提供的 Jar 包路径,实际开发会通过文件上传到服务器,然后获取上传路径,通过路径加载      */     private static final Map TPA_JAR_PATH_MAP = new HashMap<>();      private static final String TAP_ACCESS_SERVICE_NAME = "com.thirdparty.TPAAccessService";       static {         TPA_JAR_PATH_MAP.put("A", "C:UserszhaohDesktopTemptap_jarthird-party-A-for-A-1.0-SNAPSHOT.jar");         TPA_JAR_PATH_MAP.put("B", "C:UserszhaohDesktopTemptap_jarthird-party-A-for-B-1.0-SNAPSHOT.jar");     }       @SneakyThrows     public static void main(String[] args) {         for (int i = 0; i < 5; i++) {             // 用于随机生成待访问的内部平台             String internalPlatformCode = String.valueOf((char) RandomUtil.randomInt("A", "B" + 1));             // 通过访问的内部平台查询三方平台 A 为其提供的 Jar 路径             String jarPath = TPA_JAR_PATH_MAP.get(internalPlatformCode);             // 通过上述信息,使用相应的类加载器加载或直接获取类 "com.thirdparty.TPAAccessService"             Class<?> clazz = TPAClassLoader.getClass(internalPlatformCode, jarPath, TAP_ACCESS_SERVICE_NAME);             // 调用其相应的方法             ReflectUtil.invokeStatic(clazz.getMethod("send"));             Console.log("================================================================");         }     } }测试步骤
  编写完成上述代码后,按照下面步骤执行:使用 Maven package 打包项目:third-party-A-for-A、third-party-A-for-B将打包完成的 Jar 拷贝到测试目录,上面实例代码为:"C:UserszhaohDesktopTemptap_jar"目录下修改 Main 类静态代码块中的路径与 Jar 包路径一致执行 Main 类中的 main 方法
  输出结果如下:(每次输出可能不同)为内部平台 A 创建类加载器:com.ump.TPAClassLoader@568db2f2 获取内部平台 A 的类:com.thirdparty.TPAAccessService [A -> TPA] 密钥:AAAAAAAAAAA   ClassLoader:com.ump.TPAClassLoader@568db2f2 ================================================================ 获取内部平台 A 的类:com.thirdparty.TPAAccessService [A -> TPA] 密钥:AAAAAAAAAAA   ClassLoader:com.ump.TPAClassLoader@568db2f2 ================================================================ 为内部平台 B 创建类加载器:com.ump.TPAClassLoader@179d3b25 获取内部平台 B 的类:com.thirdparty.TPAAccessService [B -> TPA] 密钥:BBBBBBBBBBB   ClassLoader:com.ump.TPAClassLoader@179d3b25 ================================================================ 获取内部平台 B 的类:com.thirdparty.TPAAccessService [B -> TPA] 密钥:BBBBBBBBBBB   ClassLoader:com.ump.TPAClassLoader@179d3b25 ================================================================ 获取内部平台 A 的类:com.thirdparty.TPAAccessService [A -> TPA] 密钥:AAAAAAAAAAA   ClassLoader:com.ump.TPAClassLoader@568db2f2 ================================================================
  通过上面的输出结果可以看出:内部平台 A 和 B 分别只创建了一次类加载器创建完成类加载器后,后续均通过缓存中获取相应的类加载器在 Jar 包中 TPAAccessService 调用了 SendRequestProvider,而 SendRequestProvider 输出的日志中类加载器同加载 TPAAccessService 的类加载器相同,说明类加载传导规则内部平台 A 调用,输出的密钥是"AAAAAAAAAAA",B 调用输出的密钥是"BBBBBBBBBBB",说明为内部平台提供的 Jar 均加载到内存,而且通过类加载器实现了类的隔离

90后广东小伙娶美国媳妇,生一娃,父女颜值相差大,被质疑是保姆阅读前先关注,以便您讨论和分享,感谢您的参与和支持这是写故事的石头讲述的第155位人物!2021年,一个94年的广东河源的农村小伙娶了美国媳妇,美国媳妇还比他小2岁,毕业于美国南加煤系针状焦出口量增加,其主要贸易伙伴为美国一油系针状焦进口统计据海关统计,2022年12月我国油系针状焦进口量为6888。19吨,环比上升33。04。进口金额为1082。66万美元,进口均价为1571。77美元吨,环比下降泰晤士评恩佐技术全面缺乏经验,但假以时日能成为耀眼的那颗星直播吧2月2日讯今天凌晨,切尔西官方宣布从本菲卡签下阿根廷中场恩佐费尔南德斯,双方签约至2031年,据本菲卡方面公布的转会费为1。21亿欧元,为英超历史最高转会。今日英媒泰晤士报记神奇的AI换脸技术是如何实现的?神奇的AI换脸技术是如何实现的?什么是AI换脸技术?所谓AI换脸就是通过人脸特征自动提取编码等技术,将视频中的角色进行改头换面。目前人脸识别已经先进到民用级别,并不高深,支付宝支付关注商务部拟将激光雷达等技术列入禁止限制出口目录文懂车帝原创李德喆懂车帝原创行业近日,商务部官网关于中国禁止出口限制出口技术目录修订公开征求意见的通知。通知指出,为加强技术进出口管理,根据对外贸易法和技术进出口管理条例相关规定,聚焦机床健康两大主业发力通用技术集团经营指标飘红2022年,中国通用技术(集团)控股有限责任公司不断加大在机床装备和医疗健康两大领域的布局力度,优化增量投向,调整存量结构,着力实现高质量发展。企业去年全年实现营收1830亿元,同中英技术如何让人更像人?学习提示本文选自大西洋报,内容归大西洋报所有外刊翻译理解部分归本人所有。重点词汇和句式本人已经加粗,需要解释的部分也在一段结束后特别注明。本文非常适合拿来做阅读理解。比如把标题去掉TVB知名老戏骨不言退休!卖房为患癌女友治病,恋爱多年不结婚本文编辑剧透社issac未经授权严禁转载,发现抄袭者将进行全网投诉由视帝陈豪龚嘉欣周嘉洛以及江嘉敏等人主演的TVB悬疑剧集新四十二章,目前正在热播中。该剧题材比较新颖,讲述各位主角腌腊肉时,别只知道放盐,牢记5点,腊肉不发霉不变臭,越放越香广东有句俗话,叫做秋风起,吃腊味。广东的冬天姗姗来迟,北风起,冷晴的天气最适合做腊味了,每到这个时候,我都会买十条八条五花肉做一些腊肉,就为了吃得健康,无添加,吃得放心。腊肉是中国这就是信心人气聚起来烟火气旺起来刚刚过去不久的2023年春节,让人们体验了一把久违的人山人海,一幕幕喧闹场景的背后无不透露着一个信号烟火气回来了。春节作为我国一年之中独特的消费窗口期,在全年经济发展中有着风向标式新春走基层这个新春佳节,看得见济南的冬天来源海报新闻老舍散文济南的冬天,把古城济南写得如诗如画,令人无限神往。如今,济南的冬天还有那个味吗?节日的大明湖畔,碧波荡漾,游人如织。蓝天绿湖相映成趣,城南的千佛山倒映湖中,水中
今年四十八岁,社保只交三年,是继续交?还是选择城居养老?我觉得如果经济条件允许的话,还是交吧!如果现在交正好赶上退休,年老之后能有个生活保障。我是80后,考虑以后指望孩子养老有点难,因为孩子自己压力就大,当然孩子肯定会给养老的,但我们又感觉有些人借钱也要给孩子娶老婆,甚至一辈子都还不起,为什么还这样做?多谢邀请!对对对,为什么要这样做?想不通吧?看你老人家高高在上幸灾乐祸的样子,你大抵是没有儿子不不不原来就是孤寡老人。可你别忘记,你离开人世还得靠众人抬上山去,难道不是吗?什么人什2022年春节,退休人员每人发放1200元过节补贴,是真的吗?用心不良,在这里面说话也要负责的。没有官方消息不可以乱讲,制造假新闻要负法律责任的。不信谣不传谣!确实有地方会发放1200元补贴,但是并不是全国每一个退休人员都发放1200元。到底有人给你误转了2万,让你转回去,为什么说千万不能急着转回去?稍微有点常识的人一眼就能看穿其中必然有诈,疑点有两个1,转账到银行卡,卡号和名字必须匹配,陌生人是怎么知道你的卡号和名字的?2,转错账第一时间联系你,陌生人是怎么知道你的联系方式的为什么十堰到宜昌只有一列火车,没有动车和高铁?根据湖北省十三五期间将建成汗十高铁十西高铁,推进丹西铁路十宜铁路的建设。十宜铁路是规划的三门峡十堰宜昌铁路的一段,十宜铁路以十堰六里坪为起点,经房县,穿神农架,过兴山,最后抵达终点想移居成都,成都有什么优缺点?我也算是外地移居到成都的,我就说一说我的亲身感受吧。在来之前,听别人或者网上看到,说成都人虚伪,假打,当时还怕怕的,心里也有一些抵触。但居住了一年左右,发现成都人不仅不虚伪,而且很能否取消老年免费公交卡,发乘车补助,结余归自己,缓解乘车难?年轻人不应该排斥老年人,老人里也是从年轻人过来的。其中也有年轻人的父母,你体谅别人的父母,别人也体谅你的父母,这样的社会不和谐都难!我认为老年卡归自己这是一个很好问題,即解决交通压如何看待经济学家姚洋说5000元已经是很高的收入了?捧铁饭碗,五金一险都是国家和企事业单位买好的,工资确实不算低,但这个经济学家却对那些没有五金一险的人和通货膨胀闭而不谈,对现在的教育,医疗,住房和生儿育女,养老的成本闭口不谈,不知老人过世借侄子屋停丧三天,侄子收5000元租费还有人情味吗?这是不能借的除非是自己亲爹亲娘,还有爷爷奶奶,其他绝对不行,也没有谁这样做,对于大家来说这是晦气,多少钱都没有谁去做,别说5000元,5万也没有人做,怎么也不应该放在侄子家,如果你如果国家取消彩票的发行,将会怎样?那么多网友质疑中大奖的真实性,我这里福彩站老板娘天天在群里售卖合股彩票,自己从来不买?国家彩票中心是否能开个新闻发布会?吃不到定心汤圆,就取消吧!体彩和福彩发行了这么多年,不知道收湖北居民医保已停缴,停缴前没交的人明年就不能享受医保待遇了吗?你好!如果未缴费,2022年全年就不能享受基本医疗保险住院,门诊报销政策。相应的门诊慢病,高血压,糖尿病门诊,国家谈判药品,大病保险等各项医疗保障政策待遇。所有发生的医疗费用都只能