简体   繁体   English

加载自定义类加载器以加载Java中已传输的byte []

[英]Loading custom class loader to load a transferred byte[] in java

I transfer a whole .class file (and it's declared and anonymous classes) as a byte[] and want to be able to define it using the class loader on another computer that receives the byte[]. 我将整个.class文件(以及已声明的类和匿名类)作为byte []进行传输,并希望能够使用接收到byte []的另一台计算机上的类加载器对其进行定义。

I have found a solution at Java: How to load Class stored as byte[] into the JVM? 我在Java上找到了一个解决方案:如何将存储为byte []的Class加载到JVM中? , however, I don't understand how to use this ByteClassLoader for my purpose. ,但是,我不知道如何为我的目的使用此ByteClassLoader。 I just post the code here again: 我只是在这里再次发布代码:

public class ByteClassLoader extends URLClassLoader {
private final Map<String, byte[]> extraClassDefs;

public ByteClassLoader(URL[] urls, ClassLoader parent, Map<String, byte[]> extraClassDefs) {
    super(urls, parent);
    this.extraClassDefs = new HashMap<String, byte[]>(extraClassDefs);
}

@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
    byte[] classBytes = this.extraClassDefs.remove(name);
    if (classBytes != null) {
        return defineClass(name, classBytes, 0, classBytes.length);
    }
    return super.findClass(name);
}

} }

The question is: how do I have to instantiate it? 问题是:我必须如何实例化它?

I tried to use it by simply instantiating it as ByteClassLoader b = new ByteClassLoader(...) ; 我试图通过简单地将其实例化为ByteClassLoader b = new ByteClassLoader(...)来使用它ByteClassLoader b = new ByteClassLoader(...) ; but obviously, this is not working (as expected). 但是显然,这是行不通的(按预期)。

I found a way of loading a new class loader at the beginning of the program at https://analyzejava.wordpress.com/2014/09/25/java-classloader-loading-a-custom-classloader-on-jvm-start/ using -Djava.system.class.loader =...ByteClassLoader , however, this did not work either (it did not find the class ByteClassLoader . It would be very helpful to me if somebody could point me into the right direction of how to use the referenced class loader (I'm very new to class loaders and do not yet fully understand how they work). 我在https://analyzejava.wordpress.com/2014/09/25/java-classloader-loading-a-custom-classloader-on-jvm-start的程序开头找到了一种加载新类加载器的方法/使用-Djava.system.class.loader =...ByteClassLoader ,但是,这也不起作用(它没有找到ByteClassLoader类。如果有人可以指出正确的方向,这对我非常有帮助使用引用的类加载器(我是类加载器的新手,还没有完全了解它们的工作原理)。

Edit: I have a class SerializeUtils with a method deserialize() where I instantiate the ByteClassLoader . 编辑:我有一个带有方法ByteClassLoader ()的类SerializeUtils ,在其中实例化ByteClassLoader As it did not work with the referenced implementation, I tried to do ByteClassLoader extends ClassLoader (I did not know where to get the URL[] from) and then change the ContextClassLoader of that thread: 由于它不适用于引用的实现,因此我尝试做ByteClassLoader extends ClassLoader (我不知道从哪里获取URL[] ),然后更改该线程的ContextClassLoader

