我想在加載時檢測類路徑上某些類的字節碼。由於這些是第三方庫,我知道它們何時加載。問題是我需要有選擇地進行儀器儀表,即儀器只有一些類。現在,如果我沒有用我的類加載器加載一個類,而是用它的父類加載一個類,那麼這個父類會被設置爲類的類加載器,並且所有簡潔的類都由該父類加載,從而使我的類加載器無用。所以我需要實現一個父 - 最後一個類加載器(請參閱How to put custom ClassLoader to use?)。實現選擇性類加載器
所以我需要自己加載類。如果這些類是系統類(以「java」或「sun」開頭),則委託給父級。否則,我讀取字節碼並致電defineClass(name, byteBuffer, 0, byteBuffer.length);
。但現在引發了java.lang.ClassNotFoundException: java.lang.Object
。
下面是代碼,高度讚賞任何評論:
public class InstrumentingClassLoader extends ClassLoader {
private final BytecodeInstrumentation instrumentation = new BytecodeInstrumentation();
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> result = defineClass(name);
if (result != null) {
return result;
}
result = findLoadedClass(name);
if(result != null){
return result;
}
result = super.findClass(name);
return result;
}
private Class<?> defineClass(String name) throws ClassFormatError {
byte[] byteBuffer = null;
if (instrumentation.willInstrument(name)) {
byteBuffer = instrumentByteCode(name);
}
else {
byteBuffer = getRegularByteCode(name);
}
if (byteBuffer == null) {
return null;
}
Class<?> result = defineClass(name, byteBuffer, 0, byteBuffer.length);
return result;
}
private byte[] getRegularByteCode(String name) {
if (name.startsWith("java") || name.startsWith("sun")) {
return null;
}
try {
InputStream is = ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + ".class");
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
return buffer.toByteArray();
} catch (IOException exc) {
return null;
}
}
private byte[] instrumentByteCode(String fullyQualifiedTargetClass) {
try {
String className = fullyQualifiedTargetClass.replace('.', '/');
return instrumentation.transformBytes(className, new ClassReader(fullyQualifiedTargetClass));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
的代碼可以例如執行搭配:
InstrumentingClassLoader instrumentingClassLoader = new InstrumentingClassLoader();
Class<?> changedClass = instrumentingClassLoader.loadClass(ClassLoaderTestSubject.class.getName());
的ClassLoaderTestSubject
應該調用一些其他類,其中稱爲類儀表的目標,但ClassLoaderTestSubject
本身是不是...
當然,你是完全正確的。沒有任何其他限制,這將是最簡單和最優雅的方式。然而,我必須解決手頭的問題...... – roesslerj