构建环境:环境变量、项目属性、Gradle属性文件、多环境配置

说实话,构建环境这块,是很多团队从 Ant 或 Maven 迁移到 Gradle 时最容易踩坑的地方。我见过不少项目,开发环境跑得好好的,一到测试服务器就崩了。原因很简单——环境配置没处理好。

今天我们就来聊聊,怎么用 Gradle 把这套东西理清楚。

环境变量:最基础的配置方式

环境变量是操作系统级别的配置。说白了,就是你在 shell 里 export 的那些东西。Gradle 可以直接读取它们。

// 在 build.gradle 中读取环境变量
def javaHome = System.getenv('JAVA_HOME')
def dbUrl = System.getenv('DATABASE_URL') ?: 'jdbc:mysql://localhost:3306/dev'

我个人习惯把敏感信息放在环境变量里。比如数据库密码、API Key 这些。为什么呢?因为环境变量不会提交到 Git,相对安全。

小技巧: 在开发机上,可以把环境变量写在 ~/.bashrc 或 ~/.zshrc 里。但注意,改了之后要 source 一下,或者重启终端。我吃过这个亏,改完忘了 source,排查了半天。

项目属性:Gradle 的内置配置

项目属性(Project Properties)是 Gradle 自己管理的配置。你可以通过 -P 参数传递,也可以在 gradle.properties 里定义。

// 命令行传递
// gradle build -Penv=production -Pversion=2.0.0

// 在 build.gradle 中读取
def env = project.findProperty('env') ?: 'dev'
def version = project.property('version')

这里有个坑要注意:project.property() 如果找不到属性会抛异常。所以我一般用 findProperty(),找不到就返回 null,然后给个默认值。

注意: 项目属性是大小写敏感的。我曾经因为把 -PdbUrl 写成了 -Pdburl,结果配置没生效,数据库连不上。排查了半小时才发现是大小写问题。

Gradle 属性文件:gradle.properties 的妙用

gradle.properties 是 Gradle 的默认配置文件。放在项目根目录下,所有任务都能读到。也可以放在 ~/.gradle/ 下,作为全局配置。

// gradle.properties
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
org.gradle.parallel=true
org.gradle.caching=true

// 自定义属性
dbUrl=jdbc:mysql://localhost:3306/dev
dbUser=root
dbPassword=dev123

嗯,这里要注意:gradle.properties 里的值都是 String 类型。如果你要布尔值或数字,得自己转换。

// 读取并转换
def isParallel = project.findProperty('org.gradle.parallel')?.toBoolean() ?: false
def maxMemory = project.findProperty('org.gradle.jvmargs')?.split('\\s+')?.find { it.startsWith('-Xmx') }?.replace('-Xmx', '')?.replace('m', '')?.toInteger() ?: 1024

你想想看,如果每个环境都手动改 gradle.properties,那得多累。所以我们需要多环境配置。

多环境配置:实战中的最佳实践

多环境配置的核心思路是:把配置从代码中分离出来。不同环境加载不同的属性文件。

我常用的做法是这样的:

  1. 创建多个属性文件:gradle-dev.properties、gradle-test.properties、gradle-prod.properties
  2. 通过环境变量或命令行参数指定当前环境
  3. 在 build.gradle 中动态加载对应的属性文件
// 目录结构
// project/
//   ├── gradle-dev.properties
//   ├── gradle-test.properties
//   ├── gradle-prod.properties
//   └── build.gradle

// build.gradle
def env = System.getenv('GRADLE_ENV') ?: 'dev'
def envProps = new Properties()

// 加载环境特定的属性文件
def envFile = file("gradle-${env}.properties")
if (envFile.exists()) {
    envFile.withInputStream { stream ->
        envProps.load(stream)
    }
    println "Loaded configuration for environment: ${env}"
} else {
    println "No specific config found for environment: ${env}, using defaults"
}

// 将环境属性注入到项目
envProps.each { key, value ->
    project.ext.set(key, value)
}

// 使用配置
task printConfig {
    doLast {
        println "Database URL: ${project.ext.findProperty('dbUrl') ?: 'default-url'}"
        println "Log Level: ${project.ext.findProperty('logLevel') ?: 'INFO'}"
    }
}
核心原则: 代码不包含环境信息。环境信息由外部注入。这样你的构建脚本可以在任何环境中复用。

实战案例:一个完整的多环境构建

我记得有一次,帮一个团队重构他们的构建系统。他们原来每个环境都有一份 build.gradle 的副本,改一个功能要同步三个文件,经常漏掉。

我给他们改成了这样:

// gradle-dev.properties
dbUrl=jdbc:mysql://localhost:3306/dev
dbUser=dev_user
dbPassword=dev_pass
logLevel=DEBUG
apiEndpoint=http://localhost:8080

// gradle-test.properties
dbUrl=jdbc:mysql://test-db:3306/test
dbUser=test_user
dbPassword=test_pass
logLevel=INFO
apiEndpoint=https://test-api.example.com

// gradle-prod.properties
dbUrl=jdbc:mysql://prod-db:3306/prod
dbUser=prod_user
dbPassword=${DB_PASSWORD}  // 引用环境变量
logLevel=WARN
apiEndpoint=https://api.example.com

然后在 build.gradle 里,我们还可以根据环境做不同的行为:

// 根据环境配置不同的任务行为
tasks.named('test') {
    if (env == 'prod') {
        enabled = false  // 生产环境不跑单元测试?当然这只是个例子
        println "Tests are disabled in production environment"
    }
}

// 不同环境使用不同的依赖仓库
repositories {
    mavenCentral()
    if (env == 'dev') {
        mavenLocal()  // 开发环境优先使用本地仓库
    }
}

// 环境特定的插件
if (env == 'dev') {
    apply plugin: 'application'
    mainClassName = 'com.example.DevMain'
} else {
    apply plugin: 'war'
}
避坑指南: 我曾经在 gradle-prod.properties 里直接写了数据库密码,结果不小心提交到了 Git。虽然马上删除了,但密码已经暴露了。后来我改用环境变量引用,比如 ${DB_PASSWORD},然后在 CI/CD 系统里设置真正的值。这样既安全又灵活。

SVG 流程图:构建环境配置体系

Gradle 构建环境配置体系 环境变量 System.getenv() 项目属性 -P 参数 / findProperty() 属性文件 gradle.properties 多环境配置策略 gradle-dev.properties / gradle-test.properties / gradle-prod.properties 动态加载:根据环境变量加载对应属性文件 def env = System.getenv('GRADLE_ENV') ?: 'dev' 构建任务中使用配置

总结一下

构建环境配置,说白了就是三件事:

  • 从哪里读:环境变量、项目属性、属性文件
  • 怎么区分:通过环境名(dev/test/prod)加载不同的配置
  • 怎么用:在 build.gradle 中动态读取,应用到任务中

我个人建议,团队里统一一个规范:敏感信息走环境变量,非敏感信息走属性文件,环境切换走命令行参数。这样既安全又灵活。

最后一个小建议: 在 CI/CD 系统中,把环境变量配置在流水线的变量管理里,不要写在代码里。我曾经见过有人在 Jenkinsfile 里硬编码了密码,结果整个团队都能看到。嗯,那场面挺尴尬的。
公众号:蓝海资料掘金营,微信deep3321