1、熱部署是什么?
對於Java應用程序來說,熱部署就是在運行時更新Java類文件。
2、熱部署有什么用?
可以不重啟應用的情況下,更新應用。舉個例子,就像電腦可以在不重啟的情況下,更換U盤。
OSGI也正是因為它的模塊化和熱部署,才顯得熱門。
3、熱部署的原理是什么?
想要知道熱部署的原理,必須要了解java類的加載過程。一個java類文件到虛擬機里的對象,要經過如下過程。
首先通過java編譯器,將java文件編譯成class字節碼,類加載器讀取class字節碼,再將類轉化為實例,對實例newInstance就可以生成對象。
類加載器ClassLoader功能,也就是將class字節碼轉換到類的實例。
在java應用中,所有的實例都是由類加載器,加載而來。
一般在系統中,類的加載都是由系統自帶的類加載器完成,而且對於同一個全限定名的java類(如com.csiar.soc.HelloWorld),只能被加載一次,而且無法被卸載。
這個時候問題就來了,如果我們希望將java類卸載,並且替換更新版本的java類,該怎么做呢?
既然在類加載器中,java類只能被加載一次,並且無法卸載。那是不是可以直接把類加載器給換了?答案是可以的,我們可以自定義類加載器,並重寫ClassLoader的findClass方法。想要實現熱部署可以分以下三個步驟:
1、銷毀該自定義ClassLoader
2、更新class類文件
3、創建新的ClassLoader去加載更新后的class類文件。
示例代碼如下:
1 package com.csair.soc.hotswap; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 /** 6 * 自定義類加載器,並override findClass方法 7 */ 8 public class MyClassLoader extends ClassLoader{ 9 @Override 10 public Class<?> findClass(String name) throws ClassNotFoundException{ 11 try{ 12 String fileName = name.substring(name.lastIndexOf("." )+1) + ".class" ; 13 InputStream is = this.getClass().getResourceAsStream(fileName); 14 byte[] b = new byte[is.available()]; 15 is.read(b); 16 return defineClass(name, b, 0, b. length); 17 } catch(IOException e){ 18 throw new ClassNotFoundException(name); 19 } 20 } 21 }
需要更新的類文件:
1 package com.csair.soc.hotswap; 2 public class HelloWorld { 3 public void say(){ 4 System. out.println( "Hello World V1"); 5 } 6 }
在工程的根目錄下,生成V2版本的HelloWorld.class,內容如下。
1 package com.csair.soc.hotswap; 2 public class HelloWorld { 3 public void say(){ 4 System. out.println( "Hello World V2"); 5 } 6 }
測試主程序
1 package com.csair.soc.hotswap; 2 3 import java.io.File; 4 import java.lang.reflect.Method; 5 6 public class Hotswap { 7 public static void main(String[] args) throws Exception { 8 loadHelloWorld(); 9 // 回收資源,釋放HelloWorld.class文件,使之可以被替換 10 System. gc(); 11 Thread. sleep(1000);// 等待資源被回收 12 File fileV2 = new File( "HelloWorld.class"); 13 File fileV1 = new File( 14 "bin\\com\\csair\\soc\\hotswap\\HelloWorld.class" ); 15 fileV1.delete(); //刪除V1版本 16 fileV2.renameTo(fileV1); //更新V2版本 17 System. out.println( "Update success!"); 18 loadHelloWorld(); 19 } 20 21 public static void loadHelloWorld() throws Exception { 22 MyClassLoader myLoader = new MyClassLoader(); //自定義類加載器 23 Class<?> class1 = myLoader 24 .findClass( "com.csair.soc.hotswap.HelloWorld");//類實例 25 Object obj1 = class1.newInstance(); //生成新的對象 26 Method method = class1.getMethod( "say"); 27 method.invoke(obj1); //執行方法say 28 System. out.println(obj1.getClass()); //對象 29 System. out.println(obj1.getClass().getClassLoader()); //對象的類加載器 30 } 31 }
輸出結果:
Hello World V1
class com.csair.soc.hotswap.HelloWorld
com.csair.soc.hotswap.MyClassLoader@bfc8e0
Update success!
Hello World V2
class com.csair.soc.hotswap.HelloWorld
com.csair.soc.hotswap.MyClassLoader@860d49
根據結果可以看到,在沒有重啟應用的情況下,成功的更新了HelloWorld類。
以上只是熱部署的最簡單的原理實踐,實際情況會復雜的多。OSGI的最關鍵理念就是應用模塊(bundle)化,對於每一個bundle,都有其自己的類加載器,當需要更新bundle時,把bundle和它的類加載器一起替換掉,就可以實現模塊的熱替換。
參考資料
深入理解java虛擬機
深入探討 Java 類加載器 http://www.ibm.com/developerworks/cn/java/j-lo-classloader/
