ServiceLoader
我个人在任何情况下都不喜欢。它很慢而且不必要的浪费,你几乎无法优化它。
我也发现它有点受限——如果你想做的不仅仅是按类型搜索,你真的必须不遗余力。
xbean-finder 的资源查找器
- ResourceFinder是一个独立的 java 文件,能够替代 ServiceLoader 的使用。复制/粘贴重用没有问题。它是一个 java 文件,获得 ASL 2.0 许可,可从 Apache 获得。
在我们的注意力变得太短之前,这是它如何替换 ServiceLoader
ResourceFinder finder = new ResourceFinder("META-INF/services/");
List<Class<? extends Plugin>> impls = finder.findAllImplementations(Plugin.class);
这将META-INF/services/org.acme.Plugin
在您的类路径中找到所有实现。
请注意,它实际上并没有实例化所有实例。选择您想要的,您只需一个newInstance()
电话即可获得实例。
为什么这么好看?
newInstance()
使用适当的异常处理进行调用有多难?不难。
- 可以自由地只实例化你想要的那些是很好的。
- 现在你可以支持构造函数参数了!
缩小搜索范围
如果您只想检查特定的 URL,您可以轻松地做到这一点:
URL url = new File("some.jar").toURI().toURL();
ResourceFinder finder = new ResourceFinder("META-INF/services/", url);
在这里,将只搜索“some.jar”这个 ResourceFinder 实例的任何用法。
还有一个名为的便利类UrlSet
,它可以使从类路径中选择 URL 变得非常容易。
ClassLoader webAppClassLoader = Thread.currentThread().getContextClassLoader();
UrlSet urlSet = new UrlSet(webAppClassLoader);
urlSet = urlSet.exclude(webAppClassLoader.getParent());
urlSet = urlSet.matching(".*acme-.*.jar");
List<URL> urls = urlSet.getUrls();
替代“服务”风格
假设您想应用类型概念来重新设计 URL 处理并为特定协议ServiceLoader
查找/加载。java.net.URLStreamHandler
以下是您在类路径中布局服务的方式:
META-INF/java.net.URLStreamHandler/foo
META-INF/java.net.URLStreamHandler/bar
META-INF/java.net.URLStreamHandler/baz
foo
和以前一样包含服务实现名称的纯文本文件在哪里。现在假设有人创建了一个foo://...
URL。我们可以通过以下方式快速找到实现:
ResourceFinder finder = new ResourceFinder("META-INF/");
Map<String, Class<? extends URLStreamHandler>> handlers = finder.mapAllImplementations(URLStreamHandler.class);
Class<? extends URLStreamHandler> fooHandler = handlers.get("foo");
替代“服务”风格 2
假设您想将一些配置信息放入您的服务文件中,因此它包含的不仅仅是类名。这是将服务解析为属性文件的另一种样式。按照惯例,一个键是类名,其他键是可注入属性。
所以这red
是一个属性文件
META-INF/org.acme.Plugin/red
META-INF/org.acme.Plugin/blue
META-INF/org.acme.Plugin/green
您可以像以前一样查找内容。
ResourceFinder finder = new ResourceFinder("META-INF/");
Map<String,Properties> plugins = finder.mapAllProperties(Plugin.class.getName());
Properties redDefinition = plugins.get("red");
以下是如何将这些属性与xbean-reflect
另一个可以为您提供无框架 IoC 的小库一起使用。你只需给它类名和一些名称值对,它就会构造和注入。
ObjectRecipe recipe = new ObjectRecipe(redDefinition.remove("className").toString());
recipe.setAllProperties(redDefinition);
Plugin red = (Plugin) recipe.create();
red.start();
以下是长篇“拼写”出来的样子:
ObjectRecipe recipe = new ObjectRecipe("com.example.plugins.RedPlugin");
recipe.setProperty("myDateField","2011-08-29");
recipe.setProperty("myIntField","100");
recipe.setProperty("myBooleanField","true");
recipe.setProperty("myUrlField","http://www.stackoverflow.com");
Plugin red = (Plugin) recipe.create();
red.start();
该xbean-reflect
库比内置的 JavaBeans API 更上一层楼,但更好一点,无需您一直使用完整的 IoC 框架,如 Guice 或 Spring。它支持工厂方法和构造函数参数以及设置器/字段注入。
为什么 ServiceLoader 如此有限?
JVM 中不推荐使用的代码会损坏 Java 语言本身。很多东西在添加到 JVM 之前都被修剪到了骨子里,因为你不能在之后修剪它们。这ServiceLoader
是一个典型的例子。API 是有限的,OpenJDK 的实现大约有 500 行,包括 javadoc。
那里没有什么花哨的东西,更换它很容易。如果它对您不起作用,请不要使用它。
类路径范围
撇开 API 不谈,在纯粹的实用性上,缩小搜索的 URL 范围才是真正解决这个问题的方法。应用服务器本身有很多 URL,不包括应用程序中的 jar。例如,OSX 上的 Tomcat 7 仅在 StandardClassLoader 中就有大约 40~ 个 URL(这是所有 webapp 类加载器的父级)。
您的应用程序服务器越大,即使是简单的搜索也需要更长的时间。
如果您打算搜索多个条目,缓存将无济于事。同样,它可能会增加一些不良泄漏。可能是一个真正的双输局面。
将 URL 缩小到您真正关心的 5 或 12 个,您可以执行各种服务加载,而不会注意到命中。