熱部署其實就是如何在服務器不重啓的時候動態的替換相關模塊,要講熱部署以前就要先了解一下類加載機制。java
JVM加載一個類須要通過這麼幾個階段,加載,驗證,解析,初始化,使用,卸載。其中到初始化階段以前是類的加載所有。而且JVM在加載一個類時是用的雙親委派模型。也就是當classloader加載一個類的時候並非本身去嘗試加載,而是一層一層委派,當頂層父加載器沒法加載,再自頂向下嘗試加載。緩存
類加載器在加載一個類時,只會加載一次,也就是一個加載器下的同名類只會存在一個,每次加載都會先看緩存。因此熱部署的關鍵就是自定義一個加載器,先繼承classloader,而後實現findclass方法,這樣就能繞過雙親委派模型的限制。服務器
直接上代碼:每次加載一個類的時候先看一下是否被修改過,若是被修改過那麼嘗試加載。框架
這裏有個問題,類加載器啥時候被回收,好像看起來會一直佔着不會被GC。因此如今基本上都是經過OSGi框架來實現模塊的熱插拔,感興趣的朋友能夠參考《OSGi原理與最佳實踐》。函數
package com.dlb.note.hotDeploy; import java.lang.reflect.Method; /** * 功能:主函數類,專門用於測試 * 版本:1.0 * 日期:2016/12/9 11:18 * 做者:馟蘇 */ public class Main { /** * 主函數 */ public static void main(String []args) { while (true) { try { Class cls = ManageClassLoader.loadClass("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class"); Method method = cls.getMethod("cry"); method.invoke(cls.newInstance()); Thread.sleep(5000); } catch (Exception e) { System.out.println(e); } } } }
package com.dlb.note.hotDeploy; import java.io.File; import java.io.FileInputStream; import java.io.IOException; /** * 功能:管理類加載器 * 版本:1.0 * 日期:2016/12/9 10:34 * 做者:馟蘇 */ public class ManageClassLoader { /**類文件的上次修改時間*/ private static Long lastModified = 0l; /**類的緩存*/ private static Class cache = null; /**動態類加載器*/ private static DynamicClassLoader dc = null; /** * 這是入口方法! * 加載類, 若是類文件修改過加載,若是沒有修改,返回當前的 * @param fileName 路徑名 + 文件名 + 擴展名 * @return * @throws ClassNotFoundException * @throws IOException */ public synchronized static Class loadClass(String fileName) throws ClassNotFoundException, IOException{ if (isClassModified(fileName)){ // 類文件被修改 /**建立一個動態類加載器用於加載類(同一個類加載器下不能出現兩個同名類)*/ dc = new DynamicClassLoader(); return (cache = dc.findClass(getBytes(fileName))); } return cache; } /** * 判斷是否被修改過 * @param fileName 路徑名 + 文件名 + 擴展名 * @return */ private synchronized static boolean isClassModified(String fileName) { boolean returnValue = false; File file = new File(fileName); if (file.lastModified() > lastModified) { returnValue = true; } return returnValue; } /** * 從本地讀取文件 * @param fileName 路徑名 + 文件名 + 擴展名 * @return * @throws IOException */ private synchronized static byte[] getBytes(String fileName) throws IOException { File file = new File(fileName); long len = file.length(); lastModified = file.lastModified(); // 文件上次修改時間 byte raw[] = new byte[(int) len]; FileInputStream fin = new FileInputStream(file); int r = fin.read(raw); if (r != len) { throw new IOException("Can't read all, " + r + " != " + len); } fin.close(); return raw; } public static void main(String []args) throws Exception{ DynamicClassLoader dc = new DynamicClassLoader(); Class<?> cls = dc.findClass(getBytes("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class")); System.out.println(cls); cls = dc.findClass(getBytes("E:\\note\\target\\classes\\com\\dlb\\note\\doj\\Cat.class")); System.out.println(cls); } } /** * 一個動態類加載器,用於加載類(同一個類加載器下不能出現兩個同名類) */ class DynamicClassLoader extends ClassLoader { /** * 根據字節定義類 * 注意:對於用一個類加載器不能定義同一個類名兩次 * @param b * @return * @throws ClassNotFoundException */ public Class<?> findClass(byte[] b) throws ClassNotFoundException { return defineClass(null, b, 0, b.length); } }