2011-07-13 14 views
49

一個類可以在運行時爲其自身添加一個方法(如從static塊),以便如果某人正在對此類執行反射,即使它未在編譯時定義,他們也會看到新方法?Java類可以在運行時爲其本身添加一個方法嗎?

背景:

甲框架我使用預計Action類被定義爲具有doAction(...)方法,按照慣例。框架在運行時檢查這些類,以查看它們的doAction()方法中可用的參數類型。例如:doAction(字符串一個,整數 B)

我想每個類能夠與各種參數編程產生其doAction()方法,剛剛在時間,當它被檢測。該方法的主體可以是空的。

+1

這將是真棒,如果它可以! :) – Nicholas

+0

是的,這是可能的。檢查此問題:http://stackoverflow.com/questions/17323500/aspect-weaving-at-runtime –

+0

據我所知,aop可以用於像日誌記錄的事情,但OP要求框架識別某類處理的類基於一種方法的存在。換句話說,可以使用AOP添加方法簽名嗎? – Quaternion

回答

50

這並不簡單。一旦類由類加載器加載,就無法更改加載的類的方法。當一個類被請求時,一個類加載器將加載它並且鏈接它。沒有辦法(用Java)來改變鏈接的代碼或添加/刪除方法。

我想到的唯一技巧就是玩類加載器。如果我們刪除一個自定義類加載器,那麼該類加載器加載的類應該被刪除或無法訪問。這在我腦海的想法是

  1. 實現與自定義類加載一個自定義類加載
  2. 負荷動態
  3. 如果我們有這個類的一個更新版本,
  4. 刪除自定義類加載器和
  5. 使用自定義類加載器的新實例加載此類的新版本

我把它留作食物,不能證明,如果這導致了一個解決方案,或者如果我們有陷阱。

作爲一個簡單的回答:不,我們不能改變一個加載的類,就像我們可以用反射來改變字段的內容。 (我們也不能添加或刪除字段)。

+0

任何人都曾嘗試過,並在實踐中使用它? – Norswap

+0

當然,你可以做到這一點,但你也必須重新加載所有針對這個編譯的類。 –

+0

這是一個很好的方法。初學者可能很複雜。 –

6

我從來沒有嘗試過像這樣的事情,但你應該看看ASMcglibJavassist

+1

和其他答案中提到的自定義類加載器。 – porcoesphino

4

不,這在Java中不是(很容易)。

這聽起來像你正在嘗試使用Java,就好像它是一種動態編程語言。例如,Ruby有公開的類:您可以在運行時從Ruby類添加和刪除方法。在Ruby中,你也可以在你的類中有一個「缺少方法」的方法,當你試圖調用一個類中不存在的方法時,這個方法會被調用。 Java中也不存在這樣的事情。

有一個在JVM JRuby上運行的Ruby版本,它必須執行非常困難的技巧才能使開放類在JVM上工作。

+0

這很容易與字節碼操作庫,如ASM –

+0

@AngsumanChakraborty我不會稱之爲「簡單」。你必須知道字節碼是什麼以及它是如何工作的。關鍵是Java沒有像Ruby的開放類那樣的標準功能。 – Jesper

+0

使用與Java很好集成的Groovy可以輕鬆實現 –

2

您可以有一個doAction方法,它可以執行任何您希望生成的方法執行的操作。是否有它需要生成的原因,或者它可以是動態的?

0

我不確定這是可能的。但是,您可以使用AspectJ,ASM等,並將這些方法編織到適當的類中。

另一種選擇是使用組合來包裝目標類並提供doAction方法。在這種情況下,你最終將委派給目標類。

1

我相信你需要一些改變字節碼的工具/框架,比如asm,cglib或javassist。 你可以通過方面/編織像Spring完成這個,但我相信你仍然需要首先定義方法。

19

Andres_D是正確的,我們能夠很好地做到這一點使用自定義類加載,這裏是如何做到這一點的詳細指南:http://www.javaworld.com/javaworld/jw-06-2006/jw-0612-dynamic.html?page=1

的文章解釋瞭如何編寫動態Java代碼。它討論了運行時源代碼編譯,類重新加載以及代理設計模式的使用,以便對其調用者透明的動態類進行修改。

事實上,奧地利的研究人員編寫了一個JVM,甚至允許重新加載具有不同類型層次結構的類。他們通過使用現有的線程保存點來生成一個對象的完整「邊宇宙」以及所有它的相關引用和引用內容,然後一旦完全重新整理所有必需的更改,只需交換所有更改的類即可實現此目的。 [1]這裏的一個鏈接到他們的項目http://ssw.jku.at/dcevm/,oracle的贊助肯定會對未來的計劃產生有趣的猜測。

到方法體和田地較少干擾的變化已經在可能使用JPDA的熱插拔功能,標準的Java虛擬機中的Java 1.4中引入:
docs.oracle.com/javase/1.4.2/docs/ guide/jpda/enhancements.html#hotswap

我不確定它是否是第一個,但是這位Sun員工的文章從2001年開始似乎是提及HotSpot熱插拔功能的早期建議之一。 [2]

參考

[1] T.Würthinger,C.溫默,和L.施泰德,「動態代碼演變爲Java,」在上的原則和實踐的第八屆國際會議上提出編程在Java中,維也納2010年。

M. Dmitriev,「朝着Java語言應用程序的運行時演化的靈活和安全的技術,」在OOPSLA工程複雜的面向對象的系統進化的研討會,2001年。

1

代理可能會有所幫助。但是每次要添加或刪除方法時都必須實例化代理。

0

它看起來像沒有辦法動態地添加方法。但是你可以用方法的列表或者像哈希準備類:

import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 
import java.lang.reflect.Modifier; 
import java.util.HashMap; 

public class GenericClass { 
    private HashMap<String, Method> methodMap = new HashMap<String, Method>(); 

    public Object call(String methodName,Object ...args) 
       throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { 
     Method method = methodMap.get(methodName); 
     return method.invoke(null, args); 
    } 

    public void add(String name,Method method){ 
     if(Modifier.isStatic(method.getModifiers())) 
      methodMap.put(name, method); 
    } 

    public static void main(String[] args) { 
     try { 
      GenericClass task = new GenericClass(); 
      task.add("Name",Object.class.getMethod("Name", new Class<?>[0])); 
     } catch (NoSuchMethodException | SecurityException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

比,使用反射可以設置或取消的屬性。

相關問題