Service Loaderについて
プラグインの仕組みを作成するときに使うJavaの機能。
とあるクラスを継承したクラス群を実行時に呼び出すことができる。
例
例えば以下のようなインターフェイスがあったとする。
code:kotlin
interface Plugin {
fun install()
}
また、以下のように、このインターフェイスを継承したクラスが存在したとする。
code:kotlin
class HogePluginImpl : Plugin {
fun install = println"("Hoge")
}
class FooPluginImpl : Plugin {
fun install = println"("Foo")
}
class BarPluginImpl : Plugin {
fun install = println"("Bar")
}
これらのプラグインを利用する方法を考える。
例えば以下のような方法が考えられる。
code:kotlin
val plugins: List<Plugin> = listOf(HogePluginImpl(), FooPluginImpl(), BarPluginImpl())
plugins.forEach { it.install() }
これでも問題ないのだが、新しくプラグインを足すたびに付け足していく必要があり、少し面倒な部分がある。
そこでService Loaderを用いる。
具体的には、META-INFに記述してやることで指定することが可能になる。
code:META-INF/services/Plugin
あとは以下のように指定してやればいい
code:kotlin
ServiceLoader.load(Plugin::class.java).forEach { it.install() }
これで呼び出し可能になる。
Pluginを追加するときはMETA-INFに付け足してやる必要がある。
ちなみにgradleにMETA-INFを書かせてやるには以下の通りにする
code:build.gradle.kts
val myServiceImplementation by tasks.registering {
doLast {
val file = File("$buildDir/resources/main/META-INF/services/Plugin")
file.parentFile.mkdirs()
file.writeText(
"""
HogePluginImpl
FooPluginImpl
BarPluginImpl
"""
)
}
}
tasks.jar {
from(myServiceImplementation)
}
AutoService
Service Loaderは以上のように利用可能なのだが、大体のエンジニアはMETA-INFを書き忘れる。
また、特定のライブラリにプラグインを拡張するときにMETA-INFを書く方法は面倒である。
そこでGoogleが作成したAutoServiceを利用する。
1. AutoServiceを依存関係に加える
code:build.gradle.kts
plugins {
kotlin("kapt") version "1.8.10"
}
dependencies {
implementation("com.google.auto.service:auto-service:1.0.1")
kapt("com.google.auto.service:auto-service:1.0.1")
}
Javaで書かれているためkaptもいる。
2. 実装する側に@AutoServiceを加える
code:kotlin
@AutoService(Plugin::class)
class HogePluginImpl : Plugin {
fun install = println"("Hoge")
}
@AutoService(Plugin::class)
class FooPluginImpl : Plugin {
fun install = println"("Foo")
}
@AutoService(Plugin::class)
class BarPluginImpl : Plugin {
fun install = println"("Bar")
}
これで自動でMETA-INFに加えてくれる。
ただしkspには対応していないため注意