Java_動態重新加載Class機制


Java動態重新加載Class 

    項目中使用到了動態重新加載Class的機制,作用是讓一些代碼上線之前可以在線上環境測試一下,當然,這是非常不好的測試機制,我剛來的時候也為這種機制感到驚訝—怎么可以在線上環境運行測試代碼!后來經過了解,這么做的原因有以下兩個: 

  • 有些代碼沒有辦法在本地進行測試,本地沒有線上的環境
  • 我們弱到連測試機都沒有(這是重點)


    既然我們連測試機都沒有,那么我就覺得我們的項目其實也沒有想象中的重要,這么測就這么測吧~~ 
    之前對ClassLoader沒啥概念,google到一篇文章,翻譯了一下並且做了一些補充,加深記憶 
原文地址: 

引用
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html#classloader


--------------------------------------------------- 
ClassLoader 
    顧名思義,ClassLoader就是用來Load Class的,當一個Class被加載的時候,這個Class所引用到的所有Class也會被加載,而且這種加載是遞歸的,也就是說,如果A引用到B,B 引用到C,那么當A被加載的時候,B也會被加載,而B被加載的時候,C也會加載。如此遞歸直到所有需要的Class都加載好。 
    常見的ClassLoader: 

引用
* Bootstrap class loader:虛擬機運行時必須要用到的類的加載器,比如java.*。它通常是在虛擬機種用本地代碼(如C)實現,在系統中用null表示。 
* Extension class loader:負責加載ext目錄下的Class。 
* Application class loader:負責加載CLASSPATH上的類。



ClassLoader的代理層次關系 
    ClassLoader是以層次關系組織起來的,當你創建一個標准的Java ClassLoader的時候,你必須提供一個父ClassLoader。當一個ClassLoader需要加載一個Class的時候,它首先會讓父 ClassLoader去加載這個Class,如果父ClassLoader不能加載這個Class,那么當前的ClassLoader才會自己去加載。 
    ClassLoader加載Class的步驟: 

  • 檢查這個Class是否已經被加載過了
  • 如果沒有被加載過,那么讓父ClassLoader嘗試去加載
  • 如果父ClassLoader無法加載,那么嘗試使用當前ClassLoader加載


    從ClassLoader加載Class的步驟可以得知,如果你需要動態重新加載一個Class,那么你的ClassLoader必須跟上述標准流程有所區別,需要動態加載的Class不能交給父ClassLoader,否則你自己的ClassLoader將沒有機會去加載這個Class(因為正常情況下父ClassLoader總是能加載到你所請求的Class)。 
    所以,如果你需要ClassLoader重新加載一個Class,重寫findClass方法是起不到效果的,因為findClass在父 ClassLoader加載失敗之后才會執行 

Java代碼   收藏代碼
  1. // First, check if the class has already been loaded  
  2.         Class c = findLoadedClass(name);  
  3.         if (c == null) {  
  4.         try {  
  5.         if (parent != null) {  
  6.             c = parent.loadClass(name, false);  
  7.         } else {  
  8.             c = findBootstrapClass0(name);  
  9.         }  
  10.         } catch (ClassNotFoundException e) {  
  11.             // If still not found, then invoke findClass in order  
  12.             // to find the class.  
  13.             c = findClass(name);  
  14.         }  
  15.     }  


    必須重寫loadClass方法才能達到效果。 

動態重新加載Class 
    Java內置的ClassLoader總會在加載一個Class之前檢查這個Class是否已經被加載過,已經被加載過的Class不會加載第二次。因此要想重新加載Class,我們需要實現自己的ClassLoader。 
    另外一個問題是,每個被加載的Class都需要被鏈接(link),這是通過執行ClassLoader.resolve()來實現的,這個方法是 final的,因此無法重寫。Resove()方法不允許一個ClassLoader實例link一個Class兩次,因此,當你需要重新加載一個 Class的時候,你需要重新New一個你自己的ClassLoader實例。 
    剛才說到一個Class不能被一個ClassLoader實例加載兩次,但是可以被不同的ClassLoader實例加載,這會帶來新的問題: 

Java代碼   收藏代碼
  1. MyObject object = (MyObject)  
  2.     myClassReloadingFactory.newInstance("com.jenkov.MyObject");  


這段代碼會導致一個ClassCastException,因為在一個Java應用中,Class是根據它的全名(包名+類名)和加載它的 ClassLoader來唯一標識的。在上面的代碼中object對象對應的Class和newInstance返回的實例對應的Class是有區別的: 

  全名 ClassLoader實例
Object對象的Class com.jenkov.MyObject  AppClassLoader實例
newInstance返回對象的Class com.jenkov.MyObject 自定義ClassLoader實例



    解決的辦法是使用接口或者父類,只重新加載實現類或者子類即可。 

Java代碼   收藏代碼
  1. MyObjectInterface object = (MyObjectInterface)  
  2.     myClassReloadingFactory.newInstance("com.jenkov.MyObject");  
  3.   
  4. MyObjectSuperclass object = ( MyObjectSuperclass)  
  5.     myClassReloadingFactory.newInstance("com.jenkov.MyObject");  



    在自己實現的ClassLoader中,當需要加載MyObjectInterface或者MyObjectSuperclass的時候,要代理給父 ClassLoader去加載。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM