Java,获取可用于实现特定接口的 URLClassLoader 的所有类
我正在开发一个命令行应用程序,它在运行时加载用户指定的文本翻译器(通过命令行参数提供的类文件/jar 的路径)。基本上,我采用该参数并使用它来创建 URLClassLoader。然后我需要找到实现 Transable 接口的 URLClassloader 可用的所有类。
现在我只允许此命令行参数成为其中包含类文件的目录。使解决方案相当简单(代码如下)。但老实说,我不喜欢这个解决方案,因为它会破坏 jar 文件、jar 文件目录等...此外,这显然会破坏任何具有已定义包的类,因为 loadClass 需要包括包在内的全名。有人有更好的方法吗?
File d = new File(path);
if(d.isDirectory()) {
URL url = d.toURI().toURL();
ClassLoader cl = new URLClassLoader(new URL[]{url});
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
};
for(File f : d.listFiles(filter)) {
String name = f.getName().substring(0, f.getName().indexOf("."));
String key = "";
if(name.endsWith("Translator")) {
key = name.substring(0, name.indexOf("Translator"));
}
else if(name.endsWith("translator")) {
key = name.substring(0, name.indexOf("translator"));
}
else
key = name;
Class c = cl.loadClass(name);
if(Transable.class.isAssignableFrom(c)) {
Transable t = (Transable)c.newInstance();
env.registerTranslator(key, t);
}
else {
System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
}
}
}
else {
throw new Error("NOT IMPLEMENTED");
}
I am working on a command line app that loads user specified text translators at runtime (path to class files/jar provided via command line arg). Basically I am taking that argument and using it to create a URLClassLoader. Then I need to find all classes available to the URLClassloader that implement the Transable interface.
Right now I am only allowing this command line arg to be a directory with class files in it. Making the solution fairly simple (code below). But honestly I don't like the solution as it breaks down for jar files, directory of jar files, etc... Also, this obviously breaks down for any classes with a defined package, as loadClass needs the full name including the package. Anyone have a better method?
File d = new File(path);
if(d.isDirectory()) {
URL url = d.toURI().toURL();
ClassLoader cl = new URLClassLoader(new URL[]{url});
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
};
for(File f : d.listFiles(filter)) {
String name = f.getName().substring(0, f.getName().indexOf("."));
String key = "";
if(name.endsWith("Translator")) {
key = name.substring(0, name.indexOf("Translator"));
}
else if(name.endsWith("translator")) {
key = name.substring(0, name.indexOf("translator"));
}
else
key = name;
Class c = cl.loadClass(name);
if(Transable.class.isAssignableFrom(c)) {
Transable t = (Transable)c.newInstance();
env.registerTranslator(key, t);
}
else {
System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
}
}
}
else {
throw new Error("NOT IMPLEMENTED");
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果你继续沿着这条路走下去,你可能只需要暴力破解即可。据我所知,默认类加载器甚至不会将类加载到 JVM 中,除非以某种方式引用它。这意味着,除非您知道加载它们的完全限定类名,否则您的某些类基本上是不可见的。
您可能需要重新考虑您的要求。因为加载一组已为其指定类名的转换器会容易得多。
You may just have to brute force it if you continue down this road. To my knowledge the default class loader will not even load a class into the JVM unless it is referenced somehow. Which means, some of your classes will be basically invisible unless you know their fully qualified class name to load them.
You may want to reconsider your requirements. As it would be far easier to load a set of Translators that you have been given the class names for.
原则上,这不适用于任意类加载器,因为它们可能使用任何可以想象到的方式来实际加载类,并且根本没有任何“目录侦听”功能。
每当
loadClass
到来时,类加载器甚至可以动态生成类(即类的字节码),并想象一个 TransableClassloader,其中每个这样自动定义的类都将实现您的接口 - 您的程序将永远不会结束。也就是说,对于
URLClassloader
,您可以使用getURLs()
,对于jar:
和file:
URL您可以使用 JarFile 或 File api 来获取要尝试的文件名(以及类名)列表。由于 URL 中给出了包层次结构的根,因此找到正确的包名称也不困难。(如果您需要更多详细信息,请说出来。)
编辑:对于包名称,它们对应于层次结构内的目录名称。因此,当您有一个对应于(例如)
dir/classes
的基本 URL 时,并找到名为dir/classes/com/company/gui/SimpleTranslator.class
的类文件code>,它对应于类com.company.gui.SimpleTranslator
。因此,删除基本前缀并将
/
替换为.
(并删除.class
)。 (在 JarFile 中,您不必删除前缀。)实际上,如果您使用递归方法来遍历文件层次结构,您可以使用相同的方法构建包名称,只需附加字符串(给它们作为下一个递归调用的参数):
In principle this can't work for arbitrary classloaders, as they may use any way imaginable to actually load the classes, and not have any "directory listening" function at all.
A classloader might even generate classes (i.e. the bytecode for the classes) on the fly whenever a
loadClass
comes, and imagine a TransableClassloader where each such automatically defined class would implement your interface - your program would never end.That said, for an
URLClassloader
you can usegetURLs()
, and for thejar:
andfile:
URL you can use theJarFile
orFile
api to get the list of filenames (and thus Classnames) to try. As you have the root of your package hierarchy given in the URL, finding the right package name is not difficult, too.(If you need more details, say it.)
Edit: For the package names, they correspond to the directory names inside of your hierarchy. So, when you have a base URL which corresponds to (say)
dir/classes
, and find a class-file nameddir/classes/com/company/gui/SimpleTranslator.class
, it corresponds to classcom.company.gui.SimpleTranslator
.So, remove the base prefix and replace
/
by.
(and cut of the.class
). (In a JarFile you don't have to cut a prefix off.)Actually, if you use a recursive method to traverse your File hierarchy, you can build up your package-name with the same method, simply by appending Strings (give them as a parameter to the next recursive Invocation):
这可能符合要求。如果没有,您应该能够浏览他们的来源以了解什么对您有用。 http://code.google.com/p/reflections/
This may fit the bill. If not, you ought to be able to look through their source to get an idea of what will work for you. http://code.google.com/p/reflections/
您应该使用 Java 的服务提供者接口 (SPI) 和 ServiceLoader 类(Java 6 中引入)。 SPI 几乎是执行您在 Java 中描述的操作的标准方法。
请参阅官方 Java 教程了解如何创建服务提供程序以及如何在运行时通过 ServiceLoader 使用它。
Instead of searching for implementors explicitly, you should uss Java's Service Provider Interface (SPI) and the ServiceLoader class (introduced in Java 6). SPI is a pretty much a standard way to do what you are describing in Java.
Please see the official Java tutorial on how to create a service provider and how to use it, at runtime, with ServiceLoader.
这有帮助吗?
由于类 c = cl.loadClass(name);
类方法 getInterfaces() 返回类数组
检查每个类名称是否与转换器类名称匹配。
Would this help?
Since Class c = cl.loadClass(name);
Class method getInterfaces() returns an array of classes
Check each class name for match to translator class name.