You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在IntelliJ中为Ktor项目配置Dev/Sit/Prod多构建变体

为Ktor项目实现多环境构建变体(Dev/Sit/Prod)并加载密钥到配置类

我有一个Ktor项目,想要实现以下配置:创建Dev、Sit、Prod三种构建变体,并且为每个变体把密钥加载到类似Android BuildConfig的类中,这样就能动态选择不同的变体。下面是我在Android Studio中为Android应用实现该功能的可行示例,以及我当前Ktor项目的build.gradle.kts文件:


Android端实现示例代码

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.jetbrainsCompose)
    alias(libs.plugins.compose.compiler)
    alias(libs.plugins.ksp)
    id("org.jetbrains.kotlin.plugin.serialization")
}

val secretsFileDev = rootProject.file("secrets.dev.properties")
val secretsFileSit = rootProject.file("secrets.sit.properties")
val secretsFileProd = rootProject.file("secrets.properties")

val devProperties = Properties().apply {
    load(secretsFileDev.inputStream())
}
val sitProperties = Properties().apply {
    load(secretsFileSit.inputStream())
}
val prodProperties = Properties().apply {
    load(secretsFileProd.inputStream())
}

val code = 1
val name = "1.0.0"

android {
    namespace = "com.myapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.myapp"
        manifestPlaceholders["hostName"] = prodProperties["hostName"].toString()
        minSdk = 24
        targetSdk = 34
        versionCode = code
        versionName = name

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }

        manifestPlaceholders["hostName"] = prodProperties["hostName"].toString()
    }

    flavorDimensions.add("environment")

    productFlavors {
        create("dev") {
            dimension = "environment"
            applicationIdSuffix = ".dev"
            versionNameSuffix = "-dev"
            loadProperties(devProperties, ::buildConfigField)
        }
        create("sit") {
            dimension = "environment"
            applicationIdSuffix = ".sit"
            versionNameSuffix = "-sit"
            loadProperties(sitProperties, ::buildConfigField)
        }
        create("prod") {
            dimension = "environment"
            applicationIdSuffix = ".prod"
            versionNameSuffix = "-prod"
            loadProperties(prodProperties, ::buildConfigField)
        }
    }

    buildTypes {
        getByName("debug") {
            applicationIdSuffix = ".debug"
            isDebuggable = true
        }

        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

    buildFeatures {
        compose = true
        buildConfig = true
        viewBinding = true
    }

    applicationVariants.all { variant ->
        variant.outputs.all { output ->
            if (output is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
                if (variant.buildType.isDebuggable) {
                    output.outputFileName =
                        "myapp${variant.versionName}(${variant.versionCode}).apk"
                } else {
                    output.outputFileName = "myapp-${variant.versionName}.apk"
                }
            }
            true
        }
    }
}

/** 
* 动态将属性加载到buildConfigFields中
*/
fun loadProperties(properties: Properties, buildConfigField: (String, String, String) -> Unit) {
    buildConfigField("String", "baseUrl", properties["baseUrl"].toString())
    buildConfigField("String", "API_KEY", properties["API_KEY"].toString())
    buildConfigField("String", "GCP_API_KEY", properties["GCP_API_KEY"].toString())
}

dependencies {
    /** 依赖项省略 */
}

当前Ktor项目的build.gradle.kts

val exposedVersion: String by project
val h2Version: String by project
val hikariCPVersion: String by project
val koinVersion: String by project
val kotlinVersion: String by project
val ktorVersion: String by project
val logbackVersion: String by project
val mySQLVersion: String by project

plugins {
    kotlin("jvm") version "2.0.10"
    id("io.ktor.plugin") version "2.3.12"
    id("org.jetbrains.kotlin.plugin.serialization") version "2.0.10"
}

group = "com.myktor-backend"
version = "0.0.1"

application {
    mainClass.set("com.myktor-backended.ApplicationKt")

    val isDevelopment: Boolean = project.ext.has("development")
    applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
}

repositories {
    mavenCentral()
}

dependencies {
    /** 依赖项省略 */
}

Ktor项目的实现方案

1. 创建环境属性文件

在项目根目录创建三个属性文件,分别对应不同环境:

  • secrets.dev.properties(开发环境)
  • secrets.sit.properties(测试环境)
  • secrets.prod.properties(生产环境)

每个文件存放对应环境的敏感配置,示例内容:

baseUrl=http://dev.example.com/api
API_KEY=dev_abc123xyz
GCP_API_KEY=dev_gcp_789qwe

2. 修改build.gradle.kts配置

更新Gradle脚本,实现环境变体识别、属性加载和配置类生成:

val exposedVersion: String by project
val h2Version: String by project
val hikariCPVersion: String by project
val koinVersion: String by project
val kotlinVersion: String by project
val ktorVersion: String by project
val logbackVersion: String by project
val mySQLVersion: String by project

plugins {
    kotlin("jvm") version "2.0.10"
    id("io.ktor.plugin") version "2.3.12"
    id("org.jetbrains.kotlin.plugin.serialization") version "2.0.10"
    // 引入kapt用于代码生成
    kotlin("kapt") version "2.0.10"
}

group = "com.myktor-backend"
version = "0.0.1"

// 定义支持的环境列表
val environments = listOf("dev", "sit", "prod")

// 预加载各环境的属性文件
val envPropsMap = environments.associateWith { env ->
    val propFile = rootProject.file("secrets.$env.properties")
    java.util.Properties().apply {
        if (propFile.exists()) load(propFile.inputStream())
    }
}

