是否有可能找到给定包中的所有类或接口?(快速看了一下e.g. Package,似乎没有。)
当前回答
一般来说,类装入器不允许扫描类路径上的所有类。但通常唯一使用的类加载器是UrlClassLoader,我们可以从中检索目录和jar文件的列表(参见getURLs),并逐个打开它们以列出可用的类。这种方法称为类路径扫描,在Scannotation和Reflections中实现。
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
另一种方法是使用Java可插入注释处理API编写注释处理器,该处理器将在编译时收集所有注释类,并构建索引文件供运行时使用。此机制在ClassIndex库中实现:
// package-info.java
@IndexSubclasses
package my.package;
// your code
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
注意,由于Java编译器自动发现类路径上的任何处理器,因此扫描是完全自动化的,因此不需要额外的设置。
其他回答
如果您不使用任何动态类加载器,您可以搜索类路径,并为每个条目搜索目录或JAR文件。
我一直在尝试使用Reflections库,但在使用它时遇到了一些问题,而且为了简单地获取包上的类,我应该包含太多的jar。
我将在这个重复的问题中发布一个解决方案:如何在包中获得所有类的名称?
答案由sp00m撰写;我添加了一些更正,使其工作:
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
public final class ClassFinder {
private final static char DOT = '.';
private final static char SLASH = '/';
private final static String CLASS_SUFFIX = ".class";
private final static String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the given '%s' package exists?";
public final static List<Class<?>> find(final String scannedPackage) {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final String scannedPath = scannedPackage.replace(DOT, SLASH);
final Enumeration<URL> resources;
try {
resources = classLoader.getResources(scannedPath);
} catch (IOException e) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
}
final List<Class<?>> classes = new LinkedList<Class<?>>();
while (resources.hasMoreElements()) {
final File file = new File(resources.nextElement().getFile());
classes.addAll(find(file, scannedPackage));
}
return classes;
}
private final static List<Class<?>> find(final File file, final String scannedPackage) {
final List<Class<?>> classes = new LinkedList<Class<?>>();
if (file.isDirectory()) {
for (File nestedFile : file.listFiles()) {
classes.addAll(find(nestedFile, scannedPackage));
}
//File names with the $1, $2 holds the anonymous inner classes, we are not interested on them.
} else if (file.getName().endsWith(CLASS_SUFFIX) && !file.getName().contains("$")) {
final int beginIndex = 0;
final int endIndex = file.getName().length() - CLASS_SUFFIX.length();
final String className = file.getName().substring(beginIndex, endIndex);
try {
final String resource = scannedPackage + DOT + className;
classes.add(Class.forName(resource));
} catch (ClassNotFoundException ignore) {
}
}
return classes;
}
}
要使用它,只需调用本例中提到的sp00n的find方法: 如果需要的话,我还添加了类实例的创建。
List<Class<?>> classes = ClassFinder.find("com.package");
ExcelReporting excelReporting;
for (Class<?> aClass : classes) {
Constructor constructor = aClass.getConstructor();
//Create an object of the class type
constructor.newInstance();
//...
}
基于@Staale的回答,为了不依赖第三方库,我将通过检查第一个包的物理位置来实现文件系统方法:
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
...
Class<?>[] foundClasses = new Class<?>[0];
final ArrayList<Class<?>> foundClassesDyn = new ArrayList<Class<?>>();
new java.io.File(
klass.getResource(
"/" + curPackage.replace( "." , "/")
).getFile()
).listFiles(
new java.io.FileFilter() {
public boolean accept(java.io.File file) {
final String classExtension = ".class";
if ( file.isFile()
&& file.getName().endsWith(classExtension)
// avoid inner classes
&& ! file.getName().contains("$") )
{
try {
String className = file.getName();
className = className.substring(0, className.length() - classExtension.length());
foundClassesDyn.add( Class.forName( curPackage + "." + className ) );
} catch (ClassNotFoundException e) {
e.printStackTrace(System.out);
}
}
return false;
}
}
);
foundClasses = foundClassesDyn.toArray(foundClasses);
值得一提的
如果你想要一个包下所有类的列表,你可以用下面的方式使用Reflection:
List<Class> myTypes = new ArrayList<>();
Reflections reflections = new Reflections("com.package");
for (String s : reflections.getStore().get(SubTypesScanner.class).values()) {
myTypes.add(Class.forName(s));
}
这将创建一个类列表,稍后您可以随心所欲地使用它们。
春天
这个例子是针对Spring 4的,但是您也可以在早期版本中找到类路径扫描器。
// create scanner and disable default filters (that is the 'false' argument)
final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
// add include filters which matches all the classes (or use your own)
provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*")));
// get matching classes defined in the package
final Set<BeanDefinition> classes = provider.findCandidateComponents("my.package.name");
// this is how you can load the class type from BeanDefinition instance
for (BeanDefinition bean: classes) {
Class<?> clazz = Class.forName(bean.getBeanClassName());
// ... do your magic with the class ...
}
谷歌番石榴
注意:在版本14中,API仍然被标记为@Beta,所以在生产代码中要小心。
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
for (final ClassPath.ClassInfo info : ClassPath.from(loader).getTopLevelClasses()) {
if (info.getName().startsWith("my.package.")) {
final Class<?> clazz = info.load();
// do something with your clazz
}
}
推荐文章
- .NET反射的成本有多高?
- 指定的子节点已经有一个父节点。你必须先在子对象的父对象上调用removeView() (Android)
- 对于一个布尔字段,它的getter/setter的命名约定是什么?
- 如何获得当前屏幕方向?
- 如何在Android中渲染PDF文件
- 如何计算一个元素在列表中出现的次数
- c++中类似于java的instanceof
- 我如何解决错误“minCompileSdk(31)指定在一个依赖的AAR元数据”在本机Java或Kotlin?
- 如何POST表单数据与Spring RestTemplate?
- Mockito中检测到未完成的存根
- 我应该如何复制字符串在Java?
- “while(true)”循环有那么糟糕吗?
- 这个方法签名中的省略号(…)是干什么用的?
- Java:如何测试调用System.exit()的方法?
- 带有返回类型的Java方法在没有返回语句的情况下编译