Ответ 1
Библиотека Reflections помогает решить эту проблему. Как заявили другие, это не совсем возможно во всех ситуациях загрузки класса, но если все, что у вас есть, это банки и файлы, это сделает это надежно.
Есть ли способ перебрать все классы в пути к классам?
Я хочу сделать некоторые рефлексивные проверки для некоторых классов, реализующих определенный интерфейс, но я хочу сделать это полностью динамически, без каких-либо входных данных, какие классы проверять, просто просматривая путь к классам.
Библиотека Reflections помогает решить эту проблему. Как заявили другие, это не совсем возможно во всех ситуациях загрузки класса, но если все, что у вас есть, это банки и файлы, это сделает это надежно.
Вы не можете сделать это элегантно.
В принципе загрузчик классов может быть запрошен для загрузки определенного имени класса, но не может быть запрошен для всех классов, которые он может загрузить. (В случае чего-то, загружающего классы через Интернет, это может быть невыполнимо - вы не можете надежно попросить веб-сервер рассказать вам обо всех файлах под определенным каталогом.)
Если ваш путь к классам связан только с файловой системой, вы можете с трудом найти все файлы jar в каталогах расширений, перезапустить обычные каталоги классов и просмотреть все явно определенные файлы jar, но это будет сложно и, вероятно, хрупким.
Я решил эту проблему для одного загрузчика классов. Мне понадобилось отражение для написания кода для проверки тестов JUnit и отчета об игнорируемых тестах.
/**
* Attempts to list all the classes in the specified package as determined
* by the context class loader
*
* @param pckgname
* the package name to search
* @return a list of classes that exist within that package
* @throws ClassNotFoundException
* if something went wrong
*/
private static List<Class> getClassesForPackage(String pckgname) throws ClassNotFoundException {
// This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths
ArrayList<File> directories = new ArrayList<File>();
try {
ClassLoader cld = Thread.currentThread().getContextClassLoader();
if (cld == null) {
throw new ClassNotFoundException("Can't get class loader.");
}
String path = pckgname.replace('.', '/');
// Ask for all resources for the path
Enumeration<URL> resources = cld.getResources(path);
while (resources.hasMoreElements()) {
directories.add(new File(URLDecoder.decode(resources.nextElement().getPath(), "UTF-8")));
}
} catch (NullPointerException x) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Null pointer exception)");
} catch (UnsupportedEncodingException encex) {
throw new ClassNotFoundException(pckgname + " does not appear to be a valid package (Unsupported encoding)");
} catch (IOException ioex) {
throw new ClassNotFoundException("IOException was thrown when trying to get all resources for " + pckgname);
}
ArrayList<Class> classes = new ArrayList<Class>();
// For every directory identified capture all the .class files
for (File directory : directories) {
if (directory.exists()) {
// Get the list of the files contained in the package
String[] files = directory.list();
for (String file : files) {
// we are only interested in .class files
if (file.endsWith(".class")) {
// removes the .class extension
try
{
classes.add(Class.forName(pckgname + '.' + file.substring(0, file.length() - 6)));
}
catch (NoClassDefFoundError e)
{
// do nothing. this class hasn't been found by the loader, and we don't care.
}
}
}
} else {
throw new ClassNotFoundException(pckgname + " (" + directory.getPath() + ") does not appear to be a valid package");
}
}
return classes;
}
Я основал свое решение в коде в этом потоке:
Думаю, вам придется вручную его проверять:
String classpath = System.getProperty("java.class.path");
String[] locations = classpath.split(System.getProperty("path.separator"));
// inspect all jar and class files in these locations, which is a pain in the b%tt
Или используйте стороннюю библиотеку, которая делает это (я не знаю).
Я не знаю об этой библиотеке, но есть проекты с открытым исходным кодом, которые делают это в своих целях. Например, Spring может перебирать классы в пути к классам, чтобы найти те, у которых есть определенная аннотация. Вы можете посмотреть, как они это делают, чтобы получить некоторые идеи. Я бы начал с классов в пакете org.springframework.context.annotation, например ClassPathScanningCandidateComponentProvider и CommonAnnotationBeanPostProcessor.
Если вы используете spring -context в своем проекте: Здесь рабочий пример для перечисления всех типов с нужной аннотацией:
/**
* Lists all types in the given package (recursive) which are annotated by the given annotation.
* <p>
* All types which match the criteria are returned, no further checks (interface, abstract, embedded, etc.
* are performed.
* <p>
*
* @param aAnnotation
* the desired annotation type
* @param aRoot
* the package name where to start the search.
* @return a list of found types
*/
private Collection<? extends Class<?>> findCandidatesByAnnotation( Class<? extends Annotation> aAnnotation,
String aRoot )
{
List<Class<?>> result = new ArrayList<Class<?>>();
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider( false )
{
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
{
return true;
}
};
scanner.addIncludeFilter( new AnnotationTypeFilter( aAnnotation ) );
Set<BeanDefinition> canditates = scanner.findCandidateComponents( aRoot );
for ( BeanDefinition beanDefinition : canditates )
{
try
{
String classname = beanDefinition.getBeanClassName();
Class<?> clazz = Class.forName( classname );
result.add( clazz );
}
catch ( ClassNotFoundException t )
{
myLog.error( "Springs type scanner returns a class name whose class cannot be evaluated!!!", t );
}
}
return result;
}
Этот код был протестирован против spring -context-4.1.1.RELEASE под java 8.
вы не можете без специального загрузчика классов.
подумайте, например, о том, где вы загружаете свои классы с удаленного сервера, который даже не предлагает список каталогов. как вы можете перебирать все классы там?
интерфейс classloader не предоставляет такую функцию. но если вы используете свой собственный загрузчик классов (или расширяете существующий загрузчик классов), вы можете добавить эту функцию. в любом случае, это не очень.