Динамическая загрузка класса в Java с другим именем пакета

StackOverflow https://stackoverflow.com/questions/2897685

Вопрос

Можно ли загрузить класс в Java и «поддельный» имя пакета / каноническое имя класса? Я попытался сделать это, очевидный способ, но я получаю сообщение «Название класса не совпадает» в ClassDefNotFoundException.

Причина, по которой я делаю это, я пытаюсь загрузить API, который был написан в упаковке по умолчанию, чтобы я мог использовать его напрямую без использования отражения. Код будет составлять против класса в структуре папки, представляющей пакет и импорт имени пакета. IE:

./com/defaultpackageClass.class.class.
// ...
import com.DefaultPackageClass;
import java.util.Vector;
// ...

Мой текущий код выглядит следующим образом:

public Class loadClass(String name) throws ClassNotFoundException {
    if(!CLASS_NAME.equals(name))
            return super.loadClass(name);

    try {
        URL myUrl = new URL(fileUrl);
        URLConnection connection = myUrl.openConnection();
        InputStream input = connection.getInputStream();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        int data = input.read();

        while(data != -1){
            buffer.write(data);
            data = input.read();
        }

        input.close();

        byte[] classData = buffer.toByteArray();

        return defineClass(CLASS_NAME,
                classData, 0, classData.length);

    } catch (MalformedURLException e) {
        throw new UndeclaredThrowableException(e);
    } catch (IOException e) {
        throw new UndeclaredThrowableException(e); 
    }

}
Это было полезно?

Решение

Как упомянул Пит, это можно сделать с помощью библиотеки ASM Bytecode. На самом деле, эта библиотека фактически судна с классом, специально для обработки этих повторных отображений имени класса (RemappingClassAdapter). Вот пример классового погрузчика, используя этот класс:

public class MagicClassLoader extends ClassLoader {

    private final String defaultPackageName;

    public MagicClassLoader(String defaultPackageName) {
        super();
        this.defaultPackageName = defaultPackageName;
    }

    public MagicClassLoader(String defaultPackageName, ClassLoader parent) {
        super(parent);
        this.defaultPackageName = defaultPackageName;
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        byte[] bytecode = ...; // I will leave this part up to you
        byte[] remappedBytecode;

        try {
            remappedBytecode = rewriteDefaultPackageClassNames(bytecode);
        } catch (IOException e) {
            throw new RuntimeException("Could not rewrite class " + name);
        }

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

    public byte[] rewriteDefaultPackageClassNames(byte[] bytecode) throws IOException {
        ClassReader classReader = new ClassReader(bytecode);
        ClassWriter classWriter = new ClassWriter(classReader, 0);

        Remapper remapper = new DefaultPackageClassNameRemapper();
        classReader.accept(
                new RemappingClassAdapter(classWriter, remapper),
                0
            );

        return classWriter.toByteArray();
    }

    class DefaultPackageClassNameRemapper extends Remapper {

        @Override
        public String map(String typeName) {
            boolean hasPackageName = typeName.indexOf('.') != -1;
            if (hasPackageName) {
                return typeName;
            } else {
                return defaultPackageName + "." + typeName;
            }
        }

    }

}

Чтобы проиллюстрировать, я создал два класса, оба из которых принадлежат к пакету по умолчанию:

public class Customer {

}

а также

public class Order {

    private Customer customer;

    public Order(Customer customer) {
        this.customer = customer;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

}

Это список Order до Любое повторное сопоставление:

> JAVAP -Private -C порядок, собранный из «Order.java», продлевает java.lang.Object {частный клиент клиента; общественный порядок (клиент); Код: 0: ALOD_0 1: Invoxial # 10; // Метод java / lang / объект "." :() v 4: Aload_0 5: Aload_1 6: Путфилд № 13; // Полевой клиент: lcustomer; 9: вернуть общественный клиент GetCustomer (); Код: 0: Aload_0 1: Getfield # 13; // Полевой клиент: lcustomer; 4: Areturn Public Void SetCustomer (Клиент); Код: 0: ALOD_0 1: ALOAD_1 2: Путфилд № 13; // Полевой клиент: lcustomer; 5: return}

Это список Order после Перераспределение (используя com.mycompany Как пакет по умолчанию):

> Javap -Private -c порядок, собранный из «OUTER.JAVA» общественного класса com.mycompany.order продлевает com.mycompany.java.lang.Object {Private Com.mycompany.customer Comportion; Public Com.mycompany.Order (com.mycompany.customer); Код: 0: ALOAD_0 1: Invoxial # 30; // Метод "com.mycompany.java/lang/object"."":() 4: Aload_0 5: Aload_1 6: Путфилд № 32; // Полевой клиент: lcom.mycompany.customer; 9: возврат общественности com.mycompany.customer getCustomer (); Код: 0: Aload_0 1: Getfield # 32; // Полевой клиент: lcom.mycompany.customer; 4: Areturn Public Void SetCustomer (com.mycompany.customer); Код: 0: ALOD_0 1: ALOAD_1 2: Putfield # 32; // Полевой клиент: lcom.mycompany.customer; 5: return}

Как вы можете видеть, перерабатывание изменилось все Order ссылки на com.mycompany.Order и все Customer ссылки на com.mycompany.Customer.

Этот классный погрузчик должен будет загрузить все классы, которые либо:

  • принадлежат к пакету по умолчанию или
  • Используйте другие классы, принадлежащие к пакету по умолчанию.

Другие советы

Вы должны быть в состоянии что-то стучать с КАК М, Однако было бы легче сделать пакет переименовать один раз в скорее время нагрузки, а не при времени загрузки.

Возможно, было бы легче переместить API из пакета по умолчанию в более разумное место? Похоже, у вас нет доступа к исходному коду. Я не уверен, что пакет закодирован в файлы классов, поэтому просто перемещение класса API, возможно, стоит попробовать. В противном случае Java Decompilers, как JAD, как правило, делают хорошую работу, поэтому вы можете изменить имя пакета в декомпилированном источнике и снова компилируйте его.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top