2016-07-29 31 views
4
  • 我有一個類A擴展B.
  • 我創建了一個CustomClassLoader擴展ClassLoader使用defineClass(className, byte[], offset, length)
  • 我已經instanciate a new CustomClassLoader(Thread.currentThread().getContextClassLoader())。 因此,我的CustomClassLoader的父級是當前線程的ClassLoader。
  • 我使用ASM框架修改了B類。我已將修改後的類寫入.class文件,並使用反編譯器確保它可以正常工作。它的工作原理。
  • 我已將修改後的B類添加到我的CustomClassLoader中
  • 我已經使用我的CustomClassLoader設置了Thread.currentThread().setContextClassLoader()
  • 我已經使用Class.forName(String, true, the CustomClassLoader)加載A.
  • 但是加載的B類似乎是原始類。

我錯了什麼? 如果您需要更多信息,詳細的主題在我的GitHub上。如何在Java中加載修改的超類?

回答

2

Java類加載器第一個在查找子代之前搜索父類加載器。

稱爲加載一個類時,類加載器的loadClass方法執行這些任務,從而, :如果一個類已經被加載

  1. ,它返回它。
  2. 否則,它會將新類的搜索委託給父類加載器。
  3. 如果父類加載器未找到該類,loadClass會調用方法findClass來查找並加載該類。

Understanding Extension Class Loading - 甲骨文)

如果你想改變這個順序,你需要重寫loadClass方法很好,但也有很多注意事項,除非你理解類加載非常好這不可取。

  • 更簡單的選擇是確保父類加載器找不到原始類B
+0

「A」類未加載,因此ClassLoader子代表將一個類加載到ClassLoader父類。 但是B被加載到子中(修改後的版本被加載到子中),所以當A需要B時,因爲A擴展了B爲什麼B是從父加載的(加載原始的)? –

+0

你的意思是「但是B裝在小孩身上」?如果B存在於父類加載器中,那麼它不會加載到子類中,因爲沒有重載'loadClass'的每個類加載器首先委託給它的父類,並且只會嘗試將它加載到子類classlaoder中,如果它未找到父 –

+0

我必須在運行時更改B類字節碼。 因此,爲了保存我修改過的B類,我必須使用child.defineClass(),因爲parent.defineClass()不可見,並且原始B類可能存在於父類加載器中。 - B被修改,並裝載到子 - A從子加載 - >委託給父 - A延伸乙 - B似乎是從父 加載 - B不保存到母體,因此父裝載B使用URL我認爲 - 父裝入原始B類 - B裝載兩次:原始在父母和修改在孩子。 –

1

有幾件事情要知道:

  • 對於大多數的事情,處理線程的上下文類加載器是過時的,因爲它沒有影響。這更像是一個公約;如果有其他代碼查詢和使用它,它會產生影響。對於標準的類加載過程,它沒有任何意義。不幸的是,文檔沒有提到,並使其看起來像一個相關的東西。或許,它的目的是在增加時有更多的意義。
  • 由於pointed out by Erwin Bolwidt,當通過您的自定義加載器加載A時,它將委託給它的父裝載器,返回父裝載的類A
  • 解析類引用時,JVM將始終使用引用鏈接器的定義加載器的。所以,當從AB參考解析,JVM將始終使用該定義的類A

最後一點意味着父加載器,即使你修改自定義的類裝載器先查找自己的班級而不是遵循先查詢父代的標準模型,如果它沒有自己的A,那麼它並不解決問題,因爲它仍然返回父代的A,該代碼的引用將使用父代進行解析。由於要求A之前要調用defineClass,查找順序並不在所有問題,爲您的自定義裝載機具有已定義的B它返回,如果有人問起它B ...

所以,你可以讓你的自定義加載程序也加載並定義A。或者,在加載B之前,您可以在系統ClassLoader上使用Reflection訪問覆蓋defineClass。最簡潔的解決方案是將類修改邏輯實現爲Java代理,它可以使用Instrumentation API在加載時攔截並更改B的定義。