Почему Class.getPackage возвращает один и тот же пакет для классов из разных пакетов? - PullRequest
6 голосов

Я создаю новый ClassLoader и заставляю его определять новый Class, что означает, что новый класс должен находиться в новом пространстве имен, которым он является, AFAIK. Странно то, что когда я вызываю Class.getPackage для нового класса, он возвращает точно такой же объект, который был возвращен путем вызова getPackage для любого другого класса в моем основном пространстве имен.

Согласно спецификации JVM :

Пакет времени выполнения класса или интерфейс определяется пакетом имя и определение загрузчика класса класс или интерфейс.

Другими словами, если у вас есть два класса в одном пакете, но загружены разными загрузчиками классов, они считаются в разных пакетах. (Это также можно «подтвердить» с помощью отражения в моем тестовом примере ниже.)

Так почему же, когда я делаю это, я получаю одинаковый результат от getPackage в обоих классах?

Вот мой тест:

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

Выводит:

lc package: package pkg
my package: package pkg
lc ClassLoader: pkg.MyClassLoader@7987aeca
lc ClassLoader parent: sun.misc.Launcher$AppClassLoader@1f7182c1
my ClassLoader: sun.misc.Launcher$AppClassLoader@1f7182c1
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)

Как и ожидалось, вы обычно не можете создать экземпляр этого загруженного класса с помощью отражения, потому что package-private и находится в другом пакете (то же имя, другое пространство имен), что является правильным AFAIK, потому что он принудительно выполняет Тип безопасности.

Просто интересно, потому что я изучал JVM и архитектуру безопасности в последние несколько дней и постоянно нахожу маленькие тонкости, подобные этой, так что об этом трудно рассуждать.

1 Ответ

5 голосов
/ 11 августа 2010

Метод getPackage не указан. Вот что говорит по этому поводу ошибка 4256589

ClassLoader.getPackage ("foo") возвращает объект пакета, определенный для пакета foo в данном конкретном загрузчике классов, или если этот загрузчик классов не определил В пакете foo метод возвращает то, для чего определен загрузчик родительского класса. фу, если есть.

Для меня это говорит о том, что объект Package, возвращаемый getPackage, зависит от того, «загрузчик классов» «определил» сам пакет классов или он нашел этот пакет в своем родительском загрузчике классов. И поведение видящего, кажется, согласуется с этим.

Это довольно противоречиво. Но действительно ли имеет значение, существует ли один объект пакета или несколько объектов пакета? Конечно, это не должно иметь никакого значения для безопасности типов или безопасности ... если только вы не внедрили какую-то особую схему безопасности на основе пакетов в пользовательском загрузчике классов или менеджере безопасности.

...