私は個人的には好きではありませんServiceLoader
。遅くて不必要に無駄が多く、最適化するためにできることはほとんどありません。
また、少し制限があることもわかりました。タイプだけで検索する以上のことをしたい場合は、本当に邪魔にならないようにする必要があります。
xbean-finder の ResourceFinder
- ResourceFinderは、ServiceLoader の使用を置き換えることができる自己完結型の Java ファイルです。コピー&ペーストの再利用は問題ありません。これは 1 つの 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
これにより、クラスパス内のすべての実装が見つかります。
実際にすべてのインスタンスをインスタンス化するわけではないことに注意してください。必要なものを選択すると、1 回のnewInstance()
呼び出しでインスタンスが作成されます。
なぜこれがいいのですか?
newInstance()
適切な例外処理で呼び出すのはどれくらい難しいですか? 難しくない。
- 必要なものだけを自由にインスタンス化できるのは素晴らしいことです。
- コンストラクタ引数をサポートできるようになりました!
検索範囲の絞り込み
特定の URL だけを確認したい場合は、次のように簡単に行うことができます。
URL url = new File("some.jar").toURI().toURL();
ResourceFinder finder = new ResourceFinder("META-INF/services/", url);
ここでは、この ResourceFinder インスタンスの使用時に「some.jar」のみが検索されます。
また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();
代替の「サービス」スタイル
型の概念を適用してServiceLoader
URL 処理を再設計java.net.URLStreamHandler
し、特定のプロトコルの検索/読み込みを行いたいとします。
クラスパスでサービスをレイアウトする方法は次のとおりです。
META-INF/java.net.URLStreamHandler/foo
META-INF/java.net.URLStreamHandler/bar
META-INF/java.net.URLStreamHandler/baz
Wherefoo
は、前と同じようにサービス実装の名前を含むプレーン テキスト ファイルです。誰かが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
サービス ファイルに構成情報を入れたいとします。そのため、クラス名だけではありません。サービスをプロパティ ファイルに解決する別のスタイルを次に示します。慣例により、1 つのキーはクラス名になり、他のキーは注入可能なプロパティになります。
ここ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 を提供できるもう 1 つの小さなライブラリである でこれらのプロパティを使用する方法を次に示します。クラス名といくつかの名前と値のペアを指定するだけで、構築して注入します。
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 よりも一歩進んでいますが、Guice や Spring などの完全な IoC フレームワークを使用する必要がなければ、少し優れています。ファクトリ メソッド、コンストラクタ引数、セッター/フィールド インジェクションをサポートしています。
ServiceLoader が限定されているのはなぜですか?
JVM の非推奨コードは、Java 言語自体に損害を与えます。多くのものは、JVM に追加される前に骨に合わせてトリミングされます。後でそれらをトリミングすることはできないからです。はそのServiceLoader
最たる例です。API は限られており、OpenJDK の実装は javadoc を含めて約 500 行です。
そこには派手なものは何もなく、交換は簡単です。うまくいかない場合は、使用しないでください。
クラスパスのスコープ
API は別として、純粋に実用的には、検索される URL の範囲を狭めることが、この問題の真の解決策です。アプリケーション サーバーには、アプリケーション内の jar を除いて、それ自体で非常に多くの URL があります。たとえば、OSX 上の Tomcat 7 には、StandardClassLoader だけで約 40~ の URL があります (これはすべての webapp クラスローダーの親です)。
アプリ サーバーが大きいほど、単純な検索でも時間がかかります。
複数のエントリを検索する場合、キャッシュは役に立ちません。同様に、それはいくつかの悪いリークを追加する可能性があります. 実際の負け負けのシナリオになる可能性があります。
本当に関心のある 5 つまたは 12 の URL に絞り込むと、あらゆる種類のサービスの読み込みを行うことができ、ヒットに気付くことはありません。