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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

jar包 热加载/卸载 的初步实现

發布時間:2025/3/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jar包 热加载/卸载 的初步实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? 這兩天做的項目中按照客戶要求需要將插件模式應用到本項目中,以達到客戶可以自己動態增加相關功能的目的,然后我們就根據需求制定出接口,再由客戶自己實現接口,通過項目提供的相應界面將實現的jar包上傳,由服務器應用對jar包進行熱加載/卸載,jar包的熱加載用java原生的一些api即可實現,但問題是,使用原生的api的話,是無法實現卸載jar包的功能的,除非重啟應用,但又因為插件的基本特征就是熱加載,熱卸載,熱啟動等等熱的問題(總之就是熱,呵呵),這樣的話,重啟應用就顯得有那么點不專業了。所以還是實現一個熱加載/卸載的功能好點,一開始不知道怎么下手,后來在研究openfire時發現其插件jar包可以熱加載/卸載,于是乎研究其插件加載方式。經過一天的代碼調試,發現openfire是通過繼承URLClassLoader實現了一個自己的PluginClassLoader(想看源碼的自己去openfire官網下吧,這里就不提供了),于是我對PluginClassLoader進行了一下改造,去掉了一些沒用的方法與代碼,留下關鍵部分(這些部分仍然是openfire原生的東西).改造以后的PluginCLassLoader代碼如下:

1 package com.tds.test.classloader; 2 3 import java.net.JarURLConnection; 4 import java.net.URL; 5 import java.net.URLClassLoader; 6 import java.net.URLConnection; 7 import java.util.ArrayList; 8 import java.util.List; 9 10 11 /** 12 * 插件類加載器,在插件目錄中搜索jar包,并為發現的資源(jar)構造一個類加載器,將對應的jar添加到classpath中 13 * @author strawxdl 14 */ 15 public class PluginClassLoader extends URLClassLoader { 16 17 private List<JarURLConnection> cachedJarFiles = new ArrayList<JarURLConnection>(); 18 public PluginClassLoader() { 19 super(new URL[] {}, findParentClassLoader()); 20 } 21 22 /** 23 * 將指定的文件url添加到類加載器的classpath中去,并緩存jar connection,方便以后卸載jar 24 * @param 一個可想類加載器的classpath中添加的文件url 25 */ 26 public void addURLFile(URL file) { 27 try { 28 // 打開并緩存文件url連接 29 30 URLConnection uc = file.openConnection(); 31 if (uc instanceof JarURLConnection) { 32 uc.setUseCaches(true); 33 ((JarURLConnection) uc).getManifest(); 34 cachedJarFiles.add((JarURLConnection)uc); 35 } 36 } catch (Exception e) { 37 System.err.println("Failed to cache plugin JAR file: " + file.toExternalForm()); 38 } 39 addURL(file); 40 } 41 42 /** 43 * 卸載jar包 44 */ 45 public void unloadJarFiles() { 46 for (JarURLConnection url : cachedJarFiles) { 47 try { 48 System.err.println("Unloading plugin JAR file " + url.getJarFile().getName()); 49 url.getJarFile().close(); 50 url=null; 51 } catch (Exception e) { 52 System.err.println("Failed to unload JAR file\n"+e); 53 } 54 } 55 } 56 57 /** 58 * 定位基于當前上下文的父類加載器 59 * @return 返回可用的父類加載器. 60 */ 61 private static ClassLoader findParentClassLoader() { 62 ClassLoader parent = PluginManager.class.getClassLoader(); 63 if (parent == null) { 64 parent = PluginClassLoader.class.getClassLoader(); 65 } 66 if (parent == null) { 67 parent = ClassLoader.getSystemClassLoader(); 68 } 69 return parent; 70 } 71 }

  然后通過PluginManager.java對每個插件jar包的PluginClassLoader進行管理,PluginManager.java實現了jar包的加載(loadPlugin方法)與卸載(unloadPlugin方法),這里為了測試假設每一個插件jar包中實現了插件接口的類的package名均為com.tds.test.classloader.Plugin1,其中Plugin1即為實現了Plugin接口的類名(這兩個類稍后提供源碼),這里將其寫死在PluginManager中,在實際項目中當然每個插件的實現的package都會是不一樣的現在不深究這個問題。下邊上PluginManager.java代碼:

1 package com.tds.test.classloader; 2 3 import java.net.MalformedURLException; 4 import java.net.URL; 5 import java.util.HashMap; 6 import java.util.Map; 7 8 public class PluginManager { 9 static{ 10 System.out.println(PluginManager.class.getName()); 11 } 12 private Map<String ,PluginClassLoader> pluginMap = new HashMap<String,PluginClassLoader>(); 13 private static String packagename = "com.tds.test.classloader.Plugin1"; 14 public PluginManager(){ 15 16 } 17 18 public void doSome(String pluginName){ 19 20 try{ 21 Class<?> forName = Class.forName(packagename, true, getLoader(pluginName));//this.pluginMap.get(pluginName).loadClass(packagename); 22 Plugin ins = (Plugin)forName.newInstance(); 23 ins.doSome(); 24 }catch(Exception e){ 25 e.printStackTrace(); 26 } 27 } 28 private void addLoader(String pluginName,PluginClassLoader loader){ 29 this.pluginMap.put(pluginName, loader); 30 } 31 private PluginClassLoader getLoader(String pluginName){ 32 return this.pluginMap.get(pluginName); 33 } 34 public void loadPlugin(String pluginName){ 35 this.pluginMap.remove(pluginName); 36 PluginClassLoader loader = new PluginClassLoader(); 37 String pluginurl = "jar:file:/D:/testclassloader/"+pluginName+".jar!/"; 38 URL url = null; 39 try { 40 url = new URL(pluginurl); 41 } catch (MalformedURLException e) { 42 // TODO Auto-generated catch block 43 e.printStackTrace(); 44 } 45 loader.addURLFile(url); 46 addLoader(pluginName, loader); 47 System.out.println("load " + pluginName + " success"); 48 } 49 public void unloadPlugin(String pluginName){ 50 this.pluginMap.get(pluginName).unloadJarFiles(); 51 this.pluginMap.remove(pluginName); 52 } 53 }

???????? 下邊是接口類Plugin.java代碼:

1 package com.tds.test.classloader; 2 3 public interface Plugin { 4 5 public void doSome(); 6 }

??????? 下邊是接口Plugin.java實現類:Plugin1.java

1 package com.tds.test.classloader; 2 3 public class Plugin1 implements Plugin{ 10 public void doSome(){ 11 System.out.println("Plugin1 doSome ... 我不可以?"); 12 } 13 }

????? 將上述Plugin1.java單獨導出為jar包,命名為plugin1.jar

??????修改Plugin1.java代碼,如下所示:

package com.tds.test.classloader;public class Plugin1 implements Plugin{public void doSome(){System.out.println("Plugin1 doSome ... 我可以?");} }


????? 將修改以后的Plugin1.java單獨導出為Plugin2.jar

????? 這時Plugin1.jar和Plugin2.jar對Plugin接口的實現都是不同的了,測試主類如下:

1 package com.tds.test.classloader; 2 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 6 public class TestMain { 7 8 public static void main(String[] args) throws Exception { 9 10 11 PluginManager manager = new PluginManager();; 12 13 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 14 String cmd = br.readLine(); 15 16 while(!cmd.equals("bye")){ 17 if(cmd.startsWith("do")){ 18 String pluginName = cmd.split(" ")[1]; 19 manager.doSome(pluginName); 20 } 21 if(cmd.startsWith("load")){ 22 String pluginName = cmd.split(" ")[1]; 23 manager.loadPlugin(pluginName); 24 } 25 if(cmd.startsWith("unload")){ 26 String pluginName = cmd.split(" ")[1]; 27 manager.unloadPlugin(pluginName); 28 } 29 cmd = br.readLine(); 30 } 31 } 32 }

?????? 測試方法為:在TestMain.java中run as JavaApplication,然后plugin1.jar放到D:/testclassloader/目錄下并改名為plugin.jar,最后在控制臺輸入load plugin即可將plugin.jar使用自定義的PluginClassLoader進行裝載,這時再使用do plugin命令即可調用該插件的實現,現在卸載plugin.jar,輸入unload plugin命令即可卸載掉plugin.jar,接下來關鍵時刻到了刪除D:/testclassloader/目錄的plugin.jar,將plugin2.jar放入該目錄并更名為plugin.jar,重復之前的jar包加載命令并運行之,你會發現兩次運行的結果不一樣了下邊是我運行的示例:

com.tds.test.classloader.PluginManager load plugin load plugin success do plugin com.tds.test.classloader.Plugin1 Plugin1 doSome ... 我可以? unload plugin Unloading plugin JAR file D:\testclassloader\plugin.jar load plugin load plugin success do plugin com.tds.test.classloader.Plugin1 Plugin1 doSome ... 我不可以?

總結

以上是生活随笔為你收集整理的jar包 热加载/卸载 的初步实现的全部內容,希望文章能夠幫你解決所遇到的問題。

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