React+Gradle Spring Boot部署Tomcat多环境(IST/UAT/Prod)URL配置咨询
我之前刚好处理过类似的Spring Boot+React多环境部署场景,给你分享几个实用的方案,都能完美解决fetch URL随环境动态变更的需求:
方案1:Spring Boot启动时动态替换React静态文件占位符
这个方案的核心是让Spring Boot根据当前环境配置,在启动时替换React构建产物里的API地址占位符,不需要React额外发起请求。
第一步:在React项目的
public/index.html中添加全局变量占位符
在<head>标签里插入一段脚本,预留API地址的占位符:<script> window.API_BASE_URL = '{{API_BASE_URL_PLACEHOLDER}}'; </script>第二步:配置Spring Boot多环境属性文件
在src/main/resources下创建对应环境的配置文件:application-ist.properties:app.api.base-url=http://ist-api.your-domain.comapplication-uat.properties:app.api.base-url=http://uat-api.your-domain.comapplication-prod.properties:app.api.base-url=http://prod-api.your-domain.com
主配置文件application.properties可以加个默认值:app.api.base-url=http://localhost:8080
第三步:编写Spring Boot启动时的占位符替换逻辑
创建一个组件,在应用启动后读取配置并替换index.html里的占位符:import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @Component public class ReactEnvInitializer { @Value("${app.api.base-url}") private String apiBaseUrl; @EventListener(ApplicationReadyEvent.class) public void replaceApiUrlPlaceholder() throws Exception { File indexHtml = new File("target/classes/static/index.html"); if (indexHtml.exists()) { String content = Files.readString(indexHtml.toPath(), StandardCharsets.UTF_8); content = content.replace("{{API_BASE_URL_PLACEHOLDER}}", apiBaseUrl); Files.writeString(indexHtml.toPath(), content, StandardCharsets.UTF_8); } } }第四步:React代码中使用全局变量
发起请求时直接调用这个全局变量:fetch(`${window.API_BASE_URL}/api/your-endpoint`) .then(res => res.json()) .then(data => console.log(data));第五步:启动Spring Boot时指定环境
# 启动IST环境 java -jar your-spring-boot-app.jar --spring.profiles.active=ist # 启动UAT环境 java -jar your-spring-boot-app.jar --spring.profiles.active=uat # 启动Prod环境 java -jar your-spring-boot-app.jar --spring.profiles.active=prod
方案2:Spring Boot提供环境配置接口,React启动时请求
这个方案更灵活,React在初始化时先请求后端的配置接口,获取当前环境的API地址,适合需要动态调整配置的场景。
第一步:编写Spring Boot配置接口
创建一个Controller返回当前环境的API配置:import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class EnvConfigController { @Value("${app.api.base-url}") private String apiBaseUrl; @GetMapping("/api/env/config") public Map<String, String> getEnvConfig() { return Map.of("apiBaseUrl", apiBaseUrl); } }第二步:React中获取并全局共享配置
在入口文件src/index.js里,通过Context共享配置:import { createContext, useState, useEffect } from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; export const EnvContext = createContext(); const Root = () => { const [envConfig, setEnvConfig] = useState({ apiBaseUrl: '' }); useEffect(() => { fetch('/api/env/config') .then(res => res.json()) .then(config => setEnvConfig(config)) .catch(err => console.error('Failed to load environment config:', err)); }, []); return ( <EnvContext.Provider value={envConfig}> <App /> </EnvContext.Provider> ); }; ReactDOM.createRoot(document.getElementById('root')).render(<Root />);第三步:React组件中使用配置
在需要发起请求的组件中,通过Context获取API地址:import { useContext } from 'react'; import { EnvContext } from '../index'; const DataFetcher = () => { const { apiBaseUrl } = useContext(EnvContext); const fetchData = () => { fetch(`${apiBaseUrl}/api/your-endpoint`) .then(res => res.json()) .then(data => console.log(data)); }; return <button onClick={fetchData}>Fetch Data</button>; }; export default DataFetcher;
方案3:Gradle构建时注入React环境变量
如果你的CI/CD流程是先构建React再打包Spring Boot,可以让Gradle根据指定的环境,在构建React时直接注入对应的API地址。
第一步:配置React多环境构建脚本
在React项目的package.json中添加不同环境的build命令:"scripts": { "build": "react-scripts build", "build:ist": "REACT_APP_API_BASE_URL=http://ist-api.your-domain.com react-scripts build", "build:uat": "REACT_APP_API_BASE_URL=http://uat-api.your-domain.com react-scripts build", "build:prod": "REACT_APP_API_BASE_URL=http://prod-api.your-domain.com react-scripts build" }第二步:配置Gradle构建逻辑
在Spring Boot的build.gradle中,根据指定的profile执行对应的React构建命令,并将产物复制到Spring Boot的静态资源目录:plugins { id 'org.springframework.boot' version '3.1.0' id 'io.spring.dependency-management' version '1.1.0' id 'java' } def activeProfile = project.findProperty('spring.profiles.active') ?: 'dev' task buildReact(type: Exec) { workingDir 'src/main/react' // 你的React项目根目录 switch(activeProfile) { case 'ist': commandLine 'npm', 'run', 'build:ist' break case 'uat': commandLine 'npm', 'run', 'build:uat' break case 'prod': commandLine 'npm', 'run', 'build:prod' break default: commandLine 'npm', 'run', 'build' } } task copyReactBuild(type: Copy) { from 'src/main/react/build' into 'src/main/resources/static' } buildReact.finalizedBy copyReactBuild processResources.dependsOn buildReact第三步:React代码中使用环境变量
直接调用React内置的环境变量:fetch(`${process.env.REACT_APP_API_BASE_URL}/api/your-endpoint`) .then(res => res.json()) .then(data => console.log(data));第四步:Gradle打包时指定环境
# 打包IST环境 ./gradlew build -Pspring.profiles.active=ist # 打包UAT环境 ./gradlew build -Pspring.profiles.active=uat # 打包Prod环境 ./gradlew build -Pspring.profiles.active=prod
方案选型建议
- 方案1适合追求性能、不需要动态调整配置的场景,启动时一次性替换完成;
- 方案2灵活性最高,甚至可以配合Spring Cloud Config实现运行时配置更新;
- 方案3适合CI/CD流程标准化的场景,构建时就生成对应环境的部署包,部署流程更简洁。
内容的提问来源于stack exchange,提问作者user1912935

