0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何实现动态上传jar包热部署

Android编程精选 来源:CSDN技术社区 作者:zhangzhiqiang_0912 2022-06-20 16:57 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

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

定义简单的接口

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

publicinterfaceCalculator{
intcalculate(inta,intb);
intadd(inta,intb);
}

该接口的一个简单的实现

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

@Service
publicclassCalculatorImplimplementsCalculator{
@Autowired
CalculatorCorecalculatorCore;
/**
*注解方式
*/
@Override
publicintcalculate(inta,intb){
intc=calculatorCore.add(a,b);
returnc;
}
/**
*反射方式
*/
@Override
publicintadd(inta,intb){
returnnewCalculatorCore().add(a,b);
}
}

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

@Service
publicclassCalculatorCore{
publicintadd(inta,intb){
returna+b;
}
}

反射方式热部署

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

privatestaticStringjarAddress="E:/zzq/IDEA_WS/CalculatorTest/lib/Calculator.jar";
privatestaticStringjarPath="file:/"+jarAddress;

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

/**
*热加载Calculator接口的实现反射方式
*/
publicstaticvoidhotDeployWithReflect()throwsException{
URLClassLoaderurlClassLoader=newURLClassLoader(newURL[]{newURL(jarPath)},Thread.currentThread().getContextClassLoader());
Classclazz=urlClassLoader.loadClass("com.nci.cetc15.calculator.impl.CalculatorImpl");
Calculatorcalculator=(Calculator)clazz.newInstance();
intresult=calculator.add(1,2);
System.out.println(result);
}

注解方式热部署

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

直接上代码:

/**
*加入jar包后动态注册bean到spring容器,包括bean的依赖
*/
publicstaticvoidhotDeployWithSpring()throwsException{
SetclassNameSet=DeployUtils.readJarFile(jarAddress);
URLClassLoaderurlClassLoader=newURLClassLoader(newURL[]{newURL(jarPath)},Thread.currentThread().getContextClassLoader());
for(StringclassName:classNameSet){
Classclazz=urlClassLoader.loadClass(className);
if(DeployUtils.isSpringBeanClass(clazz)){
BeanDefinitionBuilderbeanDefinitionBuilder=BeanDefinitionBuilder.genericBeanDefinition(clazz);
defaultListableBeanFactory.registerBeanDefinition(DeployUtils.transformName(className),beanDefinitionBuilder.getBeanDefinition());
}
}
}

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

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

