网易首页 > 网易号 > 正文 申请入驻

手动部署jar包,太low了!动态上传包热部署,真爽!

0
分享至

Java精选面试题(微信小程序):5000+道面试题和选择题,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!

近期开发系统过程中遇到的一个需求,系统给定一个接口,用户可以自定义开发该接口的实现,并将实现打成jar包,上传到系统中。系统完成热部署,并切换该接口的实现。

定义简单的接口

这里以一个简单的计算器功能为例,接口定义比较简单,直接上代码。

public interface Calculator {     int calculate(int a, int b);     int add(int a, int b); }
该接口的一个简单的实现

考虑到用户实现接口的两种方式,使用spring上下文管理的方式,或者不依赖spring管理的方式,这里称它们为注解方式和反射方式。calculate方法对应注解方式,add方法对应反射方式。计算器接口实现类的代码如下:

@Service publicclass CalculatorImpl implements Calculator {     @Autowired     CalculatorCore calculatorCore;     /**      * 注解方式      */     @Override     public int calculate(int a, int b) {         int c = calculatorCore.add(a, b);         return c;     }     /**      * 反射方式      */     @Override     public int add(int a, int b) {         returnnew CalculatorCore().add(a, b);     } }

这里注入CalculatorCore的目的是为了验证在注解模式下,系统可以完整的构造出bean的依赖体系,并注册到当前spring容器中。CalculatorCore的代码如下:

@Service public class CalculatorCore {     public int add(int a, int b) {         return a+b;     } }
反射方式热部署

用户把jar包上传到系统的指定目录下,这里定义上传jar文件路径为jarAddress,jar的Url路径为jarPath。

private static String jarAddress = "E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar"; private static String jarPath = "file:/" + jarAddress;

并且可以要求用户填写jar包中接口实现类的完整类名。接下来系统要把上传的jar包加载到当前线程的类加载器中,然后通过完整类名,加载得到该实现的Class对象。然后反射调用即可,完整代码:

/**  * 热加载Calculator接口的实现 反射方式,公众号Java精选  */ public static void hotDeployWithReflect() throws Exception {     URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());     Class clazz = urlClassLoader.loadClass("com.nci.cetc15.calculator.impl.CalculatorImpl");     Calculator calculator = (Calculator) clazz.newInstance();     int result = calculator.add(1, 2);     System.out.println(result); }
注解方式热部署

如果用户上传的jar包含了spring的上下文,那么就需要扫描jar包里的所有需要注入spring容器的bean,注册到当前系统的spring容器中。其实,这就是一个类的热加载+动态注册的过程。

直接上代码:

/**  * 加入jar包后 动态注册bean到spring容器,包括bean的依赖  */ public static void hotDeployWithSpring() throws Exception {     Set classNameSet = DeployUtils.readJarFile(jarAddress);     URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());     for (String className : classNameSet) {         Class clazz = urlClassLoader.loadClass(className);         if (DeployUtils.isSpringBeanClass(clazz)) {             BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);             defaultListableBeanFactory.registerBeanDefinition(DeployUtils.transformName(className), beanDefinitionBuilder.getBeanDefinition());         }     } }

在这个过程中,将jar加载到当前线程类加载器的过程和之前反射方式是一样的。然后扫描jar包下所有的类文件,获取到完整类名,并使用当前线程类加载器加载出该类名对应的class对象。判断该class对象是否带有spring的注解,如果包含,则将该对象注册到系统的spring容器中。

DeployUtils包含读取jar包所有类文件的方法、判断class对象是否包含sping注解的方法、获取注册对象对象名的方法。代码如下:

