是否有可能找到给定包中的所有类或接口?(快速看了一下e.g. Package,似乎没有。)


当前回答

几乎所有的答案要么使用reflection,要么从文件系统读取类文件。如果尝试从文件系统读取类,则在将应用程序打包为JAR或其他格式时可能会出错。此外,您可能不想为此目的使用单独的库。

这里有另一种方法,它是纯java,不依赖于文件系统。

import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class PackageUtil {

    public static Collection<Class> getClasses(final String pack) throws Exception {
        final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null);
        return StreamSupport.stream(fileManager.list(StandardLocation.CLASS_PATH, pack, Collections.singleton(JavaFileObject.Kind.CLASS), false).spliterator(), false)
                .map(javaFileObject -> {
                    try {
                        final String[] split = javaFileObject.getName()
                                .replace(".class", "")
                                .replace(")", "")
                                .split(Pattern.quote(File.separator));

                        final String fullClassName = pack + "." + split[split.length - 1];
                        return Class.forName(fullClassName);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                })
                .collect(Collectors.toCollection(ArrayList::new));
    }
}

Java 8不是必须的。你可以使用for循环代替流。 你可以这样测试

public static void main(String[] args) throws Exception {
    final String pack = "java.nio.file"; // Or any other package
    PackageUtil.getClasses(pack).stream().forEach(System.out::println);
}

其他回答

我一直在尝试使用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();
    //...
}

一般来说,类装入器不允许扫描类路径上的所有类。但通常唯一使用的类加载器是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编译器自动发现类路径上的任何处理器,因此扫描是完全自动化的,因此不需要额外的设置。

您可能应该看看开源的Reflections库。有了它,你可以很容易地实现你想要的。

首先,设置反射索引(这有点乱,因为搜索所有类默认是禁用的):

List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());

Reflections reflections = new Reflections(new ConfigurationBuilder()
    .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
    .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
    .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.your.package"))));

然后你可以查询给定包中的所有对象:

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);

org。反思版0.10:

org.reflections.scanners.SubTypesScanner 

and

org.reflections.Reflections.getAllTypes() 

弃用。我userd:

public Set<String> getEntityNamesInPackage(String packagePath) {
    Reflections reflections = new Reflections(new ConfigurationBuilder()
                    .filterInputsBy(new FilterBuilder().includePackage(packagePath))
                    .setUrls(ClasspathHelper.forPackage(packagePath))
                    .setScanners(SubTypes.filterResultsBy(s -> true)));

    return reflections.getAll(SubTypes).stream()
            .filter(s -> s.startsWith(packagePath))
            .collect(Collectors.toSet());
}

我无法为如此简单的东西找到一个简短的工作片段。所以在这里,我自己做了一段时间后:

Reflections reflections =
    new Reflections(new ConfigurationBuilder()
            .filterInputsBy(new FilterBuilder().includePackage(packagePath))
            .setUrls(ClasspathHelper.forPackage(packagePath))
            .setScanners(new SubTypesScanner(false)));

Set<String> typeList = reflections.getAllTypes();

它使用组织反射。