Java, получить все классы, доступные для URLClassLoader, которые реализуют определенный интерфейс - PullRequest
1 голос
/ 17 февраля 2011

Я работаю над приложением командной строки, которое загружает указанные пользователем текстовые трансляторы во время выполнения (путь к файлам классов / jar предоставляется через командную строку arg). В основном я беру этот аргумент и использую его для создания URLClassLoader. Затем мне нужно найти все классы, доступные для URLClassloader, которые реализуют интерфейс Transable.

Прямо сейчас я позволяю только этой командной строке arg быть каталогом с файлами классов в нем. Сделать решение довольно простым (код ниже). Но, честно говоря, мне не нравится решение, так как оно разбивается на jar-файлы, каталог jar-файлов и т. Д. Кроме того, это очевидно, что оно разбивается на все классы с определенным пакетом, поскольку loadClass требуется полное имя, включая пакет. У кого-нибудь есть лучший метод?

    File d = new File(path);
    if(d.isDirectory()) {
        URL url = d.toURI().toURL();
        ClassLoader cl = new URLClassLoader(new URL[]{url});

        FilenameFilter filter = new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".class");
            }
        };

        for(File f : d.listFiles(filter)) {
            String name = f.getName().substring(0, f.getName().indexOf("."));
            String key = "";
            if(name.endsWith("Translator")) {
                key = name.substring(0, name.indexOf("Translator"));
            }
            else if(name.endsWith("translator")) {
                key = name.substring(0, name.indexOf("translator"));
            }
            else
                key = name;

            Class c = cl.loadClass(name);
            if(Transable.class.isAssignableFrom(c)) {
                Transable t = (Transable)c.newInstance();
                env.registerTranslator(key, t);
            }
            else {
                System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
            }
        }
    }
    else {
        throw new Error("NOT IMPLEMENTED");
    }

Ответы [ 5 ]

4 голосов
/ 17 февраля 2011

Вместо явного поиска разработчиков следует использовать интерфейс поставщика услуг (SPI) Java и класс ServiceLoader (представлен в Java 6). SPI - это довольно стандартный способ сделать то, что вы описываете в Java.

Пожалуйста, ознакомьтесь с официальным руководством Java о том, как создать поставщика услуг и как использовать его во время выполнения с ServiceLoader.

4 голосов
/ 17 февраля 2011

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

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

3 голосов
/ 17 февраля 2011

В принципе, это не может работать для произвольных загрузчиков классов, поскольку они могут использовать любой способ, который можно себе представить, для фактической загрузки классов, и вообще не иметь никакой функции "прослушивания каталогов".

Загрузчик классов может даже генерироватьклассы (т.е. байт-код для классов) на лету, когда появляется loadClass, и представьте себе TransableClassloader, где каждый такой автоматически определенный класс будет реализовывать ваш интерфейс - ваша программа никогда не закончится.


Этосказал, что для URLClassloader вы можете использовать getURLs(), а для jar: и file: URL вы можете использовать JarFile или File api, чтобы получить список имен файлов (и, следовательно, Classnames), чтобы попытаться,Поскольку у вас есть корень иерархии вашего пакета, указанный в URL-адресе, найти правильное имя пакета тоже не сложно.

(Если вам нужна дополнительная информация, произнесите это.)


Редактировать: Для имен пакетов они соответствуют именам каталогов внутри вашей иерархии.Итак, когда у вас есть базовый URL, который соответствует (скажем) dir/classes, и вы нашли файл класса с именем dir/classes/com/company/gui/SimpleTranslator.class, он соответствует классу com.company.gui.SimpleTranslator.

Итак, удалите базовый префикс изаменить / на . (и вырезать из .class).(В JarFile вам не нужно обрезать префикс.)

На самом деле, если вы используете рекурсивный метод для обхода вашей файловой иерархии, вы можете создать имя пакета с помощью того же метода, простодобавив строки (передайте их в качестве параметра для следующего рекурсивного вызова):

public void searchClassesInDir(File dir, String packagePrefix) {
    if(dir.isDirectory()) {
        String prefix = packagePrefix + dir.getName() + ".";
        for(File f : dir.listFiles()) {
            searchClasses(f, prefix);
        }
    }
    else {
       String fileName = dir.getName();
       if(! fileName.endsWith(".class"))
           return;
       String className = packagePrefix + fileName.substring(0, fileName.length()-".class".length());
       // now do the rest of your processing
    }
}

searchClasses(new File(url.toURI()), "");
3 голосов
/ 17 февраля 2011

Это может соответствовать всем требованиям. Если нет, вы должны быть в состоянии просмотреть их источник, чтобы понять, что будет работать для вас. http://code.google.com/p/reflections/

0 голосов
/ 17 февраля 2011

Поможет ли это?

Поскольку Class c = cl.loadClass (name);

Метод класса getInterfaces () возвращает массив классов

Проверьте каждое имя класса на соответствие имени класса переводчика.

...