Frage

Ist es möglich, die Paketnamen / kanonische Name einer Klasse eine Klasse in Java und ‚fake‘ zu laden? Ich habe versucht, dies zu tun, die offensichtliche Art und Weise, aber ich habe einen in einem ClassDefNotFoundException Meldung „Klassenname nicht übereinstimmt“.

Der Grund, warum ich das tue ist Ich versuche, eine API zu laden, die in der Standard-Paket geschrieben wurde, so dass ich es direkt ohne Reflexion verwenden können. Der Code wird kompiliert gegen die Klasse in einer Ordnerstruktur darstellt, das Paket und einen Paketnamen importieren. dh:

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

Mein aktueller Code ist wie folgt:

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

}
War es hilfreich?

Lösung

Wie Pete erwähnt , dies kann die ASM-Bytecode-Bibliothek durchgeführt werden. In der Tat, dass Bibliothek tatsächlich Schiffe mit einer Klasse speziell für diese Klassennamen Wiederzuordnungen für den Umgang mit ( RemappingClassAdapter ). Hier ist ein Beispiel für eine Klassenlader der Verwendung dieser Klasse:

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

    }

}

Zur Veranschaulichung habe ich zwei Klassen, die beide auf dem Standardpaket gehören:

public class Customer {

}

und

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

}

Dies ist die Auflistung von Order vor jeder Neuzuordnung:

> 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

}

Dies ist die Auflistung von Order nach Remapping (mit com.mycompany als Standard-Paket):

> 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

}

Wie Sie sehen können, hat sich die Neuzuordnung alle Order Verweise auf com.mycompany.Order und alle Customer Verweise auf com.mycompany.Customer geändert.

Diese Klasse Lader müssten alle Klassen geladen werden, dass entweder:

  • , gehören in den Standard-Paket, oder
  • verwenden, um andere Klassen, die zum Standardpaket gehören.

Andere Tipps

Es soll möglich sein, etwas zu klopfen mit ASM , obwohl es einfacher wäre, um das Paket zu tun umbenennen einmal zum Zeitpunkt der Erstellung anstatt zur Ladezeit.

Vielleicht wäre es einfacher, die API aus dem Standard-Paket in einen angemesseneren Ort zu bewegen? Es klingt wie Sie keinen Zugriff auf den Quellcode haben. Ich bin nicht sicher, ob das Paket in Klassendateien codiert ist, so einfach die API-Klasse bewegt wert sein könnte versuchen. Andernfalls Java Decompiler wie JAD in der Regel einen guten Job machen, so dass Sie die Paketnamen in der dekompiliert Quelle ändern könnten und es erneut kompilieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top