楼层: 首页/ 软件技术/ Spring 核心框架/ 资源、环境与配置:多环境切换的正确姿势
6

资源、环境与配置:多环境切换的正确姿势

Resources, Environment & Profiles

开发、测试、生产三套环境,数据库地址不一样、开关不一样、日志级别不一样。你不能靠"上线前手动改配置文件"过日子——那是事故温床。Spring 提供了一套机制:配置外置 + Profile 多环境 + 条件装配,让同一份代码在不同环境下自动加载不同配置。

论四个核心 API

Resource 接口:统一抽象 classpath、文件系统、URL 上的资源,不用关心文件到底在哪。

Environment:当前环境的抽象,能拿到激活的 Profile、系统属性、环境变量。

@Profile:标在 Bean 上,只有指定环境激活时这个 Bean 才会被创建。

@Value / @ConfigurationProperties:把配置文件里的值注入到字段里。

用 @Profile 区分开发和生产的数据源

@Configuration public class DataSourceConfig { // 只有 dev 环境激活时才创建这个 DataSource @Bean @Profile("dev") public DataSource devDataSource() { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl("jdbc:mysql://localhost:3306/dev_db"); return ds; } // 生产环境用另一个 @Bean @Profile("prod") public DataSource prodDataSource() { HikariDataSource ds = new HikariDataSource(); ds.setJdbcUrl("jdbc:mysql://prod-db:3306/app_db"); return ds; } }

@Value 和 @ConfigurationProperties 注入配置

// 方式一:@Value 一个个注入,适合少量配置 @Component public class EmailService { @Value("${mail.host:smtp.example.com}") // 冒号后面是默认值 private String host; @Value("${mail.port:25}") private int port; } // 方式二:@ConfigurationProperties 批量绑定,推荐 // 配置文件里写: // app.feature.cache-enabled=true // app.feature.max-items=100 @Component @ConfigurationProperties(prefix = "app.feature") public class FeatureProperties { private boolean cacheEnabled; private int maxItems; // 省略 getter / setter }

条件装配 @Conditional:满足条件才注册 Bean

// 只有类路径下存在 com.mysql.cj.jdbc.Driver 时,才注册这个 Bean @Bean @ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver") public DataSource mysqlDataSource() { ... } // 只有某个配置项为 true 时才注册 @Bean @ConditionalOnProperty(name = "app.cache.enabled", havingValue = "true") public CacheManager cacheManager() { ... }

Resource 接口:统一抽象 classpath、文件、URL

Java 标准库读文件很分裂:读 classpath 用 ClassLoader.getResourceAsStream,读磁盘用 FileInputStream,读 HTTP 用 URLConnection——三套 API。Spring 把它们统一成 Resource 接口,你不用关心资源到底在哪。

前缀指向哪里
classpath:config.xml从 classpath 资源(jar 包内)读。
file:/etc/app/config.yml从文件系统读。
https://example.com/x.json从 URL 读。
无前缀按资源类型自动判断,通常走 classpath。

注入 ResourceLoader 读取资源文件

@Service public class TemplateLoader { private final ResourceLoader resourceLoader; public TemplateLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } public String load() throws IOException { // 不管文件在 jar 里还是磁盘上,统一用 getResource 拿 Resource res = resourceLoader.getResource("classpath:templates/welcome.txt"); try (var in = res.getInputStream()) { return new String(in.readAllBytes(), StandardCharsets.UTF_8); } } }

Spring Boot 的自动配置原理,本质上就是一堆 @ConditionalOnXxx——类路径下有什么类、配置文件里有什么键,Spring 就决定要不要注册某个 Bean。这是第 9 章要讲的内容。

本章面试题 · 第 6 章(资源与配置)

1.(概念题)@Value("${app.name}") 和 @ConfigurationProperties 有什么区别?为什么后者更推荐?

查看答案

答案:@Value 一个注解只注入一个值,零散;@ConfigurationProperties 把一组配置批量绑定到一个 Java 对象(甚至 record),类型安全、可校验、IDE 能补全。解析:配置项少用 @Value,成组的、要校验的用 @ConfigurationProperties。

2.(实战题)Spring 怎么区分开发/测试/生产环境?

查看答案

答案:@Profile("dev") 标记不同环境的 Bean,配合 spring.profiles.active=dev 激活。配置文件拆成 application-dev.yml、application-prod.yml。解析:数据库地址、日志级别这种环境相关的东西,全按 profile 隔离,别硬编码。