/**  * 读取jar包中所有类文件  */ public static Set   readJarFile(String jarAddress) throws IOException {     Set classNameSet =  new HashSet<>();     JarFile jarFile = new JarFile(jarAddress);     Enumeration entries = jarFile.entries(); //遍历整个jar文件     while (entries.hasMoreElements()) {         JarEntry jarEntry = entries.nextElement();         String name = jarEntry.getName();         if (name.endsWith(".class")) {             String className = name.replace(".class", "").replaceAll("/", ".");             classNameSet.add(className);         }     }     return classNameSet; }

/**  * 方法描述 判断class对象是否带有spring的注解  */ public static boolean isSpringBeanClass(Class cla) {     if (cla == null) {         returnfalse;     }     //是否是接口     if (cla.isInterface()) {         returnfalse;     }     //是否是抽象类     if (Modifier.isAbstract(cla.getModifiers())) {         returnfalse;     }     if (cla.getAnnotation(Component.class) != null) {         returntrue;     }     if (cla.getAnnotation(Repository.class) != null) {         returntrue;     }     if (cla.getAnnotation(Service.class) != null) {         returntrue;     }     returnfalse; }

/**  * 类名首字母小写 作为spring容器beanMap的key  */ public static String transformName(String className) {     String tmpstr = className.substring(className.lastIndexOf(".") + 1);     return tmpstr.substring(0, 1).toLowerCase() + tmpstr.substring(1); }
删除jar时,需要同时删除spring容器中注册的bean

在jar包切换或删除时,需要将之前注册到spring容器的bean删除。spring容器的bean的删除操作和注册操作是相逆的过程,这里要注意使用同一个spring上下文。

代码如下:

/**  * 删除jar包时 需要在spring容器删除注入  */ public static void delete() throws Exception {     Set classNameSet = DeployUtils.readJarFile(jarAddress);     URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL(jarPath)}, Thread.currentThread().getContextClassLoader());     for (String className : classNameSet) {         Class clazz = urlClassLoader.loadClass(className);         if (DeployUtils.isSpringBeanClass(clazz)) {             defaultListableBeanFactory.removeBeanDefinition(DeployUtils.transformName(className));         }     } }
测试

测试类手动模拟用户上传jar的功能。测试函数写了个死循环,一开始没有找到jar会抛出异常,捕获该异常并睡眠10秒。这时候可以把jar手动放到指定的目录下。

代码如下:

 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");     DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();     while (true) {         try {               hotDeployWithReflect(); //            hotDeployWithSpring(); //            delete();             } catch (Exception e) {                 e.printStackTrace();                 Thread.sleep(1000 * 10);             }         }

作者:zhangzhiqiang_0912

https://blog.csdn.net/zhangzhiqiang_0912

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有读者交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

文章有帮助的话,点在看,转发吧!

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
突发,全线大跌!

突发,全线大跌!

魏家东
2026-01-30 13:20:50
172:233!日本新首相人选出炉,高市开始豪赌,最后三种可能性

172:233!日本新首相人选出炉,高市开始豪赌,最后三种可能性

boss外传
2026-01-30 00:00:04
父亲工龄48年,退休金每月才880元,我找去社保局,工作人员却愣了:他15年前就是高级工程师,特殊津贴每月16480

父亲工龄48年,退休金每月才880元,我找去社保局,工作人员却愣了:他15年前就是高级工程师,特殊津贴每月16480

起飞做故事
2026-01-28 18:56:29
国家再发“预警”,春节期间别去日本,如果爆发战争,会一起挨炸

国家再发“预警”,春节期间别去日本,如果爆发战争,会一起挨炸

我心纵横天地间
2026-01-29 14:45:20
立陶宛急着访华,就是等不来结果!

立陶宛急着访华,就是等不来结果!

达文西看世界
2026-01-30 17:10:33
赘婿新高度!大布一共千万资产,妻子每月零花钱就百万... 他要学哈里出书了?

赘婿新高度!大布一共千万资产,妻子每月零花钱就百万... 他要学哈里出书了?

英国那些事儿
2026-01-30 23:25:58
奥尼尔:我不想詹姆斯打全明星替补,应为“国王”留一份体面

奥尼尔:我不想詹姆斯打全明星替补,应为“国王”留一份体面

懂球帝
2026-01-30 17:36:06
该亮剑就亮剑,中国这次干得漂亮!中国向全球表明态度

