2010-05-24 16 views
9

Java'da bir sınıf yüklemek ve bir sınıfın paket adını/kurallı adını 'sahte' yüklemek mümkün mü? Bunu yapmayı denedim, açık yol, ama bir "sınıf adı eşleşmiyor" iletisini ClassDefNotFoundException numaralı telefondan aldım.Farklı bir paket adıyla java'da dinamik yükleme

Bunu yapmamın nedeni, varsayılan pakette yazılmış bir API yüklemeye çalışıyorum, böylece yansımayı kullanmadan doğrudan kullanabilmem için. Kod, paketi temsil eden bir klasör yapısında ve paket adını içe aktarmada sınıfla derlenir. yani:

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

Benim şu anki kod aşağıdaki gibidir:

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

} 

cevap

12

As Pete mentioned, bu ASM bayt kodu kütüphanesi kullanılarak yapılabilir. Aslında, bu kitaplık aslında bu sınıf adı yeniden eşlemeleri (RemappingClassAdapter) işlemek için özellikle bir sınıf ile birlikte gelir.

public class Customer { 

} 

ve

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

} 
:

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

    } 

} 

varsayılan paketine ait her ikisi de iki sınıf yarattı, göstermek için: İşte bu sınıfı kullanarak bir sınıf yükleyici bir örnektir

Order'un nolu herhangi bir yeniden eşleştirmesinin listesi:

 
> 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 

} 

Gördüğünüz gibi

, remapping'i com.mycompany.Order tüm Order başvurular değişti ve tüm Customer referanslar için etti:
 
> 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 

} 

Bu(varsayılan paket olarak com.mycompany kullanarak) remapping'i sonra Orderlistesidir com.mycompany.Customer. Varsayılan paketine ait

  • varsayılan paketine ait veya
  • kullanımı, diğer sınıfları:

    Bu sınıf yükleyici tüm ya sınıflarını yüklemek gerekir.

0

bir daha makul noktaya varsayılan paketinden API taşımak daha kolay olurdu? Kaynak koduna erişiminiz olmadığı anlaşılıyor. Paketin sınıf dosyalarına kodlanmış olup olmadığından emin değilim, bu yüzden API sınıfını hareket ettirmek denemeye değer olabilir. Aksi takdirde, JAD gibi Java decompilers genellikle iyi bir iş yapar, bu yüzden paketin adını decompiled kaynakta değiştirebilir ve tekrar derleyebilirsiniz.

+0

Hayır, kaynağa erişimim yok. Sınıfları bir dizine taşımak, derleme aşamasında görünse de çalışma zamanında çalışmaz. Mümkünse sınıfı hacklememeyi tercih ederim. –

+0

Çalışma zamanında bir sınıfın paketini değiştirmenin hack olmadığını mı söylüyorsun? Bana göre soru, en kötü hack nedir ve sonra diğerini kullanın ... – FelixM

1

ASM ile bir şeyleri parçalayabilmeniz gerekir, ancak yükleme zamanından ziyade, paketin yeniden oluşturulma süresinde bir kez daha yeniden yapılması daha kolay olurdu.

İlgili konular