我个人在任何情况下都不喜欢。它很慢,而且不必要地浪费,你几乎无法优化它。ServiceLoader
我也发现它有点有限 - 如果你想做更多的事情,而不是仅仅按类型搜索,你真的必须竭尽全力。
xbean-finder's ResourceFinder
-
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);
在这里,将仅搜索“一些.jar”,以使用此 ResourceFinder 实例的任何使用情况。
还有一个名为方便类的类,它可以使从类路径中选择URL变得非常容易。UrlSet
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
Where 是一个纯文本文件,它像以前一样包含服务实现的名称。现在假设有人创建了一个 URL。我们可以通过以下方式快速找到它的实现:foo
foo://...
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");
下面介绍如何将这些属性与 另一个可以为您提供无框架 IoC 的小库一起使用。您只需为它提供类名和一些名称值对,它就会构造和注入。xbean-reflect
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();
该库比内置的JavaBeans API更进一步,但更好一些,而不需要您一直到像Guice或Spring这样的完整IoC框架。它支持工厂方法和构造函数参数以及 setter/field 注入。xbean-reflect
为什么ServiceLoader如此有限?
JVM 中不推荐使用的代码会损坏 Java 语言本身。许多东西在被添加到JVM之前被修剪到骨头上,因为你不能在之后修剪它们。这是一个典型的例子。API是有限的,OpenJDK实现大约500行,包括javadoc。ServiceLoader
那里没有什么花哨的东西,更换它很容易。如果它不适合您,请不要使用它。
类路径作用域
撇开API不谈,纯粹在实践中,缩小搜索URL的范围是这个问题的真正解决方案。应用服务器本身具有相当多的 URL,不包括应用程序中的 jar。例如,OSX上的Tomcat 7仅在StandardClassLoader中就有大约40〜URL(这是所有webapp类加载器的父级)。
您的应用程序服务器越大,即使是简单的搜索也需要更长的时间。
如果您打算搜索多个条目,则缓存没有帮助。同样,它可能会增加一些不良泄漏。可能是一个真正的双输场景。
将URL缩小到您真正关心的5或12,您可以执行各种服务加载,并且永远不会注意到命中。