Получение списка полностью определенных имен из простого имени
Я хотел бы получить список классов, которые доступны во время выполнения и соответствуют простому имени.
Например:
public List<String> getFQNs(String simpleName) {
...
}
// Would return ["java.awt.List","java.util.List"]
List<String> fqns = getFQNs("List")
Есть ли библиотека, которая бы делала это эффективно, или мне нужно вручную проходить все классы в каждом загрузчике классов? Каков будет правильный способ сделать это?
Спасибо!
ОБНОВИТЬ
Один респондент спросил меня, почему я хотел сделать это. По сути, я хочу реализовать функцию, которая похожа на "организовать импорт / автоматический импорт", но доступна во время выполнения. Я не против, если решение будет относительно медленным (особенно если я смогу затем создать кеш, чтобы последующие запросы стали быстрее), и если это только лучшее из возможных. Например, я не против, если я не получу динамически сгенерированные классы.
ОБНОВЛЕНИЕ 2
Мне пришлось разработать собственное решение (см. Ниже): оно использует некоторые подсказки, предоставленные другими респондентами, но я пришел к выводу, что оно должно быть расширяемым для работы с различными средами. Невозможно автоматически обойти все загрузчики классов во время выполнения, поэтому вам нужно полагаться на общие и доменные стратегии, чтобы получить полезный список классов.
3 ответа
Я смешал ответы @Grodriguez и @bemace и добавил свою собственную стратегию, чтобы найти лучшее решение. Это решение имитирует во время выполнения функцию автоматического импорта, доступную во время компиляции.
Полный код моего решения здесь. Учитывая простое имя, основные шаги:
- Получить список пакетов, доступных из текущего загрузчика классов.
- Для каждого пакета попробуйте загрузить полное имя, полученное из пакета + простое имя.
Шаг 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;
}
Путь к начальной загрузке (например, 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 bundles (доменный провайдер пакетов)
// 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:
- Файл: [java.io.File, org.eclipse.core.internal.resources.File]
- Список: [java.awt.List, org.eclipse.swt.widgets.List, com.sun.xml.internal.bind.v2.schemagen.xmlschema.List, java.util.List, org.hibernate.mapping.List ]
- IResource: [org.eclipse.core.resources.IResource]
Вы, вероятно, не можете сделать это вообще. У JVM нет возможности узнать, является ли класс List
в произвольно названной упаковке a.b.c.d
доступно без попытки загрузки a.b.c.d.List
первый. Вам нужно будет проверить все возможные имена пакетов.
Не загружая их все, вы можете получить свойство classpath
String class_path = System.getProperty("java.class.path");
И тогда вы можете создать функцию для поиска в файловой системе классов в этих местах. И вам придется кодировать его для поиска внутри jar-файлов, и некоторые классы могут быть фактически недоступны из-за несовместимости, которая может быть обнаружена только при их загрузке. Но если вы просто хотите догадаться, что доступно, это может быть жизнеспособным. Может быть, вы должны сказать нам, почему вы хотите это сделать, чтобы получить альтернативные предложения?
Изменить: Хорошо, звучит так, как будто вы должны проверить этот поток и те, которые связаны в нем: Как я могу прочитать все классы из пакета Java в classpath? В частности, похоже, что среда Spring делает нечто похожее, возможно, вы можете посмотреть на этот код: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html