如何在运行时在实现定义接口的类路径中发现类?
ServiceLoader 很适合(我想,我没用过),但我需要在 Java 1.5 中使用。
如何在运行时在实现定义接口的类路径中发现类?
ServiceLoader 很适合(我想,我没用过),但我需要在 Java 1.5 中使用。
Java 1.5 没有为此内置任何内容。我自己实现了它;这不是太复杂。但是,当我们升级到 Java 6 时,我将不得不将对我的实现的调用替换为对ServiceLoader
. 我本可以在应用程序和加载器之间定义一个小桥梁,但我只在少数地方使用它,而包装器本身将是 ServiceLoader 的一个很好的候选者。
这是核心思想:
public <S> Iterable<S> load(Class<S> ifc) throws Exception {
ClassLoader ldr = Thread.currentThread().getContextClassLoader();
Enumeration<URL> e = ldr.getResources("META-INF/services/" + ifc.getName());
Collection<S> services = new ArrayList<S>();
while (e.hasMoreElements()) {
URL url = e.nextElement();
InputStream is = url.openStream();
try {
BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
while (true) {
String line = r.readLine();
if (line == null)
break;
int comment = line.indexOf('#');
if (comment >= 0)
line = line.substring(0, comment);
String name = line.trim();
if (name.length() == 0)
continue;
Class<?> clz = Class.forName(name, true, ldr);
Class<? extends S> impl = clz.asSubclass(ifc);
Constructor<? extends S> ctor = impl.getConstructor();
S svc = ctor.newInstance();
services.add(svc);
}
}
finally {
is.close();
}
}
return services;
}
更好的异常处理留给读者练习。此外,该方法可以参数化以接受调用者选择的 ClassLoader。
javax.imageio.spi.ServiceRegistry
与以前的 Java 版本等效。它从 Java 1.4 开始可用。
它看起来不像一个通用的实用程序类,但它是。它甚至比 更强大ServiceLoader
,因为它允许对返回的提供程序的顺序进行一些控制并直接访问注册表。
请参阅http://docs.oracle.com/javase/7/docs/api/index.html?javax/imageio/spi/ServiceRegistry.html
ServiceLoader 非常基础,从 1.3 开始就在 JDK 中(非正式地)使用。ServiceLoader 终于使它成为一等公民。它只是查找为您的接口命名的资源文件,该文件基本上捆绑在库 jar 的 META-INF 目录中。
该文件包含要加载的类的名称。
因此,您将拥有一个名为:
META-INF/services/com.example.your.interface
里面只有一行:com.you.your.interfaceImpl。
代替 ServiceLoader,我喜欢 Netbeans Lookup。它适用于 1.5(也许是 1.4)。
开箱即用,它的功能与 ServiceLoader 完全相同,而且使用起来很简单。但它提供了更多的灵活性。
这是一个链接: http: //openide.netbeans.org/lookup/
这是一篇关于 ServiceLoader 的文章,但它在底部提到了 Netbeans Lookup: http ://weblogs.java.net/blog/timboudreau/archive/2008/08/simple_dependen.html
很遗憾,
Java 1.5 没有为此内置任何内容...
只是真相的一部分。
周围有非标sun.misc.Service
。
http://www.docjar.com/docs/api/sun/misc/Service.html
请注意,它不是标准 J2SE API 的一部分!它是 Sun JDK 的非标准部分。所以你不能依赖它,如果你使用,比如说,JRockit
。
这是一个老问题,但另一种选择是使用Package Level Annotations。请参阅我的答案:查找实现接口的 Java 类
包级注解是 package-info.java 类中的注解。
JAXB 使用它而不是服务加载器。我也认为它比服务加载器更灵活。
没有可靠的方法可以知道类路径中有哪些类。根据其文档,ServiceLoader 依赖外部文件来告诉它要加载哪些类;你可能想做同样的事情。基本思想是有一个带有要加载的类名称的文件,然后使用反射来实例化它/它们。