这题我熟。当年第一次把生产数据库密码写死在application.yml里提交到仓库,第二天被导师单独叫去“喝茶”,从那以后我就养成了一个习惯:凡是密钥、密码、令牌这类关键信息,一律不允许出现在代码文件里。开发环境要跑起来,最轻量、最省事的手段就是在 IntelliJ IDEA 里配置环境变量。展开说,就是利用 IDE 的“运行配置(Run/Debug Configurations)”,给某个启动项单独注入一组KEY=VALUE,代码里通过环境变量去读取。这样既能隐藏关键信息,又能做到不同环境互不干扰,今天这篇就把我在实际项目里攒下的配置经验、踩坑记录和生活窍门一并交出来,不同基础的开发都能直接在工位上复现。
1. 先想清楚:环境变量到底在帮你防什么
很多人一听到“环境变量”,第一反应是JAVA_HOME、PATH这类系统级配置,觉得又老又土。但这里的“环境变量”,指的是 IDE 层面、运行配置层面的局部变量注入机制。理解它防的是什么场景,比单纯会填几个键值对要重要得多。我把平时最容易出事的场景列一下,你看看有没有命中。
1.1 硬编码敏感信息的真实代价
最典型的情况,是把数据库连接串、支付回调密钥、第三方短信 AppKey 直接写在项目里。开发阶段图省事,项目能跑就行,但隐患是逐步累积的:
- 仓库泄露:只要代码推送到远端,配置就跟着走了。哪怕项目是私有仓库,合作的同事、外包、离职人员的本地副本,每多一个人接触就多一份泄露风险。
- 多环境切换靠改代码:开发库、测试库、生产库地址不一样,要么临时注释,要么每次启动前手改
application.yml,改错一处就是连错库、甚至把测试环境数据写坏的级别。 - 团队协作互相踩脚:每个人本地配置不同,
application.yml频繁冲突,明明只改了一个密码但 diff 里全是文件变更记录,排查起来相当闹心。
提示:代码仓库里出现
password: "123456"这类字眼,等于把你家钥匙挂在门口,出事只是时间问题。
环境变量解决的就是“代码与配置分离”。配置项跟随运行环境而不是跟随代码,代码仓库里剩下的只有${DB_PASSWORD}这类占位符,真正的值只存在于你本机的 IDEA 运行配置里,这个思路在所有语言、所有框架下都是成立的。
1.2 IDEA 运行配置是个“隔离沙箱”
运行配置里面填的环境变量,有一个特别重要的特性:它只对这一次启动生效,不会污染整个操作系统,也不会影响其他项目。
打个比方,你的电脑是一栋楼,系统环境变量是贴在每层楼公告栏的通告,所有住户都得看。而 IDEA 的运行配置,是单独贴在某个房门口的便签,只有住这间房的人进门时才需要理会。这种隔离性带来的直接好处是:
- 项目 A 里定义
DB_PASSWORD=dev,项目 B 里定义DB_PASSWORD=test,两个项目同时开、来回切换,互不干扰。 - 不需要管理员权限去改系统设置,也不需要改完重启电脑,IDEA 内点一下“运行”就是一次全新的注入。
- 调试时想临时改一个参数,改完重新运行即可,秒级生效,而不用去系统设置里翻半天。
这就是环境变量配置在 IDE 里最大的价值:安全、隔离、快速。接下来我给出四种实操方式,按场景选即可,日常开发最常用的就是第一种。
2. IDEA 里配置环境变量的四种实操方式
2.1 直接在运行配置里写(最推荐)
这是我每天都在用的方式,适用于 IntelliJ IDEA 全系列版本,包括社区版。以Spring Boot的启动类为例,完整步骤记录如下:
- 在 IDEA 右上角的启动配置下拉框里,点开下拉列表,选择
Edit Configurations...。 - 左侧找到你要改的启动项。如果是 Spring Boot 项目,通常是一个带树叶图标的
Application类型配置,名称一般和启动类同名。 - 在右侧
Configuration选项卡里找到Environment variables输入框。老版本如果没有显示这一栏,就点击下方Modify options下拉列表,勾选Environment variables,输入框就会出现了。 - 在输入框里按
KEY1=VALUE1;KEY2=VALUE2的格式填写,注意分隔符是英文分号;,不是逗号。 - 也可以点击输入框右侧的文件夹图标,打开表格编辑器(就是一个小弹窗),一行一条
KEY=VALUE,鼠标点点就能新增和删除,不怕格式写错。 - 点击右下角
Apply再点OK。
比如本地开发库密码是dev123456,就填DB_PASSWORD=dev123456。代码那边通过System.getenv("DB_PASSWORD")读取,或者用@Value("${DB_PASSWORD}")注入。
这里特别提醒一句:不要在该输入框里给值加双引号。有些老手是从 shell 脚本或.properties文件习惯里带过来的,总觉得值要"dev123456"包一层,在 IDE 里这会完完整整把引号当成值的一部分,密码变成"dev123456"带引号的版本,连接数据库时直接报 Access denied,排查时会怀疑人生。
表格编辑器里编辑的键值对,最终写进配置文件时也是分号分隔格式。不过表格编辑器能避免一个坑:如果某个值里面本身就带了分号(比如某些复杂令牌里会有;),手写的分隔格式会直接把它切断,而表格编辑器不会,它清楚地知道这是一整个值。
2.2 用 .env 文件加 EnvFile 插件
运行配置里的输入框适合变量数量少的场景。一旦你这个项目需要十几个变量,每次在弹窗里逐条维护就有点憨了,而且多个运行配置之间没法复用。这个时候我建议用.env文件配合EnvFile插件。
.env文件是很多框架和工具的通用格式,形如:
DB_HOST=localhost DB_PORT=3306 DB_USER=root DB_PASSWORD=dev123456 REDIS_HOST=localhost REDIS_PORT=6379操作步骤:
- 在 IDEA 插件市场安装
EnvFile插件(Settings -> Plugins -> Marketplace,搜索 EnvFile)。 - 项目根目录新建
.env文件,按上面的格式填写内容。 - 打开
Edit Configurations...,选中你的启动项。 - 在配置界面里找到
EnvFile选项卡(安装插件后会出现在 Configuration 面板的 tab 区域),勾选Enable EnvFile。 - 点击中间的
+号,弹窗里选择你刚创建的.env文件。 - 应用并保存。启动时插件会自动把文件里的键值对注入到该运行配置中。
这个方案的好处是:第一,同一份.env文件可以被多个运行配置引用,改一处全生效;第二,前端、后端、部署脚本都可以复用同一种.env格式,团队沟通成本低;第三,以后要引入 Docker 时,docker-compose.yml里也能直接用env_file加载同一份文件,整个链路非常顺滑。
注意:
.env文件不禁入仓库的人,建议在.gitignore里把.env一起忽略掉。稍后专门讲。
2.3 写在 Maven / npm 启动器里
有些项目不走 IDEA 的Application启动类,而是用mvn spring-boot:run或者npm run dev在终端里启动。这种情况下 IDEA 运行配置里的环境变量是传不进去的,因为 Maven 或 Node 进程是独立 fork 出来的子进程,IDE 层面的注入没有覆盖到它。
以 Maven 启动 Spring Boot 为例,需要在pom.xml的spring-boot-maven-plugin配置里显式声明:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <environmentVariables> <DB_PASSWORD>dev123456</DB_PASSWORD> </environmentVariables> </configuration> </plugin>npm 脚本那边,Windows 下建议用cross-env保证跨平台一致:
{ "scripts": { "dev": "cross-env VITE_API_KEY=devKey vite" } }这个方法适合“启动命令本身就是项目约定”的团队。但说句公道话:它把敏感信息重新放回了配置文件,又回到老路上去了。如果你有得选,还是优先用运行配置或者.env文件;只有团队明确规定必须统一走命令行启动时,我才会用这招。
2.4 IDEA 全局设置(不推荐)
你可以在 IDEA 的Settings -> Tools -> Terminal -> Environment variables里写一批全局环境变量,也可以在操作系统层面设置。我把它列为“不推荐”的方式,原因很直接:它和你直接在电脑上export一个变量没本质区别,隔离性几乎为零。
全局变量的问题在于:多项目同名变量互相覆盖、换台机器又得重新配置一遍、还会污染你日常操作的 shell 环境。我用过一次就放弃了。当时图省事,想着DB_PASSWORD全局设置一次,所有项目都能用。结果项目 A 用的测试库密码,项目 B 用的本地开发密码,两个项目同名变量只能有一个值,来回倒腾,比硬编码还难受。
真到了需要全局变量的场景,绝大多数时候是系统层面的PATH或者JAVA_HOME,这跟“隐藏关键信息”的目标没有关系。敏感信息还是要留在项目级、运行配置级的地方。
3. 两个真实项目的完整配置过程
3.1 Spring Boot 接入数据库密钥
用一个典型的 Spring Boot 项目来说。假设你有一个application.yml,原本是这样:
spring: datasource: url: jdbc:mysql://localhost:3306/mydb username: root password: dev123456改造之后:
spring: datasource: url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/mydb username: ${DB_USER:root} password: ${DB_PASSWORD:}这里用到了 Spring 的占位符语法:${DB_PASSWORD}表示从环境变量或系统属性里取值,冒号后面是默认值。${DB_PASSWORD:}的意思是本地没配这个变量时用空字符串,避免启动时直接报缺属性。
然后在 IDEA 运行配置里,按前面 2.1 节的方式填好这些变量。保存后再运行,项目启动时会去读DB_HOST、DB_PORT、DB_USER、DB_PASSWORD这四个键。
读取代码时也可以不用@Value,用@ConfigurationProperties读取一整个配置块,或者直接用System.getenv("DB_PASSWORD")。实际项目里我更喜欢占位符方案,因为它和配置文件的组织方式天然融合,而且默认值机制很友好——本地某些非敏感配置(比如端口、主机名)可以直接写进 yml 里,不需要每次都配环境变量。
提示:配置完之后,记得先
Stop再重新Run。IDEA 不会自动热加载环境变量的变化,有时候你改了运行配置,旧的进程还挂着,新变量根本没进去。
这里还要防一手:如果你用的是 Maven 插件方式启动,spring-boot:run默认会 fork 一个子进程来跑,运行配置里的环境变量传不过去,刚才 pom 里没配置environmentVariables的话,就会出现“明明 ID 里填了,代码读出来却是 null”的怪事。要避免就把启动方式换成 IDEA 默认的Application类型,它会直接在 IDE 进程里注入变量,不需要走 Maven 子进程。
3.2 前端项目读取接口密钥
前端项目也是同一个套路,只是读取习惯不同。比如 Vue 项目,在项目根目录建.env.development:
VITE_APP_NAME=本地开发 VITE_API_BASE_URL=https://dev-api.example.local VITE_API_KEY=devKey123代码里用import.meta.env.VITE_API_KEY读取。这里有个命名规则必须遵守:变量名必须以VITE_开头,Vite 才会把它暴露给前端代码,这是 Vite 的硬性前缀要求。React 的 CRA 则是REACT_APP_前缀,英文变量名拼错照样读不到。
这类文件天然被前端主流脚手架视作敏感文件,gitignore里一般已经默认忽略。如果你们团队用的是自定义构建脚本,就要特别注意有没有把.env提交进去。
前端项目的环境变量还有一个特殊之处:构建时就会被替换,而不是运行时注入。所以配置文件里的变量在打包后就变成了明文写在产物里的字符串。这不是说前端能完全藏住密钥,想藏住就别在前端代码里放密钥。环境变量对前端的意义更多在于多环境切换和避免把开发环境地址提交到仓库,这个预期要摆正。
4. 踩坑实录与排查速查表
4.1 为什么变量“看起来配了”却没生效
我接到过最多的咨询,就是“我明明在 IDEA 里填了环境变量,为什么代码读取还是 null”。常见原因按出现频率排序:
- 没有重启运行进程。IDEA 的运行配置改动,不会影响正在运行的旧进程。要重新
Stop再Run。如果你只是点了Rerun图标,有时候它仍然复用了旧进程的逻辑,一定要先停掉。 - 改错了运行配置。一个项目里可能有多个 Application 运行项(比如
ServiceApplication和AdminApplication),你改了 A 却在启动 B,自然没效果。检查右上角下拉框当前选中的是哪个。 - 用 Maven/Gradle 起项目。再次强调,
mvn spring-boot:runfork 出的子进程不会继承 IDEA 运行配置的环境变量。解决方案有两个:改用Application启动,或者在对应 build 插件里声明环境变量。 - 变量名拼写不一致。
DBPASSWORD和DB_PASSWORD看起来像,但对机器来说是两个完全不同的键。IDEA 不会帮你校验,填错不如不填。 - 配置文件里占位符没写全。比如只写了
${DB_PASSWORD}但 yml 里是password:直接空着,代码读出来是 null 而不是环境变量值。检查格式。
4.2 分号、空格、编码,三个防不胜防的细节
第一个细节,值的分号会坏事。直接手写A=1;b=2格式,如果某个值本身含分号,IDEA 会以第一个分号切开,后面半截被当作一个新变量名。像某些云厂商生成的临时令牌,长得跟乱码一样,里面经常藏着分号或花括号,这时候用表格编辑器最稳。
第二个细节,空格不是自动 trim 的。DB_PASSWORD= dev123456(等号后面有个空格),IDEA 会老老实实把dev123456(带空格)当作值。数据库密码多了个空格,报错信息通常很迷惑。输入时尽量别带空格,如果确实要带,也要心里有数。
第三个细节,.env文件的编码必须是 UTF-8 无 BOM。在 IDEA 里新建文件默认没问题,但从 Windows 记事本或某些老版本工具转存过来的.env文件,往往会带上 BOM 头,第一个变量名就会变成\uFEFFDB_HOST,导致第一个键读取不生效。用 IDEA 右下角的编码指示器检查一下,有 BOM 就另存为 UTF-8 重新转换。
4.3 快速排查速查表
| 症状 | 优先排查 | 处理方式 |
|---|---|---|
| 控制台输出变量为 null | 当前运行的配置和修改的配置是否一致 | 切到正确配置并重启 |
| 数据库连接报 Access denied | 值里是否带了引号、空格或分号截断 | 用表格编辑器重新填写 |
| Maven 方式启动读不到变量 | pom 里缺少 environmentVariables 配置 | 补配置或改用 Application 启动 |
.env第一个变量读不到 | 文件编码带了 BOM | 无 BOM UTF-8 重新保存 |
| 同事拉代码后启动报缺变量 | 对方没有本地配置文件 | 提供.env.example模板并引导复制 |
| 修改变量后仍然用旧值 | 进程未重启 | 先 Stop 再重新 Run |
5. 团队协作里的一点点加分项
5.1 .env.example 模板制度
环境变量配好之后,最大的麻烦是“换一个环境就得重新配一遍”。新同事入职,拉完代码,IDE 一跑,报缺变量。这个时候光靠口头传一份清单是低效的,建议在项目根目录放一个.env.example:
# 数据库 DB_HOST=localhost DB_PORT=3306 DB_USER=root DB_PASSWORD=请改成你自己的本地密码 # 缓存 REDIS_HOST=localhost REDIS_PORT=6379这个文件提交到仓库,里面全是占位符和说明,不含任何真实敏感值。新同事只需要把它复制成.env,再替换成自己本地的配置,就能跑起来了。
对应到 IDEA 运行配置,操作上是:新同事打开Edit Configurations...,选到EnvFile面板,加载自己的.env文件即可。如果你们团队不用.env,那就把.env.example当作一份“变量对照清单”,新人按着清单在运行配置的表格编辑器里挨个填。
提示:有些团队会把 IDEA 的
.idea/runConfigurations目录提交到仓库,这本身没问题,它方便共享启动项配置。但注意千万不要把真实密码写进运行配置文件再共享出去,那样等于把钥匙串发全公司。
5.2 给新人的几条日常建议
环境变量这种“一劳永逸”的配置习惯,越早建立越省事。有几点我在带项目时反复强调:
- 变量命名统一全大写,单词间用下划线。
DB_PASSWORD、REDIS_HOST、PAY_API_KEY这种风格一眼就知道是什么,不容易和代码里的普通变量混淆。 - 不同模块加前缀区分。一个项目里既有数据库又有消息队列,也有第三方支付,统一
DB_、MQ_、PAY_前缀,以后通过System.getenv或配置管理工具做聚合时会非常舒服。 - 本地默认值能留就留。像端口号、localhost 这种非敏感参数,直接在
${DB_PORT:3306}写好默认值,能少配一个是一个。敏感的密码、密钥不要给默认值,强迫自己走环境变量注入的流程。 - 改密码要成群结队地改。如果你发现不仅
application.yml里面有密码,application-prod.yml里面也有,部署脚本里还有一个,那说明项目历史包袱重。别只改一处,要同步排查并逐步迁移到环境变量方案。
最后说点自己的体会
配置环境变量这件事,技术难度几乎是零,但收益是长期的。它最值钱的地方不在于用了什么高科技,而是在于它把“别泄露秘密”这个安全原则,落实到了每个开发者的日常流程里,让你不用去刻意记着“这个文件不能提交”“那个文件不能截图”。我后来做每一个项目的第一件事,就是在运行配置里先把所有敏感变量配齐,再动手写代码。如果你现在的工作流里还是长期把密码写在application.yml里,我真心建议你今天就花五分钟试一次 IDEA 环境变量的配置,把第一个密钥移出去。等你在新电脑上拉下一个项目时,你会感谢这个决定的。