LoginSignup
3
1

More than 1 year has passed since last update.

Kotlin + SpringBootでのGradleマルチプロジェクト

Posted at

目的

共通部品に依存するAPIやバッチを一つのプロジェクトにまとめるマルチプロジェクト化をしたい人のためにKotlin + Gradleでの方法を書きたいと思います
(せっかくなのでJarファイル作成の設定も記載しておきます)

環境

  • MacBookPro M1(zsh)
  • IntelliJ IDEA Community版
  • Gradle 7.0.2

成果物
GitHub: https://github.com/seki-shinnosuke/kotlin-gradle-multi-project

内容

今回作るマルチプロジェクト構成はこんな感じです

kotlin-gradle-multi-project
├── common
│   └── // 共通処理(UtilやComponent)を配置するためのサブプロジェクト
├── api
│   └── // SpringBootで作成したRestAPI
└── batch
    └── // SpringBootで作成したCommandLine

まずは「settings.gradle.kts」
kotlin-gradle-multi-projectをrootプロジェクトとし配下の各プロジェクトはサブプロジェクトとしてincludeします

settings.gradle.kts
rootProject.name = "kotlin-gradle-multi-project"
include("common", "api", "batch")

そして今回の肝となる「build.gradle.kts」

build.gradle.kts
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.springframework.boot.gradle.tasks.bundling.BootJar

object Versions {
    const val jdk = "17"
}

plugins {
    application
    idea
    kotlin("jvm") version "1.6.10"
    kotlin("plugin.spring") version "1.6.10"
    id("org.springframework.boot") version "2.6.6"
    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

allprojects {
    group = "com.example"

    repositories {
        mavenCentral()
        maven("https://plugins.gradle.org/m2/")
    }

    tasks {
        // JSR 305チェックを明示的に有効にする
        withType<KotlinCompile>().configureEach {
            kotlinOptions.freeCompilerArgs = listOf("-Xjsr305=strict", "-java-parameters")
            kotlinOptions.jvmTarget = Versions.jdk
        }
        withType<Test>().configureEach {
            useJUnitPlatform()
            val javaToolchains = project.extensions.getByType<JavaToolchainService>()
            javaLauncher.set(
                javaToolchains.launcherFor {
                    languageVersion.set(JavaLanguageVersion.of(Versions.jdk.toInt()))
                }
            )
        }

        // 共通部品を入れるプロジェクト以外はBootJarを生成可能にする
        // Jarファイル名: kotlin-gradle-multi-project-[api|batch].jar
        // 実行クラス名: com.example.[api|batch].[Api|Batch]Application
        withType<BootJar>().configureEach {
            if (this.project == rootProject || this.project.name == "common") {
                enabled = false
            } else {
                mainClass.set("${rootProject.group}.${this.project.name}.${this.project.name.capitalize()}Application")
            }
        }
        withType<Jar>().configureEach {
            if (this.project == rootProject) {
                enabled = false
            } else {
                enabled = true
                archiveBaseName.set("${rootProject.name}-${this.project.name}")
            }
        }
    }
}

subprojects {
    apply {
        plugin("kotlin")
        plugin("kotlin-spring")
        plugin("org.springframework.boot")
        plugin("io.spring.dependency-management")
    }

    dependencies {
        implementation("org.jetbrains.kotlin:kotlin-stdlib")
        implementation("org.jetbrains.kotlin:kotlin-reflect")
        implementation("org.jetbrains.kotlin:kotlin-gradle-plugin")
        implementation("org.springframework.boot:spring-boot-gradle-plugin:2.6.6")

        testImplementation("org.springframework.boot:spring-boot-starter-test") {
            exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
        }
    }

    configure<DependencyManagementExtension> {
        imports {
            mavenBom(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)
        }
    }
}

project(":common") {
    dependencies {
        implementation("org.springframework:spring-web")
    }

    tasks.bootJar {
        enabled = false
    }
    tasks.bootRun {
        enabled = false
    }
    tasks.jar {
        enabled = true
    }

    // commonに作成したテストクラスを共通部品として他のプロジェクトでも取り込みたい場合
    val testCompile by configurations.creating
    configurations.create("testArtifacts") {
        extendsFrom(testCompile)
    }
    tasks.register("testJar", Jar::class.java) {
        archiveClassifier.set("test")
        from(sourceSets["test"].output)
    }
    artifacts {
        add("testArtifacts", tasks.named<Jar>("testJar"))
    }
}

project(":api") {
    dependencies {
        // 共通プロジェクトへの依存を追加
        implementation(project(":common"))
        implementation("org.springframework.boot:spring-boot-starter-web")
    }

    springBoot {
        buildInfo()
    }
}

project(":batch") {
    dependencies {
        // 共通プロジェクトへの依存を追加
        implementation(project(":common"))
        implementation("org.springframework.boot:spring-boot-starter-web")
    }

    springBoot {
        buildInfo()
    }
}

ポイントは以下

allprojects {
  // ルートを含めた全プロジェクトに対しての設定
}
subprojects {
  // 全サブプロジェクトに対しての設定
}
project(":common") {
  // 共通プロジェクト
}
project(":xxxxxx") {
    dependencies {
        // 共通プロジェクトへの依存を追加
        implementation(project(":common"))
    }
}

マルチプロジェクトで共通化した際にプロダクトコードは依存関係を追加できるがテストコードでも共通のテストクラスを利用したいということも多々あると思います(DBの接続設定やテスト用のアノテーションクラスなど)
そういった時はテストクラスをJar生成時に含める形で共通プロジェクトに以下のような設定を加えると読み込んでくれます

    val testCompile by configurations.creating
    configurations.create("testArtifacts") {
        extendsFrom(testCompile)
    }
    tasks.register("testJar", Jar::class.java) {
        archiveClassifier.set("test")
        from(sourceSets["test"].output)
    }
    artifacts {
        add("testArtifacts", tasks.named<Jar>("testJar"))
    }

まとめ

Java + Gradle(Groovy)でのマルチプロジェクト化は結構記事がありますがKotlinでの記事があまり見られなかったので書いてみました
プロジェクト内で共通化するならMavenリポジトリを立ててそこにパッケージとしてPublishした方がいいのではと思いますが利用規模やGit管理時に1リポジトリで完結するプロジェクトにしたい場合は参考にしていただければと思います

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1