求SpringBoot读取解析ConfigurationProperties的代码及独立模块实现指引
实现独立的SpringBoot风格配置解析工具
要实现一个能像SpringBoot那样解析application.properties/application.yml,并支持系统属性、环境变量覆盖的独立配置解析工具,核心是复用Spring的配置绑定组件,同时剥离SpringBoot的自动装配逻辑。下面是完整的实现方案:
1. 依赖准备(Maven)
只需要引入必要的Spring核心依赖和YAML解析库:
<dependencies> <!-- Spring配置绑定核心依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <!-- 移除日志依赖,按需添加 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <!-- YAML文件解析依赖 --> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>2.2</version> </dependency> </dependencies>
2. 定义配置类
用SpringBoot的@ConfigurationProperties注解标记配置类,指定前缀并定义对应字段(必须提供getter/setter):
import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "app") public class Config { private String name; private int port; private Database database; // 必须添加getter和setter,否则绑定失败 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public Database getDatabase() { return database; } public void setDatabase(Database database) { this.database = database; } @Override public String toString() { return "Config{" + "name='" + name + '\'' + ", port=" + port + ", database=" + database + '}'; } public static class Database { private String url; private String username; private String password; // 同样需要getter和setter public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "Database{" + "url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } } }
3. 核心解析工具类
实现parseConfig方法,完成配置文件加载、环境变量/系统属性整合、配置绑定:
import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.yaml.snakeyaml.Yaml; import java.io.IOException; import java.util.Map; import java.util.Properties; public class ConfigParser { public static <T> T parseConfig(Class<T> configClass) { // 初始化标准环境,自动包含系统属性(systemProperties)和环境变量(systemEnvironment) StandardEnvironment environment = new StandardEnvironment(); MutablePropertySources propertySources = environment.getPropertySources(); // 加载classpath下的配置文件,不存在则跳过 loadPropertiesFile("application.properties", propertySources); loadYamlFile("application.yml", propertySources); // 使用Spring的Binder完成配置绑定 Binder binder = new Binder(ConfigurationPropertySources.from(environment)); return binder.bind("", Bindable.of(configClass)) .orElseThrow(() -> new RuntimeException("配置绑定失败,请检查配置类和配置文件")); } private static void loadPropertiesFile(String fileName, MutablePropertySources propertySources) { Resource resource = new ClassPathResource(fileName); if (!resource.exists()) return; try { Properties props = PropertiesLoaderUtils.loadProperties(resource); propertySources.addLast(new org.springframework.core.env.PropertiesPropertySource(fileName, props)); } catch (IOException e) { throw new RuntimeException("加载properties文件失败:" + fileName, e); } } private static void loadYamlFile(String fileName, MutablePropertySources propertySources) { Resource resource = new ClassPathResource(fileName); if (!resource.exists()) return; try { Yaml yaml = new Yaml(); Map<String, Object> yamlMap = yaml.load(resource.getInputStream()); // 将嵌套的YAML结构扁平化为Spring支持的键值对格式 Properties props = new Properties(); flattenYaml("", yamlMap, props); propertySources.addLast(new org.springframework.core.env.PropertiesPropertySource(fileName, props)); } catch (IOException e) { throw new RuntimeException("加载yaml文件失败:" + fileName, e); } } private static void flattenYaml(String prefix, Map<String, Object> yamlMap, Properties props) { for (Map.Entry<String, Object> entry : yamlMap.entrySet()) { String key = prefix.isEmpty() ? entry.getKey() : prefix + "." + entry.getKey(); if (entry.getValue() instanceof Map) { flattenYaml(key, (Map<String, Object>) entry.getValue(), props); } else { props.setProperty(key, entry.getValue().toString()); } } } }
4. 使用示例
直接调用parseConfig方法即可获取绑定好的配置实例:
public class App { public static void main(String[] args) { var config = ConfigParser.parseConfig(Config.class); System.out.println("加载的配置:" + config); } }
配置优先级说明
按照Spring的规则,配置覆盖优先级从高到低为:
- 系统属性(如启动时添加
-Dapp.name=test) - 环境变量(如设置
APP_NAME=test) application.ymlapplication.properties
如果同一配置项在多个地方存在,高优先级的配置会覆盖低优先级的。
内容的提问来源于stack exchange,提问作者morpheus
相关产品推荐
相关产品推荐

