2010-11-09 70 views
11

內:Is there a way to obtain the bytecode for a class at runtime?的Java:在運行時獲得類字節碼從與同一JVM

我加入耐久性的Clojure,我終於在這裏,我準備添加的功能點。在Clojure中,函數使用invoke方法(等等)將字節編譯爲類。這樣,功能就是一流的。爲了使這些持久化,我需要序列化和反序列化這些類。如何在不訪問.class文件的情況下獲取該類的字節碼?

如果我錯了,請糾正我的錯誤,但使用代理需要產生一個單獨的虛擬機並且代理連接到第一個虛擬機。我需要從同一個虛擬機來完成。

僅使用Serializable來設置和獲取Class對象是不夠的。在反序列化之後,我需要加載這個類,並且在後續的VM實例中,可能會出現類名衝突。我需要修改字節碼來將類重命名爲反序列化/類加載時唯一的東西。

+0

我絕不是這個話題的專家,但它可能是值得嘗試堅持函數*定義*而不是底層字節碼。你可以重新將函數重新編譯爲字節碼。 – mikera 2010-11-09 20:27:32

回答

4

你可以編寫自己的ClassLoader,並在加載類時記錄下字節碼的方案。

您需要覆蓋findClass才能自己查找類文件,將其加載到內存中,將數據保存到某處(以便稍後序列化),然後調用defineClass在JVM中定義該類。

+0

p.s.我認爲代理與主程序運行在同一個虛擬機上,所以它們可能會爲你工作。 – 2010-11-09 06:46:30

+0

Javassist已經實現了這個方案,並提供了一個方便的訪問有問題的字節碼。 BCEL也可能做類似的事情。 – Blaisorblade 2011-07-08 15:02:53

+0

最小的實現是相當微不足道的:https://gist.github.com/Felk/ed4375d27c755e21d0e6893847286d93(我知道這個職位是7歲) – Felk 2017-09-15 13:56:45

3

除非你是通過一個棘手的類加載器中運行的代碼,你應該能夠做這樣的事:如果

Class<?> clazz = .... 
String className = clazz.getCanonicalName(); // e.g. "foo.Bar" 
String resourceName = ... // map className to a resource name; e.g. "/foo/Bar.class" 
InputStream is = clazz.getClassLoader.getResourceAsStream(resourceName); 

這給你的「.class」的文件中的內容手柄...它可以被發現。

注意事項。有些類加載器可能:

  • 沒有讓所有打開的「.class」資源,
  • 給你一個加密的字節碼流,或
  • 給你的是字節碼,也沒有什麼是正在運行中,由於到由類加載器執行的一些即時轉換。

如果這種方法不起作用,您幾乎沒有選擇,因爲JVM不提供訪問加載的實際字節碼的方式。

+1

唉,這隻有在.class首先作爲資源寫入磁盤時纔有效。 Clojure不會這樣做。 – alyssackwan 2010-11-10 23:04:04

+0

這個類在另一個類中定義時也有問題。例如:'class Foo {class Bar {}}'應該變成「/Foo$Bar.class」 – iliis 2013-04-21 17:01:31

+1

'clazz.getResourceAsStream('/'+ clazz.getName()。replace('。','/')+ 「.class」)'適用於內部類,甚至具有'null' ClassLoader的引導類。 – Holger 2013-11-07 14:51:08

1

您也可以爲此使用Java Instrumentation API。您可以在調用defineClass之前訪問類文件的字節。你也可以改變它們!