2009-11-23 3 views
23

Если вы сериализовали весь файл .class в byte [], и если имя класса известно (передано вместе с байтом []), как вы преобразовываете байт [] -> Class ->, то загрузите его в JVM, чтобы впоследствии использовать его, вызвав Class.forName()?Java: Как загрузить класс, сохраненный в качестве байта [] в JVM?

Примечание: Я делаю это, потому что я послал .class к другому хозяину, и JVM хоста не знает об этом .class.

ответ

25

Я на самом деле использовать что-то вроде этого прямо сейчас в тесте, чтобы дать набор определений класса в байт [] в ClassLoader:

public static 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); 
    } 

    } 
+0

Alex, но что, если 2 .класс зависят друг от друга? будет ли он работать? – sivabudh

+0

Наверное, нет. Вероятно, вы должны отправлять (например) JAR-файл, а не отдельные файлы .class. –

+3

@ ShaChris23 - конечно, почему бы и нет? @ Stephen C - это похоже на ограниченное представление о том, что могут выполнять Java-загрузчики классов. Почему вы говорите, вероятно, нет? –

4

Протяните ClassLoader, а затем выполните необходимый звонок, чтобы получить byte[] внутри метода defineClass(). И наоборот, вы можете сделать это в findClass(). Таким образом, вы загружаете этот ClassLoader в начале или используете его, когда вам нужны определения классов через ваш массив.

public class ByteArrayClassLoader extends ClassLoader { 

    public Class findClass(String name) { 
     byte[] ba = /* go obtain your byte array by the name */; 

     return defineClass(name,ba,0,ba.length); 
    } 

} 
+0

Вам также не нужно называть resolveClass()? – sivabudh

1

Итак, вот что я сделал:

public class AgentClassLoader extends ClassLoader 
{ 
    public void loadThisClass(ClassByte classByte_) 
    { 
    resolveClass(defineClass(classByte_.getName(), 
          classByte_.getBytes(), 
          0, 
          classByte_.getBytes().length)); 
    } 
} 

Я надеюсь, что загружает класс в JVM? Если это действительно работает, почему разработчик Java не делает общедоступными методы defineClass и resolveClass? Мне действительно интересно, о чем они думают. Кто-нибудь может просветить меня?

Просто для полноты картины, вот ClassByte

import java.io.Serializable; 

public class ClassByte implements Serializable 
{ 
    private String name; 
    private byte[] bytes; 

    ClassByte(String name_, byte[] bytes_) 
    { 
    name = name_; 
    bytes = bytes_; 
    } 

    public String getName() 
    { 
    return name; 
    } 

    public byte[] getBytes() 
    { 
    return bytes; 
    } 
} 
+1

Я думаю, что идея состоит в том, чтобы отвратить случайный код от вызова 'defineClass' на загрузчиках класса system/default. Это может поставить под угрозу безопасность и, как правило, сделать очень странные вещи. Лучше разрешить это только на пользовательских загрузчиках классов. –

0

Ответ Alex правильный, но если у вас есть отношения наследования между классами, вам нужно сначала загрузить родительский класс. В противном случае загрузчик классов не сможет определить его.

+0

Действительно? Я думаю, что это проблема связи, а не проблема определения класса. Классы не обязательно должны быть определены в правильном порядке, иначе мы не могли бы иметь круговые зависимости в классах. Например, Class.getName() возвращает String, но String.getClass() возвращает класс. Кроме того, оба класса имеют родительский объект Object, но Object.toString() и Object.getClass() возвращают String и Class соответственно. Если бы вы сказали, что это правда, не будет правильного порядка загрузки этих классов. – Kidburla

+0

@ Kidburla, потому что JVM загружает классы, когда они необходимы, поэтому вы можете загрузить Class без необходимости загрузки String, а String будет загружаться при вызове 'getName'. Тем не менее, суперкласс/интерфейсы должны быть загружены или перед загружаемым классом, иначе класс будет неполным. Вот почему мы не можем иметь циклическое наследование, потому что загрузчики классов не знают, как с этим бороться. –

+0

Правда.Извините, я неправильно понял «наследование» как «зависимое» из-за комментариев к принятому ответу. – Kidburla

Смежные вопросы