查找实现接口的 Java 类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/435890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Find Java classes implementing an interface
提问by Linor
Some time ago, I came across a piece of code, that used some piece of standard Java functionality to locate the classes that implemented a given interface. I know the functions were hidden in some non-logical place, but they could be used for other classes as the package name implied. Back then I did not need it, so I forgot about it, but now I do, and I can't seem to find the functions again. Where can these functions be found?
前段时间,我遇到了一段代码,它使用一些标准的 Java 功能来定位实现给定接口的类。我知道这些函数隐藏在一些非逻辑的地方,但它们可以用于其他类,正如包名所暗示的那样。当时我不需要它,所以我忘记了,但现在我需要,而且我似乎再也找不到这些功能了。在哪里可以找到这些功能?
Edit: I'm not looking for any IDE functions or anything, but rather something that can be executed within the Java application.
编辑:我不是在寻找任何 IDE 功能或任何东西,而是寻找可以在 Java 应用程序中执行的东西。
采纳答案by Brian Clapper
Awhile ago, I put together a package for doing what you want, and more. (I needed it for a utility I was writing). It uses the ASMlibrary. You can use reflection, but ASM turned out to perform better.
不久前,我整理了一个包来做你想做的事,等等。(我正在编写的实用程序需要它)。它使用ASM库。您可以使用反射,但结果证明 ASM 的性能更好。
I put my package in an open source library I have on my web site. The library is here: http://software.clapper.org/javautil/. You want to start with the with ClassFinderclass.
我把我的包放在我网站上的一个开源库中。图书馆在这里:http: //software.clapper.org/javautil/。您想从 with ClassFinder类开始。
The utility I wrote it for is an RSS reader that I still use every day, so the code does tend to get exercised. I use ClassFinder to support a plug-in API in the RSS reader; on startup, it looks in a couple directory trees for jars and class files containing classes that implement a certain interface. It's a lot faster than you might expect.
我为它编写的实用程序是一个 RSS 阅读器,我仍然每天都在使用它,因此代码确实会得到锻炼。我使用 ClassFinder 来支持 RSS 阅读器中的插件 API;在启动时,它会在几个目录树中查找包含实现某个接口的类的 jar 和类文件。它比您预期的要快得多。
The library is BSD-licensed, so you can safely bundle it with your code. Source is available.
该库是 BSD 许可的,因此您可以安全地将其与您的代码捆绑在一起。来源可用。
If that's useful to you, help yourself.
如果这对您有用,请帮助您自己。
Update: If you're using Scala, you might find this libraryto be more Scala-friendly.
更新:如果你使用 Scala,你可能会发现这个库对 Scala 更友好。
回答by Matt Large
If you were asking from the perspective of working this out with a running program then you need to look to the java.lang.* package. If you get a Class object, you can use the isAssignableFrom method to check if it is an interface of another Class.
如果您是从使用正在运行的程序解决这个问题的角度提出问题,那么您需要查看 java.lang.* 包。如果你得到一个 Class 对象,你可以使用 isAssignableFrom 方法来检查它是否是另一个 Class 的接口。
There isn't a simple built in way of searching for these, tools like Eclipse build an index of this information.
没有一种简单的内置方式来搜索这些信息,像 Eclipse 这样的工具会为这些信息建立一个索引。
If you don't have a specific list of Class objects to test you can look to the ClassLoader object, use the getPackages() method and build your own package hierarchy iterator.
如果您没有要测试的特定 Class 对象列表,您可以查看 ClassLoader 对象,使用 getPackages() 方法并构建您自己的包层次结构迭代器。
Just a warning though that these methods and classes can be quite slow.
只是警告,这些方法和类可能会很慢。
回答by Michael Borgwardt
In full generality, this functionality is impossible. The Java ClassLoader mechanism guarantees only the ability to ask for a class with a specific name (including pacakge), and the ClassLoader can supply a class, or it can state that it does not know that class.
总的来说,这个功能是不可能的。Java ClassLoader 机制保证只能请求具有特定名称的类(包括 pacakge),并且 ClassLoader 可以提供一个类,或者它可以声明它不知道该类。
Classes can be (and frequently are) loaded from remote servers, and they can even be constructed on the fly; it is not difficult at all to write a ClassLoader that returns a valid class that implements a given interface for anyname you ask from it; a List of the classes that implement that interface would then be infinite in length.
类可以(并且经常)从远程服务器加载,甚至可以即时构建;编写一个返回有效类的 ClassLoader 并不困难,该类为您要求的任何名称实现给定的接口;实现该接口的类的列表长度将是无限的。
In practice, the most common case is an URLClassLoader
that looks for classes in a list of filesystem directories and JAR files. So what you need is to get the URLClassLoader
, then iterate through those directories and archives, and for each class file you find in them, request the corresponding Class
object and look through the return of its getInterfaces()
method.
在实践中,最常见的情况是URLClassLoader
在文件系统目录和 JAR 文件列表中查找类。所以你需要的是获取URLClassLoader
,然后遍历这些目录和档案,对于你在其中找到的每个类文件,请求相应的Class
对象并查看其getInterfaces()
方法的返回。
回答by Neil Coffey
Obviously, Class.isAssignableFrom() tells you whether an individualclass implements the given interface. So then the problem is getting the list of classes to test.
显然, Class.isAssignableFrom() 告诉您单个类是否实现了给定的接口。那么问题是获取要测试的类列表。
As far as I'm aware, there's no direct way from Java to ask the class loader for "the list of classes that you could potentiallyload". So you'll have to do this yourself by iterating through the visible jars, calling Class.forName() to load the class, then testing it.
据我所知,有从Java没有直接的方法要求类加载器“的类,您可以在列表中可能加载”。因此,您必须通过遍历可见的 jar 包、调用 Class.forName() 来加载类,然后对其进行测试来自己完成此操作。
However, it's a little easier if you just want to know classes implementing the given interface from those that have actuallybeen loaded:
但是,如果您只想从实际加载的类中了解实现给定接口的类,那就更容易了:
- via the Java Instrumentationframework, you can call Instrumentation.getAllLoadedClasses()
- via reflection, you can query the ClassLoader.classesfield of a given ClassLoader.
- 通过Java Instrumentation框架,您可以调用Instrumentation.getAllLoadedClasses()
- 通过反射,您可以查询给定 ClassLoader的ClassLoader.classes字段。
If you use the instrumentation technique, then (as explained in the link) what happens is that your "agent" class is called essentially when the JVM starts up, and passed an Instrumentation object. At that point, you probably want to "save it for later" in a static field, and then have your main application code call it later on to get the list of loaded classes.
如果您使用检测技术,那么(如链接中所述)发生的情况是您的“代理”类本质上在 JVM 启动时被调用,并传递了一个 Instrumentation 对象。那时,您可能希望在静态字段中“将其保存以备后用”,然后让您的主应用程序代码稍后调用它以获取已加载类的列表。
回答by erickson
The code you are talking about sounds like ServiceLoader
,which was introduced in Java 6 to support a feature that has been defined since Java 1.3or earlier. For performance reasons, this is the recommended approach to find interface implementations at runtime; if you need support for this in an older version of Java, I hope that you'll find my implementationhelpful.
您所谈论的代码听起来像ServiceLoader
,它是在 Java 6 中引入的,用于支持自Java 1.3或更早版本以来定义的功能。出于性能原因,这是在运行时查找接口实现的推荐方法;如果您需要在旧版本的 Java 中对此提供支持,我希望您会发现我的实现有帮助。
There are a couple of implementations of this in earlier versions of Java, but in the Sun packages, not in the core API (I think there are some classes internal to ImageIO that do this). As the code is simple, I'd recommend providing your own implementation rather than relying on non-standard Sun code which is subject to change.
在早期版本的 Java 中有几个实现,但在 Sun 包中,而不是在核心 API 中(我认为 ImageIO 内部有一些类可以做到这一点)。由于代码很简单,我建议您提供自己的实现,而不是依赖可能会更改的非标准 Sun 代码。
回答by Adam Gent
Package Level Annotations
包级注释
I know this question has already been answered a long time ago but another solution to this problem is to use Package Level Annotations.
我知道很久以前就已经回答了这个问题,但是这个问题的另一个解决方案是使用包级别注释。
While its pretty hard to go find all the classes in the JVM its actually pretty easy to browse the package hierarchy.
虽然很难找到 JVM 中的所有类,但实际上很容易浏览包层次结构。
Package[] ps = Package.getPackages();
for (Package p : ps) {
MyAno a = p.getAnnotation(MyAno.class)
// Recursively descend
}
Then just make your annotation have an argument of an array of Class. Then in your package-info.java for a particular package put the MyAno.
然后让你的注释有一个类数组的参数。然后在特定包的 package-info.java 中放入 MyAno。
I'll add more details (code) if people are interested but most probably get the idea.
如果人们感兴趣,我会添加更多细节(代码),但很可能会得到这个想法。
MetaInf Service Loader
MetaInf 服务加载器
To add to @erickson answer you can also use the service loader approach. Kohsuke has an awesome way of generating the the required META-INF stuff you need for the service loader approach:
要添加到@erickson 答案中,您还可以使用服务加载器方法。Kohsuke 有一种很棒的方法来生成服务加载器方法所需的 META-INF 内容:
http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html
http://weblogs.java.net/blog/kohsuke/archive/2009/03/my_project_of_t.html
回答by Nick Robson
Spring can do this for you...
Spring 可以为您做到这一点...
BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
ClassPathBeanDefinitionScanner s = new ClassPathBeanDefinitionScanner(bdr);
TypeFilter tf = new AssignableTypeFilter(CLASS_YOU_WANT.class);
s.addIncludeFilter(tf);
s.scan("package.you.want1", "package.you.want2");
String[] beans = bdr.getBeanDefinitionNames();
N.B. The TypeFilter is important if you want the correct results! You can also use exclusion filters here instead.
注意如果你想要正确的结果,TypeFilter 很重要!您也可以在此处使用排除过滤器。
The Scanner can be found in the spring-context jar, the registry in spring-beans, the type filter is in spring-core.
扫描器可以在 spring-context jar 中找到,注册表在 spring-beans 中,类型过滤器在 spring-core 中。
回答by robertvoliva
I really like the reflections libraryfor doing this.
我真的很喜欢反射库这样做。
It provides a lot of different types of scanners (getTypesAnnotatedWith
, getSubTypesOf
, etc), and it is dead simple to write or extend your own.
它提供了许多不同类型的扫描器(getTypesAnnotatedWith
、getSubTypesOf
等),编写或扩展自己的扫描器非常简单。
回答by Matthias Rothe
You could also use the Extensible Component Scanner (extcos: http://sf.net/projects/extcos) and search all classes implementing an interface like so:
您还可以使用可扩展组件扫描器(extcos:http://sf.net/projects/extcos )并搜索所有实现接口的类,如下所示:
Set<Class<? extends MyInterface>> classes = new HashSet<Class<? extends MyInterface>>();
ComponentScanner scanner = new ComponentScanner();
scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().
from("my.package1", "my.package2").
andStore(thoseImplementing(MyInterface.class).into(classes)).
returning(none());
}
});
This works for classes on the file system, within jars and even for those on the JBoss virtual file system. It's further designed to work within standalone applications as well as within any web or application container.
这适用于文件系统上的类、jar 中的类,甚至适用于 JBoss 虚拟文件系统上的类。它进一步设计为在独立应用程序以及任何 Web 或应用程序容器中工作。