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

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

  近期开发系统过程中遇到的一个需求,系统给定一个接口,用户可以自定义开发该接口的实现,并将实现打成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

宝鸡擀面皮的汁子怎么调?宝鸡擀面皮的汁子主要看辣椒油。1(1)准备。红辣椒碎,7制作调料和辣椒油将三奈,八角香果桂皮花椒,研成极细的粉末配成调料面,岐山陈醋,白糖,盐等。(2)把红辣椒碎与调料面先拌匀,油乌鲁木齐周边自驾游哪些地方好玩?今天,距下一次放假还遥遥无期红山公园红光山南山石人沟这些地方去了太多次,都能倒背如流,下面这些不太远,又小众的神秘景区,一两天时间就够去玩啦!苏拉夏河谷距离乌鲁木齐约60公里,大概南充哪些景点最好玩?印象嘉陵江湿地公园,大美!朱总司令故里马安林郎山丁氏庒园金城镇朱德铜像囩金城山避暑山庄还有金城镇红四方面军住址金城镇南街夜巿应有尽有热闹非凡还有白塔山公园还有冬瓜梁休闲住所烟岱山农一个家庭宽带最多可以连几台设备,电信商有没有限制?掐指一算,也是骨灰级的宽带用户了。现在,运营商对家庭宽带上网电脑数量是有限制的,联通的政策是,最多允许4台电脑同时在线。2013年前后,联通和电信开始封杀家庭路由器。当时的封杀还是大乔团战皮肤特效曝光,玩家称绿的发慌技能像化粪池爆炸带着味道,皮肤还该买吗?王者荣耀团战精神皮肤是玩家一直期待的皮肤,但是官方在团战皮肤上下的功夫明显不够,随着皮肤模型都早已曝光,但是皮肤上线的速度却是十分的慢,最近终于又有了皮肤的消息!官方在推出孙尚香诸在事业单位改革中,工人身份聘用在专业技术岗位并获得中级职称的,能直接参公吗?不能直接参公,需通过公开组织工人身份人员录用考试,达到及格分数线的,办理参公手续。事业单位能不能参公必须有国家法律法规明确授权为依据,不以政府规章三定方案会议纪要编办规定的职责等为手机外接摄像头的效果怎么样?当然好。买个微单还专业效果是有的,而且就算在某宝上随便买个几十块钱的外挂镜头拍照效果也是很不错的。但是这东西弊大于利,首先一个,手机拍照本来就是以便携为主,在手机上加上一个外接镜头热血传奇极品骷髅戒指攻5,龙之戒指也是攻5,请问这两种使用起来效果有何不同?热血传奇中的每个装备部位都非常重要,除了武器这种关键位置外,哪怕一个戒指也对最后输出的实际伤害有着重要的帮助。不过在具体的戒指选择上,也是有很多争议的,比如说不少装备虽然属性相当,信用卡中心突然要我提供消费发票,不然就降额度,真的会降吗?上次浦发的让我使用20W的消费贷过后喊我提供发票我说我个人使用买东西没有要发票只有收据。提供收据可以就可以不行我也没有办法最后没有人找我了偶尔办个分期就行了,真要降额的时候,根本不功夫中三大高手为什么不联合起来,共抗琴魔兄弟?在电影功夫中,没有宏大的历史背景作为叙事,也没有出现过叱咤风云的历史人物作为剧情的推手,如何让观众在人物群像中一眼就认出某个角色,并滋生出真实感和认同感,导演花费了巨大的心力。让我洛阳未来十年会怎样?洛阳未来十年的发展潜力将会是巨大的,而且洛阳市目前就是河南省的第二大城市,毕竟是曾经的一个非常辉煌的城市,只能够说瘦死的骆驼比马大,所以说今天的洛阳还是一个发展不错的城市。不仅如此
中国女排各个位置和世界强队相比较!都处在什么水平?2022年世界女排锦标赛将于下个月底拉开战幕,备受大家关注的中国女排究竟能拿到怎么样的成绩是大家最为关心的,实际上中国女排现阶段的实力就客观摆在那里,球队在没有朱婷的情况下很难有机死亡之组?从欧冠分组分析意甲四支球队签运北京时间2022年8月26日零点,20222023赛季欧冠小组赛抽签结束,完整的分组结果出炉。其中C组抽签结果一出,镜头立即对准国米代表萨内蒂,后者面色凝重。的确,与巴萨拜仁分在一41,狂轰15脚迅速反弹!巴萨终于赢了,过气球星爆发,升至第5名文彬少侃球(首发)西甲第2轮比赛,巴萨对阵皇家社会队,这场比赛对于巴萨来说,也是压力巨大,因为首战比赛中,他们主场对阵巴列卡诺的比赛爆出了冷门,在占据优势的情况下,竟然被保级队逼平斯诺克北爱尔兰公开赛资格赛第四日傅家俊再次告负中国军团两连败中新社北京8月27日电北京时间27日晨,2022年北爱尔兰公开赛资格赛结束第四日的较量,中国军团2人出战双遭败绩,其中,中国香港名将傅家俊再次告负,近两周遭遇三连败。本次资格赛4日央视直播,世锦赛半决赛国羽陈雨菲VS戴资颖,赵俊鹏冲击金牌2022年羽毛球世锦赛进入最后阶段,男女打单将展开半决赛厮杀。赵俊鹏陈雨菲晋级半决赛。其中,男单半决赛国羽赵俊鹏VS昆拉武特。女单半决赛国羽陈雨菲VS戴资颖。(一)世锦赛赵俊鹏逆转爵士二次交易,湖人连下两城!完美适配詹姆斯,你是新的带刀侍卫威斯布鲁克的交易,直接决定了湖人下赛季的最低高度,也就是说,这笔交易会帮助湖人重塑球队的基本盘,而湖人最终的上限,还是要看詹姆斯和戴维斯的发挥。杜兰特宣布留队之后,湖人交易欧文的美前荷兰国脚人们对伊布非爱即恨,没有中间立场直播吧8月27日讯接受米兰新闻网记者采访时,前荷兰国脚兰扎特谈到了关于伊布的话题。兰扎特表示伊布刚刚来到荷兰的时候还非常年轻,但是即便如此,那时候的他也已经拥有很强的个性和端正的态从国企辞职后,我去当了一位农民如果有一天我能够拥有一个大果园,我愿放下所有追求做个农夫去种田。每一个早晨我耕耘在绿野田园每一个黄昏我守望在乡间的麦田我会把忧虑都融化在夕阳里,让孤独的心等待秋收的欢喜。农夫渔夫你人生路上的难,躲不过就面对人生路上总会有艰难险阻,总会有大雨滂沱。如果不能躲过,倒不如不抱怨,不消极,坦然面对人生从来不会尽如人意生活里的人,往往都不在你的心里在你心里的人,往往都不在生活里现在流行一句长期杨幂的8张美图,张张皆是珍藏款1。我从冰激凌偷出了夏天,也想在你眼中窃取爱意。有你的青春是一场大雨,即使感冒了,还盼望回头再淋它一次。2。你是这世间一切美好事情的前提,你是人间这不毛之地的高楼林立,你是海风渐暖活着,好魔幻我们都活着,没活着的,不是我们!是的,我们都活着!女孩?活着的我们,有的大富,有的大贫,更多的是像你我,普普通通,还活着!追求?吃饭?我还活着,我只能这样对自己说。因为现在的我死不