可以说我有一个java包commands
其中包含所有继承自ICommand
的类可以以某种方式获得所有这些类吗?我正在锁定以下几行内容:获取包中的所有类
Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real
是这样的可能吗?
可以说我有一个java包commands
其中包含所有继承自ICommand
的类可以以某种方式获得所有这些类吗?我正在锁定以下几行内容:获取包中的所有类
Package p = Package.getPackage("commands");
Class<ICommand>[] c = p.getAllPackagedClasses(); //not real
是这样的可能吗?
这里有一个简单的例子,假设类不是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,尽管有一点小小的改进:''root.getFile()'应该是'URLDecoder.decode(root.getFile(),“UTF-8”)''在classloader'路径中有空格的情况下。 'getResource()'会将其转换为'%20',以及其他字符,我想。 – Aquillo 2013-05-04 10:12:28
从public Classloader.getResources(String name)开始。请向类加载器提供与您感兴趣的包中的每个名称相对应的类。重复所有相关的类加载器。
是的,但它不是最容易做的事情。这有很多问题。并非所有的课程都很容易找到。某些类可以在一个:罐,作为一个类文件,通过网络等
为了确保他们的ICommand的类型,那么你将不得不使用反射来检查继承类。
这是一个使用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;
}
这将是我们需要一个非常有用的工具, JDK应该提供一些支持。
但是在构建过程中可能会做得更好。你知道你所有的类文件在哪里,你可以静态地检查它们并建立一个图表。在运行时你可以查询这个图来获得所有的子类型。这需要更多的工作,但我相信它确实属于构建过程。
类似于JSR-199 API?请参阅http://stackoverflow.com/questions/1810614/getting-all-classes-from-a-package/1811120#1811120 – 2009-11-28 00:31:30
下面,从javax.tools.*
使用JSR-199 API即类的实现:
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());
}
有趣的是,但是在Eclipse和CLI中'ToolProvider.getSystemJavaCompiler()'返回'null'?是更需要的东西? – BalusC 2009-11-28 00:51:19
挖掘之后,这显然需要一个JDK而不是JRE(但是我在CLI中有这个,稍后我会挖掘为什么这种方法不起作用)。这意味着您需要在最终/产品环境中安装JDK才能使其运行。这可能是个不错的选择。 – BalusC 2009-11-28 00:59:49
事实上,你需要一个JDK来获得一个非'null'编译器对象(见http://bit.ly/89HtA0),所以这个API并不是真正的目标桌面。尽管(大多数应用程序服务器毕竟使用JDK,例如用于JSP编译),但在服务器端应该没问题。但当然,可能有例外。实际上,我只是想表明JDK中有一些支持,即使这不是Compiler API的完美用例(这里的用法很糟糕)。这个API可以做更多的事情,例如编译动态地在内存中生成的Java源代码(这已经更有趣了)。 – 2009-11-28 02:50:51
使用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查找类文件和罐子在系统类路径中。
在特定情况下,可以修改acceptClass这样的:
@Override public boolean acceptClass(Class<?> c) {
return ICommand.class.isAssignableFrom(c);
}
有一点要注意:要小心你在acceptClassName回报什么,接下来的事情ClasspathClassesFinder确实被加载类和调用acceptClass 。如果acceptClassName总是返回true,则最终会加载类路径中的每个类,并可能导致OutOfMemoryError。
你可以使用OpenPojo和做到这一点:
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
然后你可以在列表上,并执行任何你想要的功能。
请编辑这个清晰。什么是'javapackage'。什么是'命令愿望'。 – bmargulies 2009-11-27 21:00:17
主题主题完美:http://google.com/search?q=Getting+all+Classes+from+a+Package =) – BalusC 2009-11-27 21:10:07
@balusc,但/ correct/answer很难。 – 2009-11-28 09:09:17