Spring Boot × Gradleで自作ライブラリを作る
Spring Bootで自作ライブラリを1から作ったことがなかったので試してみました。
作成するライブラリ
文字列を引数で与えるとPrefixをつけて返してくれるMyTextDecorator
を作成します。
さらにadditionalを定義することで追加で文字列を付けてくれます。
github star 5,000超え待ったなしのスーパーライブラリですよこいつはぁ。
利用方法
# application.yml mylibrary: text: prefix: my-prefix additional: enabled: true prefix: additional
@Service class DemoService( private val myTextDecorator: MyTextDecorator ) : DemoUseCase { override suspend fun getSampleModel(): DemoUseCase.Output { val text = myTextDecorator.decorate("demo") println(text) // -> my-prefixadditionaldemo return DemoUseCase.Output(text) } }
やること
- mylibrary.text.prefix を読み込んでBeanを生成する
- mylibrary.text.additional.prefix を読み込んで条件に応じてBeanを生成する
- MyTextDecorator.ktをBeanに登録する
- spring.factoriesを用意する
- jarに固める
- 利用者側に取り込む
ソフトウェアバージョン
software | version |
---|---|
OS | MacOS Catalina |
Spring Boot | 2.2.2 |
Java | Corretto-11.0.4.11.1 |
Kotlin | 1.3.61 |
Gradle | 6.0.1 |
my-library 構成
my-library ├── build.gradle.kts ├── settings.gradle.kts └── src └── main ├── kotlin │ └── com │ └── example │ └── mylibrary │ ├── MyLibraryApplication.kt │ └── autoconfigure │ ├── TextAdditionalPrefixConfiguration.kt │ ├── TextDecoratorConfiguration.kt │ └── TextDefaultPrefixConfiguration.kt └── resources ├── META-INF │ └── spring.factories └── application.properties
TextDefaultPrefixConfiguration.kt
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(TextDefaultPrefixProperty::class) class TextDefaultPrefixConfiguration( private val textDefaultPrefixProperty: TextDefaultPrefixProperty ) { @Bean fun defaultPrefix() = DefaultPrefix(textDefaultPrefixProperty.prefix) } @ConfigurationProperties("mylibrary.text") @ConstructorBinding class TextDefaultPrefixProperty( val prefix: String ) data class DefaultPrefix(val value: String)
TextAdditionalPrefixConfiguration.kt
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(TextAdditionalPrefixProperty::class) class TextAdditionalPrefixConfiguration( private val textAdditionalPrefixProperty: TextAdditionalPrefixProperty ) { // mylibrary.text.additional.enabled=true の場合にBeanにセットする。 // enabledが無い場合もBeanに入る @Bean @ConditionalOnProperty(prefix = "mylibrary.text.additional", name = ["enabled"], matchIfMissing = true) fun additionalPrefix() = AdditionalPrefix(textAdditionalPrefixProperty.prefix) } @ConfigurationProperties("mylibrary.text.additional") @ConstructorBinding class TextAdditionalPrefixProperty( // mylibrary.text.additional.prefixはoptionalなので default valueをセットしておく // application.ymlに存在しない場合はBlank val prefix: String = "" ) data class AdditionalPrefix( val value: String = "" )
TextDecoratorConfiguration.kt
@Configuration(proxyBeanMethods = false) class TextDecoratorConfiguration( private val defaultPrefix: DefaultPrefix, // additionalPrefixはBeanに登録していない場合もあるのでnullableにする private val additionalPrefix: AdditionalPrefix? ) { @Bean fun decorator() = MyTextDecorator(defaultPrefix.value + (additionalPrefix?.value ?: "")) } class MyTextDecorator(private val value: String) { fun decorate(str: String) = value + str }
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.mylibrary.autoconfigure.TextDefaultPrefixConfiguration,\ com.example.mylibrary.autoconfigure.TextAdditionalPrefixConfiguration,\ com.example.mylibrary.autoconfigure.TextDecoratorConfiguration
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.2.2.RELEASE" id("io.spring.dependency-management") version "1.0.8.RELEASE" kotlin("jvm") version "1.3.61" kotlin("plugin.spring") version "1.3.61" // IDEにmeta-dataを理解してもらう。tutorialsの下の方に記載 // https://spring.io/guides/tutorials/spring-boot-kotlin/ kotlin("kapt") version "1.3.61" // maven pluginを使いたい場合 java maven } group = "com.example" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_11 repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } kapt("org.springframework.boot:spring-boot-configuration-processor") } tasks.withType<Test> { useJUnitPlatform() } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "1.8" } } // Jarを作れるようにする tasks.withType<Jar> { enabled = true } // maven pluginを利用する場合 // https://docs.gradle.org/current/userguide/maven_plugin.html tasks.named<Upload>("uploadArchives") { repositories.withGroovyBuilder { "mavenDeployer" { "repository"("url" to "file://path/to/foo/bar/") "pom" { setProperty("version", version) } } } }
Library User側のbuild.gradle.kts
jarをlibsに入れて参照するだけにする
dependencies { implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) }
Call
@Service class DemoService( private val myTextDecorator: MyTextDecorator ) : DemoUseCase { override suspend fun getSampleModel(): DemoUseCase.Output { val text = myTextDecorator.decorate("demo") return DemoUseCase.Output(text) } }
まとめ
ConditionalOnとかAutoConfigureOrderとかの動きを知りたくて作ってみたのですがAutoConfigureOrderを利用しなかったな😑
Spring Boot 2.2から @ConstructorBinding が利用できるようになったのでProperty classを表現しやすくなりました。
参考
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-auto-configuration
https://docs.gradle.org/current/userguide/maven_plugin.html
https://qiita.com/daisuzz/items/158e34e86096beae84af