加载自定义类加载器以在java

时间:2016-02-27 07:34:58

标签: java bytearray classloader .class-file

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

我在Java: How to load Class stored as byte[] into the JVM?找到了解决方案,但是,我并不了解如何将此ByteClassLoader用于我的目的。我只是再次在这里发布代码:

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(...)来使用它;但显然,这不起作用(如预期的那样)。

我找到了一种使用-Djava.system.class.loader =...ByteClassLoaderhttps://analyzejava.wordpress.com/2014/09/25/java-classloader-loading-a-custom-classloader-on-jvm-start/程序开头加载新类加载器的方法,但是,这也没有用(它没有找到类ByteClassLoader如果有人可以指出我如何使用引用的类加载器(我对类加载器非常新,并且尚未完全理解它们如何工作),那对我来说将非常有帮助。

编辑: 我有一个类SerializeUtils,带有deserialize()方法,我在其中实例化ByteClassLoader。由于它不适用于引用的实现,我尝试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;
}

我希望实现的是转换后的字节数组现在可用于执行,这样我就可以实例化它。我需要这个,因为我也使用和传递对象的实际数据作为序列化字节数组,在我加载类之后,将解码此对象并使用它:

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文件,希望以后可以使用它。在那之后,我打电话给

所以完整的反序列化过程如下所示:

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。 BTW,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 =序列化对象在另一台计算机上扩展Task,classFiles =该扩展对象的序列化类文件,className是扩展类的实际名称)

另一台计算机上的任务扩展例如像这样:

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抽象类只是以下代码:

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

对于复杂的解释感到抱歉......

1 个答案:

答案 0 :(得分:0)

未经测试,这是我的建议:

为每个定义映射创建一个类加载器:

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以在反序列化中使用此类加载器:

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