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

变态需求给定一个接口,用户自定义动态实现上传热部署,咋搞?

  近期开发系统过程中遇到的一个需求,系统给定一个接口,用户可以自定义开发该接口的实现,并将实现打成jar包,上传到系统中。系统完成热部署,并切换该接口的实现
  定义简单的接口
  这里以一个简单的计算器功能为例,接口定义比较简单,直接上代码。  public interface Calculator {     int calculate(int a, int b);     int add(int a, int b); } 该接口的一个简单的实现
  考虑到用户实现接口的两种方式,使用spring上下文管理的方式,或者不依赖spring管理的方式,这里称它们为注解方式和反射方式。 calculate 方法对应注解方式,add方法对应反射方式。
  这里推荐一个 Spring Boot 基础教程:
  https://github.com/javastacks/spring-boot-best-practice
  计算器接口实现类的代码如下:  @Service public class 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) {         return new 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接口的实现 反射方式  */ 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容器中。其实,这就是一个类的热加载+动态注册的过程。另外,最新 Spring 面试题整理好了,大家可以在Java面试库小程序在线刷题。
  直接上代码:  /**  * 加入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) {         return false;     }     //是否是接口     if (cla.isInterface()) {         return false;     }     //是否是抽象类     if (Modifier.isAbstract(cla.getModifiers())) {         return false;     }     if (cla.getAnnotation(Component.class) != null) {         return true;     }     if (cla.getAnnotation(Repository.class) != null) {         return true;     }     if (cla.getAnnotation(Service.class) != null) {         return true;     }     return false; } /**  * 类名首字母小写 作为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);             }         }
  看完,涨姿势了没?
  原文链接:https://blog.csdn.net/zhangzhiqiang_0912/article/details/106980080

统筹兼顾,谱写跨越山海的航天协奏曲2022年10月31日下午,轰鸣声打破了海岛的寂静,搭载梦天实验舱的长征五号运载火箭,在我国文昌航天发射场点火升空,卫星顺利进入预定轨道,发射任务取得圆满成功,中心技术部以实际行动泰州白米镇致力交出亩均改革高分答卷今年以来,泰州市姜堰区白米镇把亩均论英雄改革作为转变经济发展方式优化经济结构激发增长动力有力抓手,推动工业经济实现高质量发展。截止目前,全镇清退僵尸企业6家零税企业10家,在盘活的披荆斩棘蔡珩唱歌演戏综艺,不同挑战让我更有冲劲从学习跳舞到成为一名演员,我已经走了23年。你问我,我的梦想在哪里?我还年轻,我还年轻。披荆斩棘第二季初舞台播出后,演员蔡珩在社交媒体平台上发文呼应演唱的歌曲我还年轻我还年轻。为期江苏首例网约车司机获赔误工费1。54万元央广网南京10月31日消息(记者王锡斐)近日,江苏泰州一网约车因交通事故导致车辆受损修理,从事网约车经营业务的个人获赔车辆营运损失15400元的案件,成为江苏省首个网约车司机误工费她不过是指了个路2019年贾永婷去山东看望孙洪正夫妇作者杜佳冰编辑从玉华孙洪正把2018年9月19日这个日子记了5年,但贾永婷起初并没放在心上。那就是一个普通的星期三,25岁的贾永婷忙到晚上9点才德邦净利飙升20倍,刘强东捡到宝了出品电商头条作者李松月净利大涨20倍,德邦重新看见希望德邦交出了一份让市场震惊的成绩单。10月27日,京东物流和德邦共同发布了德邦2022年第三季度财报。数据显示,该季度德邦营收8中航重机2022年前三季度净利润9。16亿元同比增长50。46中证智能财讯中航重机(600765)10月31日披露2022年第三季度报告。2022年前三季度,公司实现营业总收入77。18亿元,同比增长18。34归母净利润9。16亿元,同比增长如果只看联赛的表现!这六名球员更应该入选国家队,杜锋任人唯亲最新一期的中国男篮大名单出来了,一共是17人,辽宁队没有球员入选,广东队有四名球员入选赵睿胡明轩徐杰任骏飞,深圳队有三名球员入选顾全周鹏沈梓捷,浙江队有三名球员入选吴前王奕博陆文博足坛绝杀夜!孙兴慜莱万助热刺巴萨绝处逢生,利物浦遭降级队羞辱北京时间10月29日晚间到30日凌晨,欧洲五大联赛继续展开争夺,这一夜可以说是绝对的绝杀夜,在英超和西甲当中,3场比赛都在比赛的最后时刻出现绝杀的情况。这其中热刺和巴萨都在客场绝处古而不老,是绍兴绍兴文旅发布绍兴,古称越州,这是一座2500多年钟灵毓秀的历史名城。文物之邦,鱼米之乡,镜湖月下的吟诵,光怪陆离的梦境,从市井小吃,到黄酒文化,百川汇海,耐人寻味。古而不老,陈而不Utair增加飞往撒马尔罕的航班原标题Utair增加莫斯科飞往撒马尔罕的航班据详实网报道,11月14日起,俄罗斯优梯航空公司(Utair)将推出每周一从莫斯科飞往撒马尔罕的航班,届时,该航线航班每周将有4班周一周
女篮世界杯澳大利亚vs中国东道主?打的就是你东道主女篮世界杯澳大利亚vs中国澳大利亚作为本届世界杯东道主表现出色顺利晋级四强,除了首场比赛不敌之外剩下5场比赛均顺利赢球!坎贝奇这名顶级内线退出国家队后球队的上限确实大受影响,但球队只需一样食材,做出益肾填精的食疗方!早晚一勺,聪耳明目又益智填精补髓你的髓海还充盈吗?哪种健忘很危险?专家带来神秘金髓煎,这个膏能够填精补髓养肝益肾聪耳明目延年益寿,是老年人养生益智的常食之物,教你在家制作!脑为髓海,脑髓的充盈与否,与健康奇葩!全网首个为了玩娃娃机,被活活冻死在雪山中的玩家出现了经常玩网游的玩家,难免在游戏中会有一死,而在不同游戏中,玩家们的死法也是千奇百怪,有的是纯技术问题,而有一些则是玩家自己nozuonodie。但你见过有哪个玩家,是被活活冻死在雪地新倩女幽魂筑梦家园资料片上线,高自由度家园随心建造自从新倩女幽魂筑梦家园资料片上线后,相信很多小伙伴都沉浸在自由打造豪华家园的快乐中。而且,新倩女幽魂还和中国国家地理进行了国风联动,为我们带来了上千件家具组件,小伙伴们可以尽情体验京东刘强东的跑步鸡扶贫项目雷声大雨点小?这番操作算是扶贫吗?原创文章,敬请阅读2016年,京东刘强东提出跑步鸡扶助贫困农户脱贫项目,6年过去了,似乎并没有多大的进展,有人直言不讳这是不是典型的雷声大雨点小?刘强东这番不痛不痒的小操作,算是扶来了!首秀27分7板!单换王哲林的大中锋用一张图表达心情!世界之大,无奇不有。前有我大CBA同曦队,西热力江同时兼任球员和主教练职务后有西甲俱乐部赫罗纳老板兼球员。这个老板还很不简单,曾是三届NBA全明星中锋,以及前几年极印立可印N1口袋照片打印机,随时留住珍贵瞬间开场白从怀孕开始就想着给宝宝制作一本世界上属于她的独一无二的成长相册,从宝宝出生到现在手机里无数照片与视频,每次都想着怎么能把它们都打印出来整理成册,以前就很喜欢拍立得相机,每一次大学生买电脑,买Mac还是买windows?最近恰逢新iPhone发布,又到了开学季,不少学生都会选择在这个时候购买自己的第一台电脑,对于许多需要电脑进行工作的人来说,接下来的双十一也是他们购买电脑的良机。买Mac还是买wi迫不及待想要分享5个很好用的Win10功能,快来看呀电脑操作系统这玩意,非常有意思的是,总有很多你没有掌握的知识。比如说现在主流Win10系统,小编用着用着,才知道它还有这些操作技巧,真是太好用了。突然就觉得这些年,Win10系统就焦煤毛利润946元吨铁矿石50美元吨,钢材价格下跌空间很大?查询今日钢材价格,请点击众钢网专注钢材价格数据近日,沙钢董事局总裁沙钢东北特钢集团董事长龚盛在一次座谈会上表示,我们要充分认识到当前市场形势的严峻性以及严峻形势的长期性,做好过苦日怎么样选择专业的养老管理系统?养老管理系统简单来说就是利用云计算互联网人工智能物联网系统和信息平台,为老年人养老社区和养老机构提供服务。这种模式整合了个人家庭护工机构医疗等资源,满足不同程度的养老需求,通过明确