假设我有一个java包commands
,它包含所有继承自ICommand
的类,我能以某种方式获得所有这些类吗?我正在锁定以下内容:
Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real
这样的事情可能吗?
答案 0 :(得分:14)
这是一个基本的例子,假设类不是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);
}
}
答案 1 :(得分: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());
}
答案 2 :(得分:3)
这是一个使用Spring的实用方法。
可以找到有关模式的详细信息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;
}
答案 3 :(得分:1)
从公共Classloader.getResources(String name)开始。向类加载器询问与您感兴趣的包中的每个名称对应的类。重复所有相关的类加载器。
答案 4 :(得分:1)
是的,但这不是最简单的事情。这有很多问题。并非所有课程都很容易找到。有些类可以在:Jar,作为类文件,通过网络等。
要确保它们是ICommand类型,那么您必须使用反射来检查继承类。
答案 5 :(得分:1)
这将是我们需要的非常有用的工具,JDK应该提供一些支持。
但是在构建期间可能会做得更好。您知道所有类文件的位置,您可以静态检查它们并构建图形。在运行时,您可以查询此图以获取所有子类型。这需要更多的工作,但我相信它确实属于构建过程。
答案 6 :(得分:0)
使用Johannes Link's 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。
答案 7 :(得分:0)
您可以使用OpenPojo并执行此操作:
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
然后你可以浏览列表并执行你想要的任何功能。
答案 8 :(得分:0)
如果您不想使用外部依赖关系,并且想要在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;
}