public static Map<String, Class<?>> deserialize(Map<String, byte[]> classesToDefine) {

    // SerializeUtils.class.getClassLoader().
    ByteClassLoader l = new ByteClassLoader(Thread.currentThread().getContextClassLoader(), classesToDefine); // TODO this
                                                                // may be a
                                                                // problem
    Thread.currentThread().setContextClassLoader(l);
    Map<String, Class<?>> classes = new HashMap<>();
    for (String className : classesToDefine.keySet()) {
        try {
            System.out.println("ClassName in deserialize: "+ className);
            Class<?> c = ((ByteClassLoader)Thread.currentThread().getContextClassLoader()).findClass(className);
            System.out.println("Class found is : " + c.getName());
            classes.put(className, c);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    return classes;
}

What I hoped to achieve is that the converted byte array is now available for the execution, such that I may be able to instantiate it. 我希望实现的是,转换后的字节数组现在可用于执行,这样我就可以实例化它。 I need this as I am also passing the actual data of the object as a serialized byte array using and, right after I loaded the class, would decode this object and use it using: 我需要这个,因为我还使用传递了对象的实际数据作为序列化的字节数组,并且在我加载该类之后,将对该对象进行解码并使用以下代码:

public static Object decodeJavaObject(byte[] me, int offset, int length) throws ClassNotFoundException,
        IOException {
    ByteArrayInputStream bais = new ByteArrayInputStream(me, offset, length);
    ObjectInputStream ois = new ObjectInputStream(bais);
    Object obj = ois.readObject();
    // no need to call close of flush since we use ByteArrayInputStream
    return obj;
}

So I call SerializeUtils.deserialize() to deserialize the .class file in the hope of having it available afterwards. 因此,我调用SerializeUtils.deserialize()对.class文件进行反序列化,以期以后可以使用它。 Right after that, I call 之后,我打电话

So the complete deserialization process looks as follows: 因此,完整的反序列化过程如下所示:

public static Job deserialize(JobTransferObject jobToDeserialize) throws ClassNotFoundException, IOException {
    Job job = new Job();
    for (TransferObject taskTransferObject : jobToDeserialize.taskTransferObjects()) {
        SerializeUtils.deserialize(taskTransferObject.classFiles());
        Task task = (Task) Utils.decodeJavaObject(taskTransferObject.data(), 0, taskTransferObject.data().length);
        job.addTask(task);
    }

    return job;
}

So I first call SerializeUtils.deserialize() to deserialize the class files in the hope of having them available for the next invocation of Utils.decodeJavaObject where I try to deserialize the byte[] of the actual object I transferred (so I send the class files of the class I want to instantiate and then decode an object of that class I also sent over the network). 因此,我首先调用SerializeUtils.deserialize()对类文件进行反序列化,以期使它们可用于下一次对Utils.decodeJavaObject的调用,在此我尝试对所传输的实际对象的byte []反序列化(因此,我发送了类我要实例化的类的文件,然后对我也通过网络发送的该类的对象进行解码)。 Task is an abstract class that the class file I deserialize extends, so Task is known to the program that needs to deserialize the implementation of it). Task是我反序列化的类文件扩展的抽象类,因此需要对它进行反序列化的程序知道Task。 BTW, TransferObject looks like this: 顺便说一句,TransferObject看起来像这样:

public class TransferObject implements Serializable{
/**
 * 
 */
private static final long serialVersionUID = 8971732001157216939L;
private byte[] data;
private Map<String, byte[]> classFiles;
private String className;

public TransferObject(byte[] data, Map<String, byte[]> classFiles, String className) {
    this.data = data;
    this.classFiles = classFiles;
    this.className = className;
}

public byte[] data() {
    return this.data;
}

public Map<String, byte[]> classFiles() {
    return this.classFiles;
}

public String className() {
    return this.className;
}

} }

(data = serialised object extending Task on another computer, classFiles = the serialised class files of that extended object, and className the actual name of the extended class) (数据=另一台计算机上扩展了Task的序列化对象,classFiles =该扩展对象的序列化类文件,而className是扩展类的实际名称)

And extension of Task on another computer looks eg like this: 而在另一台计算机上的Task扩展看起来像这样:

Task initShutdown = new Task(writeTask.currentId(), NumberUtils.next()) {

        /**
         * 
         */
        private static final long serialVersionUID = -5543401293112052880L;

        @Override
        public void broadcastReceiver(NavigableMap<Number640, Data> input, DHTConnectionProvider dht)
                throws Exception {
            dht.shutdown(); 
        }

    };

And the Task abstract class is simply the following code: Task抽象类就是下面的代码:

public abstract class Task implements Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9198452155865807410L;
private final Number640 previousId;
private final Number640 currentId;

public Task(Number640 previousId, Number640 currentId) {
    this.previousId = previousId;
    this.currentId = currentId;
}

public abstract void broadcastReceiver(NavigableMap<Number640, Data> input, DHTConnectionProvider dht)
        throws Exception;


public Number640 currentId() {
    return this.currentId;
}

public Number640 previousId() {
    return this.previousId;
}}

Sorry for the complicated explanation... 抱歉,解释很复杂...

Without having tested it, here is my suggestion: 未经测试,这是我的建议:

Create a single classloader per definition map: 为每个定义图创建单个类加载器:

public static Job deserialize(JobTransferObject jobToDeserialize) throws ClassNotFoundException, IOException {
    Job job = new Job();
    for (TransferObject taskTransferObject : jobToDeserialize.taskTransferObjects()) {
        ClassLoader cl = new ByteClassLoader(new URL[0], Task.class.getClassLoader(), taskTransferObject.classFiles());
        Task task = (Task)Utils.decodeJavaObject(taskTransferObject.data(), 0, taskTransferObject.data().length, cl);
        job.addTask(task);
    }
    return job;
}

and extend ObjectInputStream to use this classloader in deserialization: 并扩展ObjectInputStream以在反序列化中使用此类加载器:

public static Object decodeJavaObject(byte[] me, int offset, int length, final ClassLoader cl) throws ClassNotFoundException, IOException {
    ByteArrayInputStream bais = new ByteArrayInputStream(me, offset, length);
    ObjectInputStream ois = new ObjectInputStream(bais) {
        @Override
        protected Class<?> resolveClass(ObjectStreamClass objectStreamClass)
                throws IOException, ClassNotFoundException {

            Class<?> clazz = Class.forName(objectStreamClass.getName(), false, cl);
            if (clazz != null) {
                return clazz;
            } else {
                return super.resolveClass(objectStreamClass);
            }
        }

        @Override
        protected Class<?> resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
            Class<?>[] interfaceClasses = new Class[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                interfaceClasses[i] = Class.forName(interfaces[i], false, cl);
            }
            try {
                return Proxy.getProxyClass(cl, interfaceClasses);
            } catch (IllegalArgumentException e) {
                return super.resolveProxyClass(interfaces);
            }
        }
    };
    Object obj = ois.readObject();
    return obj;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM