2010-08-11 16 views
6

Hago un nuevo ClassLoader y lo hago definir un nuevo Class, lo que significa que la nueva clase debe estar en un nuevo espacio de nombres, que es, AFAIK. Lo extraño es que cuando llamo al Class.getPackage en la nueva clase, devuelve el mismo objeto exacto devuelto al llamar al getPackage en cualquier otra clase en mi espacio de nombres principal.¿Por qué Class.getPackage devuelve el mismo paquete para clases de diferentes paquetes?

Según la JVM spec:

El paquete de tiempo de ejecución de una clase o interfaz se determina por el paquete nombre y la definición de cargador de clases de la clase o interfaz.

En otras palabras, si tiene dos clases en el mismo paquete, pero las cargan diferentes cargadores de clases, se considera que están en paquetes diferentes. (Esto también puede ser "confirmado" a través de la reflexión en mi caso de prueba a continuación.)

Entonces, ¿cómo es que cuando hago esto obtengo el mismo resultado de getPackage en ambas clases?

Aquí es mi prueba:

package pkg; 
import java.io.*; 

// Yes, you can try commenting this class, you'll get the same result. 
class LoadedClass { 
    LoadedClass() { 
     System.out.println("LoadedClass init"); 
    } 
} 

class MyClassLoader extends ClassLoader { 
    Class<?> defineClass(String name, byte[] b) { 
     return defineClass(name, b, 0, b.length); 
    } 
} 

class Main { 
    public static void main(String[] args) throws Exception { 
     MyClassLoader mcl = new MyClassLoader(); 

     // load compiled class from file 
     FileInputStream fileinputstream = new FileInputStream(
      "/home/me/test/pkg/LoadedClass.class" /* <- point to whever these classes 
                * are being compiled to. */ 
     ); 
     int numberBytes = fileinputstream.available(); 
     byte classBytes[] = new byte[numberBytes]; 
     fileinputstream.read(classBytes); 
     fileinputstream.close(); 

     Class<?> lc = mcl.defineClass("pkg.LoadedClass", classBytes); 
     Package myPackage = Main.class.getPackage(); 
     Package lcPackage = lc.getPackage(); 
     System.out.println("lc package: " + lcPackage); 
     System.out.println("my package: " + myPackage); 
     System.out.println("lc ClassLoader: " + lc.getClassLoader()); 
     System.out.println("lc ClassLoader parent: " + 
          lc.getClassLoader().getParent()); 
     System.out.println("my ClassLoader: " + Main.class.getClassLoader()); 
     System.out.println("are they equal? " + (lcPackage == myPackage)); 
     if (lcPackage == myPackage) { 
      System.out.println("okay... we should be able to instantiate " + 
           "the package if that's true, lets try"); 
      lc.newInstance(); // boom as expected 
     } 
    } 
} 

Genera:

lc package: package pkg 
my package: package pkg 
lc ClassLoader: [email protected] 
lc ClassLoader parent: [email protected] 
my ClassLoader: [email protected] 
are they equal? true 
okay... we should be able to instantiate the package if that's true, lets try 
Exception in thread "main" java.lang.IllegalAccessException: Class pkg.Main can not access a member of class pkg.LoadedClass with modifiers "" 
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) 
    at java.lang.Class.newInstance0(Class.java:349) 
    at java.lang.Class.newInstance(Class.java:308) 
    at pkg.Main.main(Main.java:42) 

Como era de esperar, normalmente no se puede crear una instancia de esta clase cargada a través de la reflexión, porque el paquete-privada y se encuentra en un diferente paquete (mismo nombre, diferente espacio de nombres), que es correcto AFAIK, porque está aplicando seguridad de tipo.

Solo me pregunto porque he estado estudiando la JVM y la arquitectura de seguridad en los últimos días y sigo encontrando pequeñas sutilezas como esta, así que es difícil razonar.

Respuesta

5

El método getPackage no está especificado. Esto es lo que bug 4256589 dice al respecto:

ClassLoader.getPackage ("foo") devuelve el objeto de paquete definido para el paquete foo en este cargador de clases en particular, o si este cargador de clases no definió paquete foo, la método devuelve lo que el cargador de clases padre ha definido para foo, si corresponde.

Para mí, esto dice que el objeto devuelto por PackagegetPackage depende de si el cargador de clases "definido" un paquete de clases en sí, o si se encuentra que el paquete en su cargador de clases padre. Y el comportamiento que está viendo parece ser consistente con esto.

Es bastante inconsistente. Pero, ¿realmente importa si hay un objeto de paquete o varios objetos de paquete? Ciertamente, no debería haber ninguna diferencia al escribir seguridad o seguridad ... a menos que haya implementado algún esquema especial de seguridad basado en paquetes en un cargador de clases personalizado o administrador de seguridad.

+0

Sí, se estaba llamando al ClassLoader principal. "Pero, ¿realmente importa si hay un objeto de paquete o varios objetos de paquete?"Supongo que es un caso de uso raro, pero la implementación de openjdk que examiné comprueba el paquete en la reflexión, por supuesto, y resulta que compara el ClassLoader y el nombre completamente calificado, en lugar de llamar a cualquiera de los' getPackage's. –

+0

I Supongo que esta es una de las razones por las que las personas de OSGi odian los "paquetes divididos" (paquetes que se extienden a través de archivos jar, paquetes, cargadores de clases). – Thilo

+0

@Longpoke - mi * "¿realmente hace alguna diferencia ..." * punto es que una vez comprenda esta anomalía que puede programar a su alrededor, como lo hace el código que encontró. –

Cuestiones relacionadas