从包中获取所有类

时间:2022-09-25 10:28:13

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

假设我有一个java包命令,它包含所有从ICommand继承的类,我能以某种方式获得所有这些类吗?我锁定了一些东西:

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

Is something like that possible?

这样的事情可能吗?

8 个解决方案

#1


14  

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);
    }
}

#2


6  

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

#3


3  

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;
}

#4


1  

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.

从公共类加载器。getresource(字符串名称)。向classloader请求一个与您感兴趣的包中的每个名称对应的类。对所有相关的类装入器重复。

#5


1  

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类型,您必须使用反射检查继承类。

#6


1  

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.

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

#7


0  

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错误。

#8


0  

You could use OpenPojo and 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.

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

#1


14  

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);
    }
}

#2


6  

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

#3


3  

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;
}

#4


1  

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.

从公共类加载器。getresource(字符串名称)。向classloader请求一个与您感兴趣的包中的每个名称对应的类。对所有相关的类装入器重复。

#5


1  

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类型,您必须使用反射检查继承类。

#6


1  

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.

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

#7


0  

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错误。

#8


0  

You could use OpenPojo and 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.

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