私が作業した最後のアプリケーションでは、当時私には知られていないSPI
ps = Service.providers(java.sql.Driver.class); try { while (ps.hasNext()) { ps.next(); } } catch (Throwable t) { // Do nothing }
このコードのおかげで、アプリケーションはClass.forName(<driver class>)コンストラクトを必要としません(動作しますが)、JDBCドライバーはDriverManagerクラスのメソッドに初めてアクセスするときに自動的にロードされます。
SPIメカニズムは、 Java Cryptography Extension(JCE) 、 Java Naming and Directory Service(JNDI) 、 Java API for XML Processing(JAXP) 、 Java Business Integration(JBI) 、Java Sound、Java Image I / Oでも使用されます 。
どのように機能しますか?
全体のポイントは、ロジックをサービス(サービス)とプロバイダー(サービスプロバイダー)に分離することです。 プロバイダーへのリンクは、テキストファイル(UTF-8)
プロバイダのリストを取得するためのメインアプリケーションは、Java SE 6 APIの一部であるjava.util.ServiceLoaderユーティリティを使用できます。このユーティリティは、次の原則に従って動作します。
カスタムコードは特定のサービスの構成ローダーによって要求され、ローダーは必要に応じて構成からプロバイダーを読み込み、キャッシュに保存します。 キャッシュをクリアして設定をリロードすることもできます。
Java SEの以前のバージョンには同様のユーティリティsun.misc.Serviceがあり、同じ原理で動作しますが、
使用例
たとえば、コンピューターで音楽を検索し、名前でソートされた結果を画面に表示するプログラムがあります。
public class MusicFinder { public static List<String> getMusic() { //some code } } public class ReportRenderer { public void generateReport() { final List<String> music = findMusic(); for (String composition : music) { System.out.println(composition); } } public List<String> findMusic() { final List<String> music = MusicFinder.getMusic(); Collections.sort(music); return music; } public static ReportRenderer getInstance() { return new ReportRenderer(); } public static void main(final String[] args) { final ReportRenderer renderer = ReportRenderer.getInstance(); renderer.generateReport(); } }
ある時点で、私たちは社会にとってこのプログラムの重要性を十分に認識し、友人と共有することにしました。 友人はこのサービスを使用して、何かが足りないと判断しました。 別のファイルに出力できますか? ただし、このクールなコードをすべて書き直す必要があります。 必要ありません。SPIメカニズムを使用できます。
たとえば、スーパープログラムのプラグインを作成します。
public class FileReportRenderer extends ReportRenderer { @Override public void generateReport() { final List<String> music = findMusic(); try { final FileWriter writer = new FileWriter("music.txt"); for (String composition : music) { writer.append(composition); writer.append("\n"); } writer.flush(); } catch (IOException e) { e.printStackTrace(); } } }
以下をMETA-INF / services / com.example.ReportRendererに追加します。
com.example.FileReportRenderer
ソースプログラムを拡張可能にしましょう:
public class ReportRenderer { //... public static ReportRenderer getInstance() { final Iterator<ReportRenderer> providers = ServiceLoader.load(ReportRenderer.class).iterator(); if (providers.hasNext()) { return providers.next(); } return new ReportRenderer(); } //... }
起動時に、アプリケーションは、以前と同様に、画面で見つかったすべての音楽を表示します。 ただし、新しく作成した拡張機能jarをクラスパスに配置すると、検索結果を含むmusic.txt ファイルが作成されます。
さあ、 MusicFinderで遊んでみましょう 。 拡張可能にしましょう。 これを行うには、クラスをインターフェイスに変更します。
public interface MusicFinder { List<String> getMusic(); }
メインモジュールに実装を追加します。
public class DummyMusicFinder implements MusicFinder { public List<String> getMusic() { return Collections.singletonList("From DummyMusicFinder..."); } }
ReportRendererの拡張機能サポート:
public class ReportRenderer { //... public List<String> findMusic() { final List<String> music = new ArrayList<String>(); for (final MusicFinder finder : ServiceLoader.load(MusicFinder.class)) { music.addAll(finder.getMusic()); } Collections.sort(music); return music; } //... }
ReportRendererの場合と同様に、次を含むテキストファイルMETA-INF / services / com.example.MusicFinderを追加します。
com.example.DummyMusicFinder
繰り返しますが、最初のプログラムの結果は変更されていません。 今拡張。 ここでは、 MusicFinderの 2つの実装を作成します。
public class ExtendedMusicFinder implements MusicFinder { public List<String> getMusic() { return Collections.singletonList("From ExtendedMusicFinder..."); } } public class MyMusicFinder implements MusicFinder { public List<String> getMusic() { return Collections.singletonList("From MyMusicFinder..."); } }
META-INF / service / com.example.MusicFinder :
com.example.MyMusicFinder com.example.ExtendedMusicFinder
さて、それですべて、拡張機能をサポートするプログラムの準備ができました。 クラスパスに拡張機能が追加されました。
DummyMusicFinderから... ExtendedMusicFinderから... MyMusicFinderから...
サンプルのソースはこちらにあります 。
おわりに
上記の例は完璧とはほど遠いものであり、私は世界で最もクールな音楽検索エンジンの著者であるふりをしていません。 また、このメカニズムの狂信的な使用も求めていません。どこでも適用できるわけではないため、IoCコンテナの使用はより美しいソリューションであると考えていますが、それでもこのアプローチはいくつかの場所で役立つかもしれません。 記事を読んでくれてありがとう。
文学
プラグイン
サービスプロバイダーインターフェイス
サービスプロバイダー
サービスプロバイダーインターフェイス:拡張可能なJavaアプリケーションの作成
サービスローダー