#navi_header|Java| Javaではライブラリはjarで配布される場合が多い。その中でもインターフェイスと実装をjar単位で分離して、サードパーティによる実装の拡張が容易になるような仕組みが"SPI"(Service Provider Interface)と呼ばれており、JDK5までは "sun.misc.Service" という非公開クラスとメソッドだったのが、JDK6以降は "java.util.ServiceLoader" クラスとして公開され、利用できるようになった。 SPIの良い例がJDBCの処理で、インターフェイスは定義しておき、それを実装するライブラリを、DBMSごとに用意する。プログラマはSPI経由でJDBCインターフェイスを取得すれば、実際のどのDBMSの実装クラスが使われたのかを意識せずに、すべて同じインターフェイス(クラス名・メソッド名)でDB接続を操作できる。なおアプリを配布する際は、使用するDBMSのJDBC用jarファイルも一緒に配布する。 簡単なユースケースを以下のAntプロジェクトで試してみた: - https://github.com/msakamoto-sf/JavaServiceProviderInterfaceDemo 仕組みとしては、実装側のjarファイルの "META-INF/services/" の下に、実装したいインターフェイスのクラス名でファイルを作成し、その中に実装ファイルのクラスを一行一個で記載する。 今回のサンプルでは、FooProviderが "spidemo.cloud.spi.Cloud" と "spidemo.search.spi.Search" の2つのインターフェイスの実装を提供するため、"META-INF/services/"に以下の2ファイルを配置している。 FooProvider/src/META-INF/services/spidemo.cloud.spi.Cloud: #pre||> spidemo.cloud.FooCloud ||< FooProvider/src/META-INF/services/spidemo.search.spi.Search: #pre||> spidemo.search.FooSearch ||< また、BarProviderでは "spidemo.search.spi.Search" に対して2つの実装クラスを提供するため、 以下のように2行にして2つのクラス名を記述している。 BarProvider/src/META-INF/services/spidemo.search.spi.Search: #pre||> spidemo.search.BarSearch spidemo.search.BarSearch2 ||< mainクラスはDemoAppに配置しているが、DemoAppそれ自身でもCloudとSearchの両インターフェイスを実装して、自分自身の実装をロードできるか確認する。 DemoApp/src/META-INF/services/spidemo.cloud.spi.Cloud: #pre||> spidemo.cloud.MyCloud ||< DemoApp/src/META-INF/services/spidemo.search.spi.Search: #pre||> spidemo.search.MySearch ||< 実行時の出力(解説付き): #pre||> # CloudService インターフェイスの実装としてFooProvider, BazProviderの2つを用意した。 # また、DemoApp自身が "My Cloud Provider" という名前の実装を含んでいる。 # getProviderName()とgetServiceNames()をprintしている。 Provider Name: My Cloud Provider My Container My Tomcat Provider Name: Foo Cloud Provider Foo VPC Foo VPN Foo Shared Server Foo Dedicated Server Provider Name: Baz Cloud Provider Baz Xen Computing Baz Security Gateway Baz Shared Storage # SearchService インターフェイスの実装としてFooProvider, BarProviderの2つを用意した。 # (なお、FooProviderはCloudとSearchの2つとも実装している) # また、DemoApp自身が "My Cloud Provider" という名前の実装を含んでいる。 Provider Name: My Search Provider My Search 1 My Search 2Provider Name: Foo Search Provider Foo Search 1 Foo Search 2 Foo Search 3 ## BarProviderでは、Search SPIに対して2つの実装を定義している。 Provider Name: Bar Search Provider Bar Search 1 Bar Search 2 Bar Search 3 Provider Name: Bar Search2 Provider Bar2 Search 1 Bar2 Search 2 Bar2 Search 3 ||< 参考: - Introduction to the Service Provider Interfaces (The Java™ Tutorials > Sound) -- https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html - Creating Extensible Applications (The Java™ Tutorials > The Extension Mechanism > Creating and Using Extensions) -- https://docs.oracle.com/javase/tutorial/ext/basics/spi.html - ServiceLoader (Java Platform SE 7 ) -- https://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html 日本語資料: - ServiceLoader (Java Platform SE 7 ) -- https://docs.oracle.com/javase/jp/7/api/java/util/ServiceLoader.html - Jarファイルメモ(Hishidama's java-archive Memo) -- http://www.ne.jp/asahi/hishidama/home/tech/java/jar.html#h_Service_Provider #navi_footer|Java|