// 为每个环境创建配置类生成任务
environments.forEach { env ->
    val taskName = "generate${env.replaceFirstChar(Char::uppercase)}BuildConfig"
    tasks.register<JavaExec>(taskName) {
        group = "build"
        description = "生成${env.uppercase()}环境的BuildConfig类"

        classpath = sourceSets["main"].runtimeClasspath
        mainClass.set("com.myktorbackend.BuildConfigGenerator")

        // 传递环境参数给生成器
        args = listOf(
            env,
            envPropsMap[env]?.getProperty("baseUrl") ?: "",
            envPropsMap[env]?.getProperty("API_KEY") ?: "",
            envPropsMap[env]?.getProperty("GCP_API_KEY") ?: ""
        )
    }

    // 让编译任务依赖对应环境的配置生成任务
    tasks.named("compileKotlin") {
        dependsOn(taskName)
    }
}

application {
    mainClass.set("com.myktor-backended.ApplicationKt")

    // 根据传入的环境参数设置系统属性
    val targetEnv = project.findProperty("env") ?: "prod"
    applicationDefaultJvmArgs = listOf(
        "-Dio.ktor.development=${targetEnv == "dev"}",
        "-Dapp.env=$targetEnv"
    )
}

repositories {
    mavenCentral()
}

dependencies {
    // 保留原有依赖,示例如下
    implementation("io.ktor:ktor-server-core-jvm:$ktorVersion")
    implementation("io.ktor:ktor-server-content-negotiation-jvm:$ktorVersion")
    implementation("io.ktor:ktor-serialization-kotlinx-json-jvm:$ktorVersion")
    implementation("io.ktor:ktor-server-netty-jvm:$ktorVersion")
    implementation("ch.qos.logback:logback-classic:$logbackVersion")
    testImplementation("io.ktor:ktor-server-tests-jvm:$ktorVersion")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    // 其他依赖...
}

tasks.test {
    useJUnitPlatform()
}

3. 编写BuildConfig生成器

在src/main/kotlin/com/myktorbackend目录下创建BuildConfigGenerator.kt,用于生成对应环境的配置类:

import java.io.File

fun main(args: Array<String>) {
    val env = args[0]
    val baseUrl = args[1]
    val apiKey = args[2]
    val gcpApiKey = args[3]

    val packageName = "com.myktorbackend"
    val outputPath = "src/main/kotlin/${packageName.replace(".", "/")}"
    val className = "BuildConfig${env.replaceFirstChar(Char::uppercase)}"
    val outputFile = File("$outputPath/$className.kt")

    // 创建输出目录(如果不存在)
    outputFile.parentFile.mkdirs()

    // 生成配置类代码
    val classContent = """
package $packageName

object $className {
    const val BASE_URL = "$baseUrl"
    const val API_KEY = "$apiKey"
    const val GCP_API_KEY = "$gcpApiKey"
    const val ENVIRONMENT = "$env"
}
    """.trimIndent()

    // 写入文件
    outputFile.writeText(classContent)
}

4. 实现动态配置加载

创建统一的配置入口类AppConfig.kt,根据当前环境自动加载对应配置:

package com.myktorbackend

object AppConfig {
    val baseUrl: String
    val apiKey: String
    val gcpApiKey: String
    val environment: String

    init {
        val currentEnv = System.getProperty("app.env") ?: "prod"
        when (currentEnv) {
            "dev" -> {
                baseUrl = BuildConfigDev.BASE_URL
                apiKey = BuildConfigDev.API_KEY
                gcpApiKey = BuildConfigDev.GCP_API_KEY
                environment = BuildConfigDev.ENVIRONMENT
            }
            "sit" -> {
                baseUrl = BuildConfigSit.BASE_URL
                apiKey = BuildConfigSit.API_KEY
                gcpApiKey = BuildConfigSit.GCP_API_KEY
                environment = BuildConfigSit.ENVIRONMENT
            }
            else -> {
                baseUrl = BuildConfigProd.BASE_URL
                apiKey = BuildConfigProd.API_KEY
                gcpApiKey = BuildConfigProd.GCP_API_KEY
                environment = BuildConfigProd.ENVIRONMENT
            }
        }
    }
}

5. 在Ktor应用中使用配置

在主函数或路由中直接调用AppConfig获取配置:

fun main() {
    embeddedServer(Netty, port = 8080) {
        install(ContentNegotiation) {
            json()
        }
        routing {
            get("/status") {
                call.respondText("运行环境: ${AppConfig.environment}\n接口地址: ${AppConfig.baseUrl}")
            }
        }
    }.start(wait = true)
}

6. 运行不同环境的变体

IntelliJ运行配置

修改应用运行配置的VM选项,添加环境参数:

  • Dev环境:-Dapp.env=dev
  • Sit环境:-Dapp.env=sit
  • Prod环境:默认或添加-Dapp.env=prod

Gradle命令行运行

# 运行开发环境
./gradlew run -Penv=dev

# 运行测试环境
./gradlew run -Penv=sit

# 运行生产环境
./gradlew run -Penv=prod

注意事项

  • 务必将secrets.*.properties添加到.gitignore,防止敏感信息泄露到版本库
  • 如果属性文件不存在,生成器会使用空字符串作为默认值,可根据需求修改默认逻辑
  • 配置类会在编译前自动生成,无需手动维护

内容的提问来源于stack exchange,提问作者Klem Lloyd Mwenya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 14:50:54