Получение списка полностью определенных имен из простого имени - PullRequest
5 голосов
/ 02 октября 2010

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

Например:

public List<String> getFQNs(String simpleName) {
    ...
}

// Would return ["java.awt.List","java.util.List"]
List<String> fqns = getFQNs("List")

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

Спасибо!

UPDATE

Один респондент спросил меня, почему я хотел это сделать. По сути, я хочу реализовать функцию, которая похожа на «организовать импорт / автоматический импорт», но доступна во время выполнения. Я не против, если решение будет относительно медленным (особенно если я смогу затем построить кэш, чтобы последующие запросы стали быстрее) и , если это только лучший вариант . Например, я не против, если я не получу динамически сгенерированные классы.

ОБНОВЛЕНИЕ 2

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

Ответы [ 3 ]

4 голосов
/ 03 октября 2010

Я смешал ответы @Grodriguez и @bemace и добавил свою собственную стратегию, чтобы найти лучшее решение.Это решение имитирует во время выполнения функцию автоматического импорта, доступную во время компиляции.

Полный код Мое решение здесь .Для простого имени основными шагами являются:

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

Шаг 2 прост:

public List<String> getFQNs(String simpleName) {
    if (this.packages == null) {
        this.packages = getPackages();
    }

    List<String> fqns = new ArrayList<String>();
    for (String aPackage : packages) {
        try {
            String fqn = aPackage + "." + simpleName;
            Class.forName(fqn);
            fqns.add(fqn);
        } catch (Exception e) {
            // Ignore
        }
    }
    return fqns;
}

Шаг 1 сложнее и зависит от вашего приложения / среды, поэтому я реализовал различные стратегии для получения разных списков пакетов.

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

public Collection<String> getPackages() {
    Set<String> packages = new HashSet<String>();
    for (Package aPackage : Package.getPackages()) {
        packages.add(aPackage.getName());
    }
    return packages;
}

Classpath (достаточно для приложений, которые полностью загруженыиз classpath. Не подходит для сложных приложений, таких как Eclipse)

public Collection<String> getPackages() {
    String classpath = System.getProperty("java.class.path");
    return getPackageFromClassPath(classpath);
}

public static Set<String> getPackageFromClassPath(String classpath) {
    Set<String> packages = new HashSet<String>();
    String[] paths = classpath.split(File.pathSeparator);
    for (String path : paths) {
        if (path.trim().length() == 0) {
            continue;
        } else {
            File file = new File(path);
            if (file.exists()) {
                String childPath = file.getAbsolutePath();
                if (childPath.endsWith(".jar")) {
                    packages.addAll(ClasspathPackageProvider
                            .readZipFile(childPath));
                } else {
                    packages.addAll(ClasspathPackageProvider
                            .readDirectory(childPath));
                }
            }
        }

    }
    return packages;
}

Bootstrap classpath (например, java.lang)

public Collection<String> getPackages() {
    // Even IBM JDKs seem to use this property...
    String classpath = System.getProperty("sun.boot.class.path");
    return ClasspathPackageProvider.getPackageFromClassPath(classpath);
}

Eclipse связки (поставщик пакетов для конкретного домена)

// Don't forget to add "Eclipse-BuddyPolicy: global" to MANIFEST.MF
public Collection<String> getPackages() {
    Set<String> packages = new HashSet<String>();
    BundleContext context = Activator.getDefault().getBundle()
            .getBundleContext();
    Bundle[] bundles = context.getBundles();
    PackageAdmin pAdmin = getPackageAdmin(context);

    for (Bundle bundle : bundles) {
        ExportedPackage[] ePackages = pAdmin.getExportedPackages(bundle);
        if (ePackages != null) {
            for (ExportedPackage ePackage : ePackages) {
                packages.add(ePackage.getName());
            }
        }
    }

    return packages;
}

public PackageAdmin getPackageAdmin(BundleContext context) {
    ServiceTracker bundleTracker = null;
    bundleTracker = new ServiceTracker(context,
            PackageAdmin.class.getName(), null);
    bundleTracker.open();
    return (PackageAdmin) bundleTracker.getService();
}

Примеры запросов и ответов в моей среде Eclipse:

  1. Файл: [java.io.File, org.eclipse.core.internal.resources.File]
  2. Список: [java.awt.List, org.eclipse.swt.widgets.List, com.sun.xml.internal.bind.v2.schemagen.xmlschema.List, java.util.List, org.hibernate.mapping..List]
  3. IResource: [org.eclipse.core.resources.IResource]
1 голос
/ 02 октября 2010

Не загружая их все, вы можете получить свойство classpath

String class_path = System.getProperty("java.class.path");

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

Edit: Хорошо, звучит так, как будто вы должны проверить этот поток и связанные в нем: Как мне прочитать все классы из пакета Java в classpath? В частности, похоже, что среда Spring делает нечто подобное, возможно, вы можете посмотреть на этот код: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html

1 голос
/ 02 октября 2010

Вы, вероятно, не можете сделать это вообще.У JVM нет возможности узнать, доступен ли класс List в произвольно названном пакете a.b.c.d, не пытаясь сначала загрузить a.b.c.d.List.Вам нужно будет проверить все возможные имена пакетов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...