该亮剑就亮剑,中国这次干得漂亮!中国向全球表明态度

军武咖
2026-01-30 14:31:27
高峰也没想到,他当年抛弃的儿子,如今开始给那英争光了

高峰也没想到,他当年抛弃的儿子,如今开始给那英争光了

趣文说娱
2026-01-04 16:34:24
大局已定,今年的返乡潮可能彻底消失了!

大局已定,今年的返乡潮可能彻底消失了!

达文西看世界
2026-01-29 19:30:48
泽连斯基回应“若愿意可前往莫斯科与俄总统会晤”:我可以邀请普京来基辅

泽连斯基回应“若愿意可前往莫斯科与俄总统会晤”:我可以邀请普京来基辅

环球网资讯
2026-01-30 17:11:42
嘉禾望岗爆火:经济衰退后,又一波年轻人“逃离北上广”

嘉禾望岗爆火:经济衰退后,又一波年轻人“逃离北上广”

老白读书
2026-01-30 17:03:38
广东3消息!杜锋遭上海下马威,朱芳雨开抢麦基,徐杰复出打宁波

广东3消息!杜锋遭上海下马威,朱芳雨开抢麦基,徐杰复出打宁波

多特体育说
2026-01-30 22:55:21
没人看好穆里尼奥!皇马附加赛再战本菲卡,克罗斯:不可能再爆冷

没人看好穆里尼奥!皇马附加赛再战本菲卡,克罗斯:不可能再爆冷

夏侯看英超
2026-01-31 02:12:33
“访华热”持续升温,立陶宛总统急了,要中方释放善意让自己访华

“访华热”持续升温,立陶宛总统急了,要中方释放善意让自己访华

荆楚寰宇文枢
2026-01-30 23:07:35
为什么医生建议你多做俯卧撑?你 1 分钟能做多少俯卧撑?

为什么医生建议你多做俯卧撑?你 1 分钟能做多少俯卧撑?

增肌减脂
2026-01-30 15:57:59
多名省委常委当选新职务!10人履新副部级新职

多名省委常委当选新职务!10人履新副部级新职

上观新闻
2026-01-30 13:12:04
“我妈妈没了”女乘客在高铁上得知母亲去世后,情绪崩溃大哭,乘客们都关掉手机声安静陪伴

“我妈妈没了”女乘客在高铁上得知母亲去世后,情绪崩溃大哭,乘客们都关掉手机声安静陪伴

观威海
2026-01-30 11:10:25
81亿!存储巨头签订大单,利润暴涨672%

81亿!存储巨头签订大单,利润暴涨672%

芯东西
2026-01-30 20:44:33
总统秒变摇滚主唱!阿根廷米莱突袭前女友演唱会,合唱情歌掀狂欢

总统秒变摇滚主唱!阿根廷米莱突袭前女友演唱会,合唱情歌掀狂欢

老马拉车莫少装
2026-01-30 01:08:35
2026-01-31 03:56:49
Java精选
Java精选
一场永远也演不完的戏
1769文章数 3859关注度
往期回顾 全部

科技要闻

意念控制机器人不是科幻 1-2年就落地

头条要闻

伊朗总统:若美国寻求谈判 就必须停止挑衅

头条要闻

伊朗总统:若美国寻求谈判 就必须停止挑衅

体育要闻

“假赌黑”的子弹,还要再飞一会儿吗?

娱乐要闻

警方通报金晨交通事故,否认网传骗保

财经要闻

水贝惊雷:揭秘杰我睿百亿黄金赌局的背后

汽车要闻

合资品牌首搭800V/5C快充 东风日产NX8将于3、4月上市

态度原创

数码
本地
游戏
亲子
公开课

数码要闻

陶瓷外壳与键帽,黑爵推出非遗传承人黄云鹏联名Blue67磁轴键盘

本地新闻

云游中国|拨开云雾,巫山每帧都是航拍大片

向全体二游策划宣战,你们能不能直接把我推删了?

亲子要闻

心血管风险或始于子宫孕期不良暴露留下"胎儿期烙印"

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版