/**
*读取jar包中所有类文件
*/
publicstaticSetreadJarFile(StringjarAddress)throwsIOException{
SetclassNameSet=newHashSet<>();
JarFilejarFile=newJarFile(jarAddress);
Enumerationentries=jarFile.entries();//遍历整个jar文件
while(entries.hasMoreElements()){
JarEntryjarEntry=entries.nextElement();
Stringname=jarEntry.getName();
if(name.endsWith(".class")){
StringclassName=name.replace(".class","").replaceAll("/",".");
classNameSet.add(className);
}
}
returnclassNameSet;
}
/**
*方法描述判断class对象是否带有spring的注解
*/
publicstaticbooleanisSpringBeanClass(Classcla){
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
*/
publicstaticStringtransformName(StringclassName){
Stringtmpstr=className.substring(className.lastIndexOf(".")+1);
returntmpstr.substring(0,1).toLowerCase()+tmpstr.substring(1);
}

删除jar时,需要同时删除spring容器中注册的bean

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

代码如下:

/**
*删除jar包时需要在spring容器删除注入
*/
publicstaticvoiddelete()throwsException{
SetclassNameSet=DeployUtils.readJarFile(jarAddress);
URLClassLoaderurlClassLoader=newURLClassLoader(newURL[]{newURL(jarPath)},Thread.currentThread().getContextClassLoader());
for(StringclassName:classNameSet){
Classclazz=urlClassLoader.loadClass(className);
if(DeployUtils.isSpringBeanClass(clazz)){
defaultListableBeanFactory.removeBeanDefinition(DeployUtils.transformName(className));
}
}
}

测试

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

代码如下:

ApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");
DefaultListableBeanFactorydefaultListableBeanFactory=(DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();
while(true){
try{
hotDeployWithReflect();
//hotDeployWithSpring();
//delete();
}catch(Exceptione){
e.printStackTrace();
Thread.sleep(1000*10);
}
}

		

					
						


																															

																															

																															

																																			

																																			

																																					

																																					

																																					

原文标题:求求你别再手动部署jar包了,太low了!动态上传热部署真的太爽了!

文章出处:【微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    9443

    浏览量

    156108
  • 开发系统
    +关注

    关注

    0

    文章

    39

    浏览量

    10208
  • spring
    +关注

    关注

    0

    文章

    341

    浏览量

    15770

原文标题:求求你别再手动部署jar包了,太low了!动态上传热部署真的太爽了!

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    商品图片批量上传接口设计与实现

    ? 在电商平台或内容管理系统中,商品图片的高效管理是核心需求之一。批量上传接口允许用户一次性上传多张图片,显著提升操作效率。本文将逐步介绍如何设计并实现一个可靠的商品图片批量上传接口,
    的头像 发表于 10-13 15:25 195次阅读

    产品图片上传API接口

    ​ 在电商平台、内容管理系统或移动应用中,产品图片上传API接口是核心功能之一。它允许用户或第三方应用通过HTTP请求将图片文件上传到服务器,实现产品图像的快速添加和管理。本文将逐步介绍该接口
    的头像 发表于 07-25 14:30 454次阅读
    产品图片<b class='flag-5'>上传</b>API接口

    上传压缩的时候总是显示上传失败,为什么?

    上传压缩的时候总是显示上传失败是说明原因
    发表于 07-23 08:17

    ocr识别时数据集上传压缩上传成功,但不显示图片,图片数量仍显示0,为什么?

    ocr识别时数据集上传压缩上传成功,但不显示图片,图片数量仍显示0
    发表于 07-23 08:11

    ​数字孪生热管理:NTC热敏电阻阵列与场重构算法的动态适配

    本文以东莞市平尚电子科技有限公司(平尚科技)的NTC热敏电阻阵列与场重构算法为核心,探讨其在车载数字孪生热管理系统中的动态适配技术。通过高精度NTC阵列、多物理场耦合模型及实时反馈控制算法,实现
    的头像 发表于 06-06 17:59 610次阅读
    ​数字孪生热管理:NTC热敏电阻阵列与<b class='flag-5'>热</b>场重构算法的<b class='flag-5'>动态</b>适配

    桂花网蓝牙网关物联网医院动态血糖管理应用案例

    医院动态血糖管理应用案例 1、三诺动态血糖管理 该方案是集智能硬件、物联网、大数据分析于一体的综合性解决方案,通过以下核心步骤实现院内糖尿病患者的精准管理: (1)、全院CGM统一管理:通过统一平台
    发表于 06-05 16:17

    蓝牙数据通道空口(数据

    ​ 与蓝牙广播相对应,蓝牙数据是另一种Bluetooth LE packet。蓝牙数据是蓝牙数据信道空中的简称,表示空中只在蓝牙数
    发表于 06-03 10:51

    业务监控—一站式搭建jmeter+telegraf+influxdb+Grafana看板

    jar的JVM指标如何?jar的火焰图是什么样的? 对此,只能望洋兴叹。基于以上背景,对jar监控做了一些调研和实战。    二、调研
    的头像 发表于 05-27 14:40 1095次阅读
    业务监控—一站式搭建jmeter+telegraf+influxdb+Grafana看板

    鸿蒙开发实现图片上传上传用户头像)

    (FilePicker),实现该能力。通过Picker访问相关文件,将拉起对应的应用,引导用户完成界面操作,接口本身无需申请权限。 import picker from \'@ohos.file.picker
    发表于 05-24 23:09

    HarmonyOS5云服务技术分享--Serverless抽奖模板部署

    通过endCallback实现中奖消息推送 ? ​​安全加固​​ 非免认证模式下建议: 添加请求频率限制 使用HTTPS加密回调 用户ID做哈希处理 五、常见问题QA ❓ 部署后访问显示空白
    发表于 05-22 20:25

    锂电池失控原理及安全检测技术解析

    与未来趋势 现有设备大多已实现高精度检测,但仍面临两大瓶颈: 动态监测能力不足:现有系统多局限于实验室环境,难以实时监控车载电池状态; 多参数耦合分析缺失:-电-机械多场耦合效应对
    发表于 05-12 16:51

    K230D部署模型失败的原因?

    重现步骤 1.按照教程实现MicroPython 版本部署流程,连接上并将文件放到对应位置后,刚运行就断开和canmv ide的连接了(此时用的固件版本是图中推荐的K230D_Zero...
    发表于 03-11 06:19

    是否可以使用OpenVINO™部署管理器在部署机器上运行Python应用程序?

    使用 OpenVINO™部署管理器创建运行时软件。 将运行时转移到部署机器中。 无法确定是否可以在部署机器上运行 Python 应用程
    发表于 03-05 08:16

    使用DRBD和keepalived实现文件实时同步和双机

    使用DRBD和keepalived实现文件实时同步和双机
    的头像 发表于 03-03 17:20 854次阅读

    ComplexHeatmap:个性化图绘制利器

      图在很多文章中都有出现,如何画出让人眼前一亮的图?ComplexHeatmap具有非常高的可定制性,无论是图的颜色搭配、字体样式,还是聚类方式、注释信息,每一个细节都能按照
    的头像 发表于 12-31 11:17 1338次阅读
    ComplexHeatmap<b class='flag-5'>包</b>:个性化<b class='flag-5'>热</b>图绘制利器