加载自定义类加载器以加载Java中已传输的byte [] [英] Loading custom class loader to load a transferred byte[] in java

查看:99
本文介绍了加载自定义类加载器以加载Java中已传输的byte []的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将整个.class文件(以及声明的类和匿名类)作为byte []进行传输,并希望能够使用接收到byte []的另一台计算机上的类加载器对其进行定义.

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[].

我在 Java中找到了解决方案:如何将存储为byte []的Class加载到JVM中?,但是,我不了解如何将ByteClassLoader用于我的目的.我只是将代码再次发布在这里:

I have found a solution at Java: How to load Class stored as byte[] into the JVM?, however, I don't understand how to use this ByteClassLoader for my purpose. 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);
}

}

问题是:我必须如何实例化它?

我试图通过简单地将其实例化为ByteClassLoader b = new ByteClassLoader(...)来使用它;但是很明显,这是行不通的(如预期的那样).

I tried to use it by simply instantiating it as 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).

我有一个带有方法deserialize()的类SerializeUtils,在其中实例化ByteClassLoader.由于它不适用于引用的实现,因此我尝试执行ByteClassLoader extends ClassLoader(我不知道从何处获取URL[]),然后更改该线程的ContextClassLoader:

I have a class SerializeUtils with a method deserialize() where I instantiate the 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:

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;
}

因此,我调用SerializeUtils.deserialize()对.class文件进行反序列化,以希望以后可以使用它.之后,我打电话

So I call SerializeUtils.deserialize() to deserialize the .class file in the hope of having it available afterwards. 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;
}

因此,我首先调用SerializeUtils.deserialize()对类文件进行反序列化,以期使它们可用于Utils.decodeJavaObject的下一次调用,在此我尝试对我传输的实际对象的byte []反序列化(所以发送要实例化的类的类文件,然后对我也通过网络发送的该类的对象进行解码). Task是我反序列化的类文件扩展的抽象类,因此需要反序列化其实现的程序才知道Task.顺便说一句,TransferObject看起来像这样:

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). 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). BTW, TransferObject looks like this:

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;
}

}

(数据=在另一台计算机上扩展了Task的序列化对象,classFiles =该扩展对象的序列化类文件,而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扩展看起来例如像这样:

And extension of Task on another computer looks e.g. like this:

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(); 
        }

    };

Task抽象类仅是以下代码:

And the Task abstract class is simply the following code:

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;
}

并扩展ObjectInputStream以在反序列化中使用此类加载器:

and extend ObjectInputStream to use this classloader in deserialization:

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;
}

这篇关于加载自定义类加载器以加载Java中已传输的byte []的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