日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/1/1 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 还在手动部署jar包?太low了,动态上传jar包热部署真的爽! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是寶哥!

近期開發系統過程中遇到的一個需求,系統給定一個接口,用戶可以自定義開發該接口的實現,并將實現打成jar包,上傳到系統中。系統完成熱部署,并切換該接口的實現。

定義簡單的接口

這里以一個簡單的計算器功能為例,接口定義比較簡單,直接上代碼。

public?interface?Calculator?{int?calculate(int?a,?int?b);int?add(int?a,?int?b); }

該接口的一個簡單的實現

考慮到用戶實現接口的兩種方式,使用spring上下文管理的方式,或者不依賴spring管理的方式,這里稱它們為注解方式和反射方式。calculate方法對應注解方式,add方法對應反射方式。計算器接口實現類的代碼如下:

@Service public?class?CalculatorImpl?implements?Calculator?{@AutowiredCalculatorCore?calculatorCore;/***?注解方式*/@Overridepublic?int?calculate(int?a,?int?b)?{int?c?=?calculatorCore.add(a,?b);return?c;}/***?反射方式*/@Overridepublic?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容器中。其實,這就是一個類的熱加載+動態注冊的過程。

直接上代碼:

/***?加入jar包后?動態注冊bean到spring容器,包括bean的依賴*/ public?static?void?hotDeployWithSpring()?throws?Exception?{Set<String>?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<String>?readJarFile(String?jarAddress)?throws?IOException?{Set<String>?classNameSet?=?new?HashSet<>();JarFile?jarFile?=?new?JarFile(jarAddress);Enumeration<JarEntry>?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<String>?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

精彩推薦: MyBatis的三種分頁方式,你用過幾種?幾行代碼,搞定 SpringBoot 接口惡意刷新和暴力請求!Java實現人臉識別登錄、注冊等功能【附源碼】 SpringCloud+Gateway+Security 搭建微服務統一認證授權(附源碼)發現個工具,一鍵生成Spring Boot +Vue項目!接私活縮短一半工期...

總結

以上是生活随笔為你收集整理的还在手动部署jar包?太low了,动态上传jar包热部署真的爽!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。