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

Действительно ли возможно загрузить класс в Java и 'фальсифицировать' имя/каноническое имя пакета класса? Я пытался делать это, очевидный путь, но я получаю "имя класса, не соответствует" сообщению в a ClassDefNotFoundException.

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

./com/DefaultPackageClass.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); 
    }

}
12
задан C. Ross 24 May 2010 в 14:27
поделиться

3 ответа

Как упомянул Пит , это можно сделать с помощью библиотеки байт-кода ASM. Фактически, эта библиотека фактически поставляется с классом, специально предназначенным для обработки повторных отображений имен классов ( 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
Compiled from "Order.java"
public class Order extends java.lang.Object{
private Customer customer;

public Order(Customer);
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."":()V
   4:   aload_0
   5:   aload_1
   6:   putfield    #13; //Field customer:LCustomer;
   9:   return

public Customer getCustomer();
  Code:
   0:   aload_0
   1:   getfield    #13; //Field customer:LCustomer;
   4:   areturn

public void setCustomer(Customer);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #13; //Field customer:LCustomer;
   5:   return

}

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

> javap -private -c Order
Compiled from "Order.java"
public class com.mycompany.Order extends com.mycompany.java.lang.Object{
private com.mycompany.Customer customer;

public com.mycompany.Order(com.mycompany.Customer);
  Code:
   0:   aload_0
   1:   invokespecial   #30; //Method "com.mycompany.java/lang/Object"."":()V
   4:   aload_0
   5:   aload_1
   6:   putfield    #32; //Field customer:Lcom.mycompany.Customer;
   9:   return

public com.mycompany.Customer getCustomer();
  Code:
   0:   aload_0
   1:   getfield    #32; //Field customer:Lcom.mycompany.Customer;
   4:   areturn

public void setCustomer(com.mycompany.Customer);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #32; //Field customer:Lcom.mycompany.Customer;
   5:   return

}

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

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

  • принадлежат пакету по умолчанию, либо
  • используют другие классы, принадлежащие пакету по умолчанию.
16
ответ дан 2 December 2019 в 19:53
поделиться

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

0
ответ дан 2 December 2019 в 19:53
поделиться

У вас должно получиться кое-что с помощью ASM , хотя было бы проще переименовать пакет один раз во время сборки, а не во время загрузки.

1
ответ дан 2 December 2019 в 19:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: