有没有办法迭代类路径中的所有类?
我想对实现某个接口的某些类进行一些反思性检查,但我想完全动态地完成它,而不需要检查哪些类,只需浏览类路径。
答案 0 :(得分:13)
Reflections库有助于解决此问题。正如其他人所说的那样,在所有类加载情况下都不是完全可能的,但如果你拥有的只是罐子和文件,那么这将是可靠的。
答案 1 :(得分:8)
你不能优雅地做到这一点。
基本上可以要求类加载器加载特定的类名,但不能要求它加载所有类。 (如果在Web上加载类,则可能无法这样做 - 您无法可靠地要求Web服务器告诉您特定目录下的所有文件。)
如果你的类路径只处理文件系统,你可能会痛苦地找到扩展目录中的所有jar文件,递归正常的类路径目录,并查看所有明确指定的jar文件 - 但它'会很棘手,也可能很脆弱。
答案 2 :(得分:6)
我已经为单个类加载器解决了这个问题。我需要反思来编写代码来检查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;
}
我的解决方案基于code in this thread:
答案 3 :(得分:3)
我认为你必须手动检查它:
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
或者使用一些第三方库(我不知道)。
答案 4 :(得分:2)
我不知道有这样做的库,但有些开源项目是出于自己的目的而这样做的。例如,Spring可以迭代类路径中的类以查找具有特定注释的类。您可以看看他们是如何做到的,以获得一些想法。我将从 org.springframework.context.annotation 包中的类开始,例如 ClassPathScanningCandidateComponentProvider 和 CommonAnnotationBeanPostProcessor 。
答案 5 :(得分:1)
如果在项目中使用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;
}
此代码在java 8下针对spring-context-4.1.1.RELEASE进行了测试。
答案 6 :(得分:0)
你不能没有自定义类加载器。
例如,考虑从甚至不提供目录列表的远程服务器加载类的情况。你怎么能迭代那里的所有课程呢?
类加载器接口不提供这样的功能。但如果你使用自己的类加载器(或扩展现有的类加载器),你可以添加这个功能。 无论如何,它并不漂亮。