15

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

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

这样的事情可能吗?

4

9 回答 9

20

这是一个基本示例,假设类不是 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);
    }
}
于 2009-11-27T21:19:27.293 回答
6

下面是使用 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());
}
于 2009-11-28T00:30:09.917 回答
3

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

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

    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;
}
于 2009-11-27T21:12:55.267 回答
2

如果你不想使用外部依赖并且你想在你的 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;
}

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

于 2019-11-08T20:19:49.320 回答
1

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

于 2009-11-27T21:04:40.583 回答
1

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

看看这个线程。

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

于 2009-11-27T21:10:09.370 回答
1

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

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

于 2009-11-27T23:31:56.227 回答
0

使用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();
}

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

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

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

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

于 2009-12-18T20:47:45.823 回答
0

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

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

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

于 2012-08-28T04:04:14.913 回答