Java:在类路径中的所有类上循环
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1498122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java : loop on all the classes in the classpath
提问by glmxndr
Is there a way to iterate over all the classes in the classpath ?
有没有办法遍历类路径中的所有类?
I want to make some reflective checks on some classes implementing a certain interface, but I want to do it completely dynamically, without any input on which classes to check, just browsing the classpath.
我想对实现某个接口的某些类进行一些反射检查,但我想完全动态地进行检查,无需输入要检查的类,只需浏览类路径。
采纳答案by Yishai
The Reflectionslibrary helps deal with this problem. As others have stated, it isn't fully possible in all class loading situations, but if all you have are jars and files, this will do it reliably.
该思考图书馆可以帮助解决这个问题。正如其他人所说,在所有类加载情况下都不是完全可能的,但是如果您只有 jars 和文件,这将可靠地完成。
回答by Jon Skeet
You can't do this elegantly.
你不能优雅地做到这一点。
Basically a classloader can be asked to load a specific class name, but can't be asked for all the classes it could load. (In the case of something loading classes over the web, it may not be feasible to do so - you can't reliably ask the web server to tell you all the files under a particular directory.)
基本上可以要求类加载器加载特定的类名,但不能要求它可以加载的所有类。(在通过 Web 加载类的情况下,这样做可能不可行 - 您不能可靠地要求 Web 服务器告诉您特定目录下的所有文件。)
If your classpath onlydeals with the file system, you could painfully find all the jar files in extension directories, recurse down normal classpath directories, and look inside all explicitly specified jar files - but it'll be tricky and probably fragile too.
如果您的类路径只处理文件系统,您可能会痛苦地在扩展目录中找到所有 jar 文件,向下递归正常的类路径目录,并查看所有明确指定的 jar 文件 - 但它会很棘手,而且可能也很脆弱。
回答by ckovacs
I have solved this problem for a single class loader. I needed reflection to write code to inspect JUnit tests and report on ignored tests.
我已经为单个类加载器解决了这个问题。我需要反射来编写代码来检查 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;
}
I based my solution on the code in this thread:
回答by Bart Kiers
I think you'll have to inspect it manually:
我认为您必须手动检查它:
String classpath = System.getProperty("java.class.path");
String[] locations = classpath.split(System.getProperty("path.separator"));
// inspect all jar's and class files in these locations, which is a pain in the b%tt
Or use some third party library that does this (I don't know of any).
或者使用一些这样做的第三方库(我不知道)。
回答by Rob H
I'm not aware of a library that does this, but there are open source projects that do this for their own purposes. For example, Springcan iterate over classes in the classpath to find ones that have a certain annotation. You could take a look at how they do it to get some ideas. I'd start with the classes in the org.springframework.context.annotationpackage such as ClassPathScanningCandidateComponentProviderand CommonAnnotationBeanPostProcessor.
我不知道有一个库可以这样做,但是有一些开源项目出于自己的目的这样做。例如,Spring可以遍历类路径中的类以查找具有特定注释的类。你可以看看他们是如何做到的,以获得一些想法。我将从org.springframework.context.annotation包中的类开始,例如ClassPathScanningCandidateComponentProvider和CommonAnnotationBeanPostProcessor。
回答by Heri
If you use spring-context in your project: Here a working example to enumerate all types with the desired annotation:
如果您在项目中使用 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;
}
This code was tested against spring-context-4.1.1.RELEASE under java 8.
此代码在 java 8 下针对 spring-context-4.1.1.RELEASE 进行了测试。
回答by Omry Yadan
you can't without a custom classloader.
你不能没有自定义类加载器。
think for example about a case where you load your classes from a remote server that does not even offer directory listing. how can you possibly iterate over all the classes there?
例如,考虑从远程服务器加载类的情况,该服务器甚至不提供目录列表。你怎么可能遍历那里的所有类?
the classloader interface does not provide such a function. but if you use your own classloader (or extend an existing classloader) you could add this function. in any case, it's not pretty.
类加载器接口不提供这样的功能。但是如果您使用自己的类加载器(或扩展现有的类加载器),则可以添加此功能。无论如何,它并不漂亮。

