[英]Is it possible to fake missing classes using ClassLoader?
我正在從實現公共 API 接口的 JAR 加載類。 接口本身將保持不變,但與 API 關聯的其他類可能會隨着時間而改變。 顯然,一旦 API 發生更改,我們將不再能夠支持使用舊版本編寫的接口的實現。 然而,一些接口方法提供了簡單的String
類型的元數據,我們可以假設這些元數據永遠不會改變,也永遠不會依賴 API 中可能改變的其他部分。 即使 API 發生變化,我也希望能夠提取此元數據。
例如,考慮以下可能加載的實現,其中Foo
是接口, Bar
是 API 中的另一個 class。 即使 class Bar
不再存在,我也想調用name
方法。
class MyFoo implements Foo {
Bar bar = null;
@Override public String name() {
return "MyFoo"
}
}
據我所知,顯而易見的方法是在我的自定義ClassLoader
中覆蓋loadClass(String name)
並為Bar
返回一些“假” class 。 可以假設元數據方法永遠不會創建或使用Bar
object。 問題是當被要求加載Bar
時如何生成這個“假” class 。 我考慮過以下方法:
Object.class
,但是當我嘗試實例化Foo
的實例時,這仍然會導致Bar
的NoClassDefFoundError
。Bar
並加載它。2. 和 3. 似乎都很復雜,所以我想知道是否有更簡單的方法來實現我的目標?
這是一個 class 加載程序,它將以非常簡單的方式為在搜索路徑上找不到的每個 class 創建一個虛擬 class:
public class DummyGeneratorLoader extends URLClassLoader {
public DummyGeneratorLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public DummyGeneratorLoader(URL[] urls) {
super(urls);
}
public DummyGeneratorLoader(
URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
super(urls, parent, factory);
}
static final byte[] template = ("Êþº¾\0\0\0002\0\n\1\7\0\1\1\0\20java/lang/Object"
+ "\7\0\3\1\0\6<init>\1\0\3()V\14\0\5\0\6\n\0\4\0\7\1\0\4Code\0\1\0\2\0\4\0"
+ "\0\0\0\0\1\0\1\0\5\0\6\0\1\0\t\0\0\0\21\0\1\0\1\0\0\0\5*·\0\b±\0\0\0\0\0\0")
.getBytes(StandardCharsets.ISO_8859_1);
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
return super.findClass(name);
}
catch(ClassNotFoundException ex) { }
return new ByteArrayOutputStream(template.length + name.length() + 10) { {
write(template, 0, 11);
try { new DataOutputStream(this).writeUTF(name.replace('.', '/')); }
catch (IOException ex) { throw new AssertionError(); }
write(template, 11, template.length - 11);
}
Class<?> toClass(String name) {
return defineClass(name, buf, 0, count); } }.toClass(name);
}
}
但是,虛擬 class 無法滿足的使用代碼可能會帶來很多期望或結構限制。 畢竟,在調用接口方法之前,你必須創建一個 class 的實例,所以它必須通過驗證並成功執行其構造函數。
如果方法確實具有假定的結構,例如public String name() { return "MyFoo"; }
public String name() { return "MyFoo"; }
使用 ASM 可能是更簡單的選擇,但不是生成任意復雜的假環境,而是解析這些方法並預測它們返回的常量值。 這種方法將僅包含兩個指令, ldc value
和areturn
。 您只需要檢查是否是這種情況並從第一條指令中提取值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.