java 从包中获取所有类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1810614/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 18:05:27  来源:igfitidea点击:

Getting all Classes from a Package

javareflectionpackage

提问by JustMaximumPower

Lets say I have a java package commandswhich contains classes that all inherit from ICommandcan I get all of those classes somehow? I'm locking for something among the lines of:

假设我有一个 java 包commands,其中包含所有继承自的类,ICommand我可以以某种方式获取所有这些类吗?我正在锁定以下内容:

Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real 

Is something like that possible?

这样的事情可能吗?

回答by BalusC

Here's a basic example, assuming that classes are not JAR-packaged:

这是一个基本示例,假设类不是 JAR 打包的:

// Prepare.
String packageName = "com.example.commands";
List<Class<ICommand>> commands = new ArrayList<Class<ICommand>>();
URL root = Thread.currentThread().getContextClassLoader().getResource(packageName.replace(".", "/"));

// Filter .class files.
File[] files = new File(root.getFile()).listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        return name.endsWith(".class");
    }
});

// Find classes implementing ICommand.
for (File file : files) {
    String className = file.getName().replaceAll(".class$", "");
    Class<?> cls = Class.forName(packageName + "." + className);
    if (ICommand.class.isAssignableFrom(cls)) {
        commands.add((Class<ICommand>) cls);
    }
}

回答by Pascal Thivent

Below, an implementation using the JSR-199 API i.e. classes from javax.tools.*:

下面是使用 JSR-199 API 的实现,即来自 的类javax.tools.*

List<Class> commands = new ArrayList<Class>();

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(
        null, null, null);

Location location = StandardLocation.CLASS_PATH;
String packageName = "commands";
Set<JavaFileObject.Kind> kinds = new HashSet<JavaFileObject.Kind>();
kinds.add(JavaFileObject.Kind.CLASS);
boolean recurse = false;

Iterable<JavaFileObject> list = fileManager.list(location, packageName,
        kinds, recurse);

for (JavaFileObject javaFileObject : list) {
    commands.add(javaFileObject.getClass());
}

回答by Bozho

Here is an utility method, using Spring.

这是一个使用 Spring 的实用方法。

Details about the pattern can be found here

可以在此处找到有关该模式的详细信息

    public static List<Class> listMatchingClasses(String matchPattern) throws IOException {
    List<Class> classes = new LinkedList<Class>();
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(matchPattern);

    for (Resource resource : resources) {
        Class<?> clazz = getClassFromResource(resource);
        classes.add(clazz);
    }

    return classes;
}



public static Class getClassFromResource(Resource resource) {
    try {
        String resourceUri = resource.getURI().toString();
        resourceUri = resourceUri.replace(esourceUri.indexOf(".class"), "").replace("/", ".");
        // try printing the resourceUri before calling forName, to see if it is OK.
        return Class.forName(resourceUri);
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return null;
}

回答by bmargulies

Start with public Classloader.getResources(String name). Ask the classloader for a class corresponding to each name in the package you are interested. Repeat for all classloaders of relevance.

从 public Classloader.getResources(String name) 开始。向类加载器询问与您感兴趣的包中的每个名称对应的类。对所有相关的类加载器重复。

回答by monksy

Yes but its not the easiest thing to do. There are lots of issues with this. Not all of the classes are easy to find. Some classes could be in a: Jar, as a class file, over the network etc.

是的,但它不是最容易做的事情。这有很多问题。并不是所有的课程都很容易找到。一些类可能在:Jar、作为类文件、通过网络等。

Take a look at this thread.

看看这个线程。

To make sure they were the ICommand type then you would have to use reflection to check for the inheriting class.

为了确保它们是 ICommand 类型,您必须使用反射来检查继承类。

回答by irreputable

This would be a very useful tool we need, and JDK should provide some support.

这将是我们需要的一个非常有用的工具,JDK 应该提供一些支持。

But it's probably better done during build. You know where all your class files are and you can inspect them statically and build a graph. At runtime you can query this graph to get all subtypes. This requires more work, but I believe it really belongs to the build process.

但在构建过程中可能做得更好。您知道所有类文件的位置,并且可以静态检查它们并构建图形。在运行时,您可以查询此图以获取所有子类型。这需要更多的工作,但我相信它确实属于构建过程。

回答by Nicolas

Using Johannes Link's ClasspathSuite, I was able to do it like this:

使用Johannes Link 的 ClasspathSuite,我可以这样做:

import org.junit.extensions.cpsuite.ClassTester;
import org.junit.extensions.cpsuite.ClasspathClassesFinder;

public static List<Class<?>> getClasses(final Package pkg, final boolean includeChildPackages) {
    return new ClasspathClassesFinder(new ClassTester() {
        @Override public boolean searchInJars() { return true; }
        @Override public boolean acceptInnerClass() { return false; }
        @Override public boolean acceptClassName(String name) {
            return name.startsWith(pkg.getName()) && (includeChildPackages || name.indexOf(".", pkg.getName().length()) != -1);
        }
        @Override public boolean acceptClass(Class<?> c) { return true; }
    }, System.getProperty("java.class.path")).find();
}

The ClasspathClassesFinder looks for class files and jars in the system classpath.

ClasspathClassesFinder 在系统类路径中查找类文件和 jar。

In your specific case, you could modify acceptClass like this:

在您的特定情况下,您可以像这样修改 acceptClass:

@Override public boolean acceptClass(Class<?> c) {
    return ICommand.class.isAssignableFrom(c);
}

One thing to note: be careful what you return in acceptClassName, as the next thing ClasspathClassesFinder does is to load the class and call acceptClass. If acceptClassName always return true, you'll end up loading every class in the classpath and that may cause an OutOfMemoryError.

需要注意的一件事:小心你在 acceptClassName 中返回的内容,因为 ClasspathClassesFinder 做的下一件事是加载类并调用 acceptClass。如果acceptClassName 总是返回true,你最终会加载类路径中的每个类,这可能会导致OutOfMemoryError。

回答by Osman Shoukry

You could use OpenPojoand do this:

您可以使用OpenPojo并执行以下操作:

final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);

Then you can go over the list and perform any functionality you desire.

然后您可以查看列表并执行您想要的任何功能。

回答by tirz

If you do not want to use external depencies and you want to work on your IDE / on a JAR file, you can try this:

如果您不想使用外部依赖项并且想在您的 IDE / JAR 文件上工作,您可以尝试以下操作:

public static List<Class<?>> getClassesForPackage(final String pkgName) throws IOException, URISyntaxException {
    final String pkgPath = pkgName.replace('.', '/');
    final URI pkg = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(pkgPath)).toURI();
    final ArrayList<Class<?>> allClasses = new ArrayList<Class<?>>();

    Path root;
    if (pkg.toString().startsWith("jar:")) {
        try {
            root = FileSystems.getFileSystem(pkg).getPath(pkgPath);
        } catch (final FileSystemNotFoundException e) {
            root = FileSystems.newFileSystem(pkg, Collections.emptyMap()).getPath(pkgPath);
        }
    } else {
        root = Paths.get(pkg);
    }

    final String extension = ".class";
    try (final Stream<Path> allPaths = Files.walk(root)) {
        allPaths.filter(Files::isRegularFile).forEach(file -> {
            try {
                final String path = file.toString().replace('/', '.');
                final String name = path.substring(path.indexOf(pkgName), path.length() - extension.length());
                allClasses.add(Class.forName(name));
            } catch (final ClassNotFoundException | StringIndexOutOfBoundsException ignored) {
            }
        });
    }
    return allClasses;
}

From: Can you find all classes in a package using reflection?

来自:你能使用反射找到包中的所有类吗?