Динамически загружать класс верхнего уровня в банку вне пути к классам

ОБНОВЛЕНИЕ. Все файлы в моем проекте теперь объявляют один и тот же пакет. Однако загружаемая банка должна быть вне пути к классам. Я пробовал child.loadClass(className) и Class.forName(className, false, child);, но ни один из них не смог его найти.

Я пишу программу, которая хочет использовать плагины или модули в виде банок и использовать их так, как если бы они были встроены в путь к классам.

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

У меня есть папка, полная банок. Я знаю, что у каждой банки есть один класс верхнего уровня, который расширяет класс моего модуля. Но я не знаю, как называются эти классы.

Сначала я нахожу все файлы в каталоге. Это прекрасно работает. Затем для каждого файла я пытаюсь получить экземпляр его класса верхнего уровня, используя следующий код:

(Сначала он открывает банку в виде zip-архива, чтобы получить имена всех классов, затем смотрит на имя, чтобы определить, является ли это классом верхнего уровня, а затем, если это так, он должен загрузить, создать экземпляр и вернуть его.)

private static Module jarLoader(String jar){
    try{
        ZipInputStream zip = new ZipInputStream(new FileInputStream(ModulesDir+"/"+jar));
        URL[] myJarFile = new URL[]{new URL("jar","","file:"+ModulesDir)};
        URLClassLoader child = new URLClassLoader (myJarFile , Main.class.getClassLoader());
        for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
            if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
                // This ZipEntry represents a class. Now, what class does it represent?
                String className = entry.getName().replace('/', '.'); // including ".class"
                className = className.substring(0, className.length() - ".class".length());


                String[] classNameParts = className.replace('$', ':').split(":");//I don't know why i have to do this, but it won't split on "$"
                if(classNameParts.length == 1){
                    System.out.println("trying to load "+className);
                    Class classToLoad = Class.forName(className, false, child);
                    System.out.println("loaded class "+classToLoad.getName());
                    if(classToLoad.getSuperclass() == Module.class){
                        Module instance = (Module) classToLoad.newInstance();
                        return instance;
                    }
                }
            }
        }
        zip.close();
    }catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

Он доходит до «попытки загрузить ...» и показывает имя класса, как я и ожидал, а затем выдает java.lang.ClassNotFoundException.

Использование того же имени для загрузки класса работало нормально, когда я поместил его в файл .java в исходном каталоге, но он не получит его из банки.

Я делаю что-то неправильно? Я подозреваю, что имени класса может понадобиться какой-то префикс или суффикс, но я не могу понять, что именно.

Спасибо за помощь!


person QuinnFreedman    schedule 24.06.2015    source источник
comment
classname также потребует полного имени пакета   -  person Juned Ahsan    schedule 24.06.2015
comment
@JunedAhsan Я догадался, но не знал, какое имя пакета использовать, так как не указал его в банках. Есть ли значение по умолчанию или мой единственный вариант объявить пакет в каждой банке?   -  person QuinnFreedman    schedule 24.06.2015
comment
Вы поместили все банки под свой путь к классу? Class.forName() будет искать текущий путь к классам, указанный в параметре «-classpath» при запуске «java.exe».   -  person Harry.Chen    schedule 24.06.2015


Ответы (2)


throw ClassNotFoundException заключается в том, что ваш загрузочный файл jar не будет добавлен в путь к классу, а приведенный ниже код не сможет успешно загрузить ваш файл jar.

URL[] myJarFile = new URL[]{new URL("jar","","file:"+ModulesDir)};
        URLClassLoader child = new URLClassLoader (myJarFile , Main.class.getClassLoader());

должно быть так:

   URL[] myJarFile = new URL[]{new URL("jar","",jarFilePath)};

   URLClassLoader child = new URLClassLoader (myJarFile, this.getClass().getClassLoader());

и когда класс компилируется javac, внутренний класс будет выражен полным путем и разделен на $, например com/hello/world/TT$InnerClass.class.

Полный образец:

 private void jarLoader(){
        try{
            String ModulesDir = "./test/test.jar";
            ZipInputStream zip = new ZipInputStream(new FileInputStream(ModulesDir));

            URL[] myJarFile = new URL[]{new URL("jar","",ModulesDir)};

            URLClassLoader child = new URLClassLoader (myJarFile , this.getClass().getClassLoader());

            for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
                if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
                    // This ZipEntry represents a class. Now, what class does it represent?
                    String className = entry.getName().replace('/', '.'); // including ".class"
                    className = className.substring(0, className.length() - ".class".length());


                    String[] classNameParts = className.replace('$', ':').split(":");//I don't know why i have to do this, but it won't split on "$"
                    if(classNameParts.length == 1){
                        System.out.println("trying to load "+className);
                        Class classToLoad = Class.forName(className, true, child);
                        System.out.println("loaded class "+classToLoad.getName());
                    }
                }
            }
            zip.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    } 
person chengpohi    schedule 24.06.2015

Все, что было предложено, похоже, не сработало, потому что файлы, которые мне нужно было загрузить, находились за пределами моего пути к классам.

Этот ответ работает, хотя

person QuinnFreedman    schedule 24.06.2015