Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 实现选择性类加载器_Java_Classloader_Instrumentation - Fatal编程技术网

Java 实现选择性类加载器

Java 实现选择性类加载器,java,classloader,instrumentation,Java,Classloader,Instrumentation,我想在加载时在类路径上插入一些类的字节码。因为这些是第三方库,所以我确切地知道它们是何时加载的。问题是我需要有选择地进行插装,即只插装某些类。现在,如果我不使用我的类加载器加载一个类,而是使用它的父类加载一个类,那么这个父类将被设置为类加载器,并且所有简洁的类都由该父类加载,从而有效地使我的类加载器停止使用。所以我需要实现一个父类装入器(参见) 所以我需要自己加载类。如果这些类是系统类(以“java”或“sun”开头),我将委托给父类。否则,我读取字节码并调用defineClass(name,b

我想在加载时在类路径上插入一些类的字节码。因为这些是第三方库,所以我确切地知道它们是何时加载的。问题是我需要有选择地进行插装,即只插装某些类。现在,如果我不使用我的类加载器加载一个类,而是使用它的父类加载一个类,那么这个父类将被设置为类加载器,并且所有简洁的类都由该父类加载,从而有效地使我的类加载器停止使用。所以我需要实现一个父类装入器(参见)

所以我需要自己加载类。如果这些类是系统类(以“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);
    }
}
}
公共类检测ClassLoader扩展了ClassLoader{
专用最终字节码指令插入=新字节码指令插入();
@凌驾
公共类loadClass(字符串名称)引发ClassNotFoundException{
类结果=定义类(名称);
如果(结果!=null){
返回结果;
}
结果=findLoadedClass(名称);
如果(结果!=null){
返回结果;
}
结果=super.findClass(名称);
返回结果;
}
私有类defineClass(字符串名称)引发ClassFormatError{
字节[]byteBuffer=null;
if(instrumentation.willInstrument(名称)){
byteBuffer=仪表字节码(名称);
}
否则{
byteBuffer=getRegularByteCode(名称);
}
if(byteBuffer==null){
返回null;
}
Class result=defineClass(name,byteBuffer,0,byteBuffer.length);
返回结果;
}
专用字节[]getRegularByteCode(字符串名称){
if(name.startsWith(“java”)| | name.startsWith(“sun”)){
返回null;
}
试一试{
InputStream=ClassLoader.getSystemResourceAsStream(名称.replace('.','/')+“.class”);
ByteArrayOutputStream缓冲区=新建ByteArrayOutputStream();
国际nRead;
字节[]数据=新字节[16384];
而((nRead=is.read(data,0,data.length))!=-1){
写入(数据,0,nRead);
}
buffer.flush();
返回buffer.toByteArray();
}捕获(IOException){
返回null;
}
}
专用字节[]指令字节码(字符串fullyQualifiedTargetClass){
试一试{
String className=fullyQualifiedTargetClass.replace('.','/');
返回instrumentation.transformBytes(类名,新类读取器(fullyQualifiedTargetClass));
}捕获(例外e){
抛出新的运行时异常(e);
}
}
}
代码可通过以下方式执行:

    InstrumentingClassLoader instrumentingClassLoader = new InstrumentingClassLoader();
    Class<?> changedClass = instrumentingClassLoader.loadClass(ClassLoaderTestSubject.class.getName());
InstrumentingClassLoader InstrumentingClassLoader=新InstrumentingClassLoader();
Class changedClass=instrumentingClassLoader.loadClass(ClassLoaderTestSubject.Class.getName());

ClassLoaderTestSubject
应该调用一些其他类,其中被调用的类是插装的目标,但是
ClassLoaderTestSubject
本身不是…

我建议您使用常规类装入器策略,即先使用父类。但是,将要插入的所有类放入单独的jar文件中,不要将其添加到应用程序的类路径中。使用扩展URL类加载器的类加载器实例化这些类,并知道在其他位置搜索JAR。在这种情况下,所有JDK类都将被自动知道,您的代码将更简单。您不必“思考”是否插入类:如果父类加载器未加载该类,则必须插入的是您的类

愚蠢的错误。父类加载器不是继承层次结构中的父类。它是给定给构造函数的父级。因此,正确的代码如下所示:

public InstrumentingClassLoader() {
    super(InstrumentingClassLoader.class.getClassLoader());
    this.classLoader = InstrumentingClassLoader.class.getClassLoader();
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
    [... as above ...]
    result = classLoader.loadClass(name);
    return result;
}
public InstrumentingClassLoader(){
super(InstrumentingClassLoader.class.getClassLoader());
this.classLoader=InstrumentingClassLoader.class.getClassLoader();
}
@凌驾
公共类loadClass(字符串名称)引发ClassNotFoundException{
[……如上所述……]
结果=classLoader.loadClass(名称);
返回结果;
}

当然,你完全正确。如果没有任何其他限制,这将是最简单、最优雅的方式。然而,我必须解决手头的问题。。。