首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >从配置到业务:SpringBoot 配置读取的 7 种实战方案,看完再也不踩坑

从配置到业务:SpringBoot 配置读取的 7 种实战方案,看完再也不踩坑

作者头像
果酱带你啃java
发布2026-04-14 10:24:55
发布2026-04-14 10:24:55
360
举报

在 SpringBoot 开发中,配置读取是连接外部环境与业务逻辑的关键桥梁。无论是数据库连接信息、第三方 API 密钥,还是业务参数,都需要通过配置机制灵活注入应用。但你真的掌握了所有配置读取方式吗?为什么同样的配置在不同场景下表现不同?本文将从基础到进阶,全面解析 SpringBoot 的 7 种配置读取方式,结合底层原理和实战案例,让你既能解决当下问题,又能理解背后逻辑。

一、SpringBoot 配置体系基础

在深入具体读取方式前,我们先理清 SpringBoot 的配置体系。理解这些基础,能帮你在实际开发中避开 80% 的配置问题。

1.1 配置文件的类型与格式

SpringBoot 支持两种主流配置文件格式:

  • properties:键值对格式,如user.name=ken
  • yml/yaml:层级结构格式,更适合复杂配置,如:
代码语言:javascript
复制
user:
  name: ken
  age: 30
代码语言:javascript
复制

两者的核心区别在于语法结构:properties 用.分隔层级,yml 用缩进表示层级(注意缩进必须用空格,不能用 Tab)。

1.2 配置文件的加载顺序

SpringBoot 会从多个位置加载配置,优先级从高到低如下(高优先级配置会覆盖低优先级):

  1. 命令行参数(如java -jar app.jar --user.name=ken
  2. 环境变量
  3. application-{profile}.properties/yml(激活的环境配置)
  4. application.properties/yml(默认配置)
  5. 类路径下的config目录(classpath:config/
  6. 类路径根目录(classpath:/

1.3 外部化配置的底层逻辑

SpringBoot 的配置最终会被抽象为PropertySource对象,所有PropertySource被聚合到Environment中,形成一个统一的配置查询入口。这个过程可以用流程图表示:

二、7 种配置读取方式全解析

2.1 @Value 注解:简单配置的快速注入

原理

@Value通过 Spring 的BeanPostProcessor机制,在 Bean 初始化时解析${}表达式,从Environment中获取对应配置并注入字段。支持 SpEL 表达式(#{}),可实现简单的计算或逻辑。

用法与示例

步骤 1:添加配置(application.yml)

代码语言:javascript
复制
app:
  name: spring-config-demo
  version: 1.0.0
  enable-feature: true
  timeout: 3000
  supported-types: json,xml,csv
  max-connections: 100
代码语言:javascript
复制

步骤 2:创建配置读取类

代码语言:javascript
复制
package com.ken.configdemo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

import java.util.Arrays;

/**
 * 演示@Value注解读取配置
 *
 * @author ken
 */
@RestController
@RequestMapping("/value-demo")
@Slf4j
@Tag(name = "@Value配置读取示例", description = "展示@Value注解的各种用法")
public class ValueDemoController {

    /**
     * 注入字符串类型配置
     */
    @Value("${app.name}")
    private String appName;

    /**
     * 注入整数类型配置
     */
    @Value("${app.version}")
    private String appVersion;

    /**
     * 注入布尔类型配置
     */
    @Value("${app.enable-feature:false}")
    private boolean enableFeature;

    /**
     * 注入长整数类型配置
     */
    @Value("${app.timeout}")
    private Long timeout;

    /**
     * 注入数组(通过逗号分隔的字符串转换)
     */
    @Value("${app.supported-types}")
    private String[] supportedTypes;

    /**
     * 使用SpEL表达式计算(max-connections的1/2)
     */
    @Value("#{${app.max-connections} / 2}")
    private Integer halfConnections;

    /**
     * 展示所有通过@Value注入的配置
     *
     * @return 配置信息
     */
    @GetMapping
    @Operation(summary = "获取@Value注入的配置", description = "返回所有通过@Value注解读取的配置值")
    public String showConfig() {
        log.info("应用名称:{}", appName);
        log.info("支持的类型:{}", Arrays.toString(supportedTypes));

        return String.format(
            "appName: %s, appVersion: %s, enableFeature: %s, timeout: %d, supportedTypes: %s, halfConnections: %d",
            appName, appVersion, enableFeature, timeout, Arrays.toString(supportedTypes), halfConnections
        );
    }
}
代码语言:javascript
复制

关键特性
  • 默认值:通过{key:defaultValue}设置,如@Value("
  • 类型转换:自动将字符串配置转换为目标类型(如StringLongboolean
  • 数组注入:逗号分隔的字符串会自动转换为数组
  • SpEL 支持:通过#{}使用表达式,如#{T(java.lang.Math).random()}
适用场景
  • 零散的简单配置(如单个字符串、数字)
  • 需要简单计算或逻辑的配置
  • 快速原型开发
注意事项
  • 不适合批量注入(如一组相关配置需要多个 @Value)
  • 无法直接注入复杂对象(如嵌套结构的配置)
  • 若配置不存在且未设置默认值,启动时会抛IllegalArgumentException

2.2 @ConfigurationProperties:批量配置的优雅绑定

原理

@ConfigurationProperties通过绑定规则(默认按字段名匹配配置键),将一组相关配置批量注入到 Bean 的字段中。支持复杂类型(如嵌套对象、集合),且提供配置校验能力。

用法与示例

步骤 1:添加配置(application.yml)

代码语言:javascript
复制
database:
  primary:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/main_db?useSSL=false&serverTimezone=UTC
    username: root
    password: root123
    pool:
      max-active: 20
      max-idle: 10
      min-idle: 5
      test-query: SELECT 1
  secondary:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/second_db?useSSL=false&serverTimezone=UTC
    username: root
    password: root123
代码语言:javascript
复制

步骤 2:创建配置类

代码语言:javascript
复制
package com.ken.configdemo.config;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

/**
 * 数据库配置属性类
 * 通过@ConfigurationProperties绑定database前缀的配置
 *
 * @author ken
 */
@Data
@ConfigurationProperties(prefix = "database")
@Validated // 启用配置校验
public class DatabaseProperties {

    /**
     * 主数据库配置
     */
    private DbConfig primary;

    /**
     * 从数据库配置
     */
    private DbConfig secondary;

    /**
     * 数据库连接配置内部类
     */
    @Data
    public static class DbConfig {
        /**
         * 驱动类名(不能为空)
         */
        @NotBlank(message = "数据库驱动类名不能为空")
        private String driverClassName;

        /**
         * 连接URL(不能为空)
         */
        @NotBlank(message = "数据库连接URL不能为空")
        private String url;

        /**
         * 用户名(不能为空)
         */
        @NotBlank(message = "数据库用户名不能为空")
        private String username;

        /**
         * 密码
         */
        private String password;

        /**
         * 连接池配置
         */
        private PoolConfig pool;

        /**
         * 连接池配置内部类
         */
        @Data
        public static class PoolConfig {
            /**
             * 最大活跃连接数(最小值为1)
             */
            @Min(value = 1, message = "最大活跃连接数不能小于1")
            private Integer maxActive;

            /**
             * 最大空闲连接数
             */
            private Integer maxIdle;

            /**
             * 最小空闲连接数
             */
            private Integer minIdle;

            /**
             * 测试查询语句
             */
            private String testQuery;
        }
    }
}
代码语言:javascript
复制

步骤 3:启用配置绑定在启动类添加@ConfigurationPropertiesScan注解,扫描配置类:

代码语言:javascript
复制
package com.ken.configdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan("com.ken.configdemo.config") // 扫描配置类所在包
public class ConfigDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigDemoApplication.class, args);
    }
}
代码语言:javascript
复制

步骤 4:使用配置

代码语言:javascript
复制
package com.ken.configdemo.controller;

import com.ken.configdemo.config.DatabaseProperties;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 演示@ConfigurationProperties的使用
 *
 * @author ken
 */
@RestController
@RequestMapping("/config-properties-demo")
@Slf4j
@Tag(name = "@ConfigurationProperties示例", description = "展示批量配置绑定的用法")
@RequiredArgsConstructor // 生成构造器注入
public class ConfigPropertiesDemoController {

    /**
     * 注入数据库配置(通过构造器注入,Spring推荐方式)
     */
    private final DatabaseProperties databaseProperties;

    /**
     * 展示数据库配置信息
     *
     * @return 配置详情
     */
    @GetMapping("/primary-db")
    @Operation(summary = "获取主数据库配置", description = "返回主数据库的连接信息和连接池配置")
    public String showPrimaryDbConfig() {
        DatabaseProperties.DbConfig primary = databaseProperties.getPrimary();
        DatabaseProperties.DbConfig.PoolConfig pool = primary.getPool();

        log.info("主数据库URL:{}", primary.getUrl());
        log.info("主数据库连接池最大活跃数:{}", pool.getMaxActive());

        return String.format(
            "主库配置:URL=%s, 用户名=%s, 最大连接数=%d",
            primary.getUrl(), primary.getUsername(), pool.getMaxActive()
        );
    }
}
代码语言:javascript
复制

关键特性
  • 批量绑定:通过prefix指定配置前缀,自动匹配字段(支持驼峰与短横线转换,如max-active匹配maxActive
  • 嵌套对象:支持多层嵌套对象(如DatabasePropertiesDbConfigPoolConfig
  • 配置校验:结合@Validated和 JSR-303 注解(如@NotBlank@Min)实现配置合法性校验
  • IDE 支持:主流 IDE(如 IntelliJ IDEA)能识别配置与字段的绑定关系,提供自动补全
适用场景
  • 一组相关的配置(如数据库连接、缓存配置)
  • 复杂结构的配置(嵌套对象、集合)
  • 需要配置校验的场景
与 @Value 的核心区别

特性

@Value

@ConfigurationProperties

批量注入

不支持(需逐个标注)

支持(通过前缀批量绑定)

复杂类型

不支持嵌套对象

支持嵌套对象、集合

配置校验

不支持

支持(结合 @Validated)

松绑定

不支持(严格匹配)

支持(驼峰 / 短横线转换)

默认值

支持(通过 ${key:default})

支持(直接给字段赋值)

2.3 Environment 接口:底层配置的灵活查询

原理

Environment是 Spring 的核心接口,聚合了所有PropertySource,提供统一的配置查询方法。它不仅能获取配置值,还能获取当前激活的环境(profile)等信息。

用法与示例

步骤 1:添加配置(application.yml)

代码语言:javascript
复制
spring:
  profiles:
    active: dev
cache:
  type: redis
  redis:
    expiration: 3600
    host: localhost
    port: 6379
代码语言:javascript
复制

步骤 2:使用 Environment 查询配置

代码语言:javascript
复制
package com.ken.configdemo.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.StringUtils;

/**
 * 演示Environment接口读取配置
 *
 * @author ken
 */
@RestController
@RequestMapping("/environment-demo")
@Slf4j
@Tag(name = "Environment示例", description = "展示通过Environment接口读取配置的用法")
@RequiredArgsConstructor
public class EnvironmentDemoController {

    /**
     * 注入Environment接口
     */
    private final Environment environment;

    /**
     * 获取缓存配置及当前环境信息
     *
     * @return 配置信息
     */
    @GetMapping("/cache-info")
    @Operation(summary = "获取缓存配置", description = "通过Environment获取缓存配置和当前激活的环境")
    public String getCacheInfo() {
        // 获取简单配置
        String cacheType = environment.getProperty("cache.type");

        // 获取整数配置(指定默认值)
        Integer redisPort = environment.getProperty("cache.redis.port", Integer.class, 6379);

        // 获取长整数配置
        Long expiration = environment.getProperty("cache.redis.expiration", Long.class);

        // 获取当前激活的环境
        String[] activeProfiles = environment.getActiveProfiles();
        String activeProfilesStr = StringUtils.arrayToCommaDelimitedString(activeProfiles);

        log.info("当前激活环境:{}", activeProfilesStr);
        log.info("缓存类型:{},Redis端口:{}", cacheType, redisPort);

        return String.format(
            "激活环境:%s, 缓存类型:%s, Redis地址:%s:%d, 过期时间:%ds",
            activeProfilesStr,
            cacheType,
            environment.getProperty("cache.redis.host"),
            redisPort,
            expiration
        );
    }
}
代码语言:javascript
复制

关键方法
  • getProperty(String key):获取字符串类型配置
  • getProperty(String key, Class<T> type):获取指定类型的配置
  • getProperty(String key, Class<T> type, T defaultValue):带默认值的类型转换
  • getActiveProfiles():获取当前激活的环境
  • containsProperty(String key):判断配置是否存在
适用场景
  • 动态查询配置(如根据变量名动态获取不同配置)
  • 需要获取环境信息(如当前激活的 profile)
  • 框架开发中需要底层配置访问能力
注意事项
  • 需手动处理类型转换(若转换失败返回 null)
  • 频繁调用时建议缓存结果(避免重复解析)
  • 不支持直接绑定对象(需手动封装)

2.4 @PropertySource:自定义配置文件的加载

原理

@PropertySource用于加载指定路径的自定义配置文件(默认支持 properties 格式),加载的配置会被添加到Environment中,可通过@Value@ConfigurationProperties读取。

用法与示例

步骤 1:创建自定义配置文件(classpath:config/app-features.properties)

代码语言:javascript
复制
# 自定义功能配置
feature.payment.enable=true
feature.payment.supported-methods=alipay,wechat,unionpay
feature.notify.url=http://localhost:8080/notify
feature.rate.limit=100
代码语言:javascript
复制

步骤 2:加载自定义配置文件

代码语言:javascript
复制
package com.ken.configdemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * 加载自定义配置文件
 *
 * @author ken
 */
@Configuration
@PropertySource(value = "classpath:config/app-features.properties", encoding = "UTF-8")
// 若需要加载多个文件,可使用数组:@PropertySource(value = {"file:./config/ext.properties", "classpath:app.properties"})
public class CustomConfigLoader {
}
代码语言:javascript
复制

步骤 3:读取自定义配置

代码语言:javascript
复制
package com.ken.configdemo.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.StringUtils;

import java.util.Arrays;

/**
 * 演示@PropertySource加载自定义配置
 *
 * @author ken
 */
@RestController
@RequestMapping("/property-source-demo")
@Slf4j
@Tag(name = "@PropertySource示例", description = "展示加载自定义配置文件的用法")
@RequiredArgsConstructor
public class PropertySourceDemoController {

    @Value("${feature.payment.enable}")
    private boolean paymentEnabled;

    @Value("${feature.payment.supported-methods}")
    private String[] supportedPaymentMethods;

    @Value("${feature.notify.url}")
    private String notifyUrl;

    @Value("${feature.rate.limit}")
    private Integer rateLimit;

    /**
     * 展示自定义配置文件中的配置
     *
     * @return 配置信息
     */
    @GetMapping
    @Operation(summary = "获取自定义配置", description = "返回通过@PropertySource加载的配置")
    public String showCustomConfig() {
        log.info("支付功能是否启用:{}", paymentEnabled);
        log.info("支持的支付方式:{}", Arrays.toString(supportedPaymentMethods));

        return String.format(
            "支付功能:%s, 支持方式:%s, 通知地址:%s, 限流阈值:%d",
            paymentEnabled ? "启用" : "禁用",
            StringUtils.arrayToCommaDelimitedString(supportedPaymentMethods),
            notifyUrl,
            rateLimit
        );
    }
}
代码语言:javascript
复制

加载 YAML 文件

@PropertySource默认不支持 YAML 文件,需自定义PropertySourceFactory

步骤 1:创建 YAML 配置工厂

代码语言:javascript
复制
package com.ken.configdemo.config;

import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable;

import java.io.IOException;
import java.util.List;

/**
 * 支持YAML文件的PropertySourceFactory
 *
 * @author ken
 */
public class YamlPropertySourceFactory implements PropertySourceFactory {
    @Override
    public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
        // 使用SpringBoot的YamlPropertySourceLoader加载YAML文件
        YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
        List<PropertySource<?>> sources = loader.load(resource.getResource().getFilename(), resource.getResource());
        return sources.get(0);
    }
}
代码语言:javascript
复制

步骤 2:加载 YAML 文件(classpath:config/third-party.yml)

代码语言:javascript
复制
third-party:
  oss:
    endpoint: oss-cn-beijing.aliyuncs.com
    access-key: LTAI4Gxxxxxxxxx
    secret-key: 8hxxxxxxxxxxxxxxxxxxxx
    bucket-name: my-bucket
代码语言:javascript
复制

步骤 3:通过 @PropertySource 加载

代码语言:javascript
复制
package com.ken.configdemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * 加载YAML格式的自定义配置文件
 *
 * @author ken
 */
@Configuration
@PropertySource(
    value = "classpath:config/third-party.yml",
    factory = YamlPropertySourceFactory.class, // 指定自定义工厂
    encoding = "UTF-8"
)
public class YamlConfigLoader {
}
代码语言:javascript
复制

适用场景
  • 配置分离(如将业务配置与框架配置分开)
  • 多环境下的个性化配置
  • 需要加载外部文件系统的配置(通过file:前缀)
注意事项
  • 自定义文件的优先级低于默认配置文件(可被覆盖)
  • 路径支持classpath:(类路径)和file:(文件系统)
  • 加载 YAML 需自定义PropertySourceFactory

2.5 构造器注入:依赖注入的最佳实践

原理

构造器注入是 Spring 推荐的依赖注入方式,通过构造方法将配置值注入到 Bean 中。相比字段注入,它能确保 Bean 在实例化时就完成配置注入,避免NullPointerException

用法与示例

步骤 1:添加配置(application.yml)

代码语言:javascript
复制
message:
  welcome: "欢迎使用SpringBoot配置示例"
  max-length: 200
  enabled: true
代码语言:javascript
复制

步骤 2:通过构造器注入配置

代码语言:javascript
复制
package com.ken.configdemo.service;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * 演示构造器注入配置
 *
 * @author ken
 */
@Service
@Slf4j
@Getter // 提供getter方法
public class MessageService {

    /**
     * 欢迎消息
     */
    private final String welcomeMessage;

    /**
     * 消息最大长度
     */
    private final Integer maxLength;

    /**
     * 是否启用消息功能
     */
    private final boolean enabled;

    /**
     * 通过构造器注入配置
     *
     * @param welcomeMessage 欢迎消息
     * @param maxLength 消息最大长度
     * @param enabled 是否启用
     */
    public MessageService(
            @Value("${message.welcome}") String welcomeMessage,
            @Value("${message.max-length}") Integer maxLength,
            @Value("${message.enabled:true}") boolean enabled) {
        // 校验配置合法性
        if (!StringUtils.hasText(welcomeMessage)) {
            throw new IllegalArgumentException("欢迎消息不能为空");
        }
        if (maxLength == null || maxLength <= 0) {
            throw new IllegalArgumentException("消息最大长度必须大于0");
        }

        this.welcomeMessage = welcomeMessage;
        this.maxLength = maxLength;
        this.enabled = enabled;

        log.info("MessageService初始化完成,欢迎消息:{}", welcomeMessage);
    }

    /**
     * 处理消息(截断超长消息)
     *
     * @param content 消息内容
     * @return 处理后的消息
     */
    public String processMessage(String content) {
        if (!enabled) {
            return "消息功能已禁用";
        }
        if (content.length() > maxLength) {
            return content.substring(0, maxLength) + "...";
        }
        return content;
    }
}
代码语言:javascript
复制

步骤 3:使用服务

代码语言:javascript
复制
package com.ken.configdemo.controller;

import com.ken.configdemo.service.MessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 演示构造器注入配置的使用
 *
 * @author ken
 */
@RestController
@RequestMapping("/constructor-inject-demo")
@Slf4j
@Tag(name = "构造器注入示例", description = "展示通过构造器注入配置的最佳实践")
@RequiredArgsConstructor
public class ConstructorInjectDemoController {

    private final MessageService messageService;

    /**
     * 处理消息
     *
     * @param content 消息内容
     * @return 处理结果
     */
    @GetMapping("/process")
    @Operation(summary = "处理消息", description = "根据配置处理消息内容(截断超长消息)")
    public String processMessage(@RequestParam String content) {
        return messageService.processMessage(content);
    }

    /**
     * 获取欢迎消息
     *
     * @return 欢迎消息
     */
    @GetMapping("/welcome")
    @Operation(summary = "获取欢迎消息", description = "返回通过构造器注入的欢迎消息")
    public String getWelcomeMessage() {
        return messageService.getWelcomeMessage();
    }
}
代码语言:javascript
复制

核心优势
  • 不可变性:字段可声明为final,确保注入后不可修改
  • 依赖明确:通过构造方法参数清晰展示 Bean 的依赖
  • 避免 NPE:实例化时完成注入,防止未注入就使用的情况
  • 便于测试:单元测试时可通过构造方法手动传入参数
适用场景
  • 所有需要注入配置的 Bean(Spring 官方推荐)
  • 对不可变性有要求的场景
  • 需要在初始化时校验配置的场景

2.6 配置中心集成:分布式环境的配置管理

在分布式系统中,单机配置文件无法满足多实例同步更新的需求,配置中心(如 Nacos、Apollo)成为最佳选择。SpringBoot 可无缝集成配置中心,实现配置的动态更新。

以 Nacos 为例的集成示例

步骤 1:添加依赖(pom.xml)

代码语言:javascript
复制
<!-- Nacos配置中心依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2023.0.1.0</version>
</dependency>
<!-- SpringCloud上下文依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
    <version>4.1.1</version>
</dependency>
代码语言:javascript
复制

步骤 2:配置 Nacos 地址(bootstrap.yml,优先级高于 application.yml)

代码语言:javascript
复制
spring:
  application:
    name: config-demo # 应用名,用于匹配Nacos中的配置
  cloud:
    nacos:
      config:
        server-addr: localhost:8848 # Nacos服务器地址
        file-extension: yml # 配置文件格式
        group: DEFAULT_GROUP # 配置分组
        namespace: public # 命名空间(默认public)
代码语言:javascript
复制

步骤 3:在 Nacos 控制台添加配置

  • 配置 ID:config-demo.yml(格式:{spring.application.name}.{file-extension})
  • 配置内容:
代码语言:javascript
复制
distributed:
  config:
    enable-monitor: true
    refresh-interval: 5000
    server-list: 192.168.1.100:8080,192.168.1.101:8080
代码语言:javascript
复制

步骤 4:读取 Nacos 配置(支持动态更新)

代码语言:javascript
复制
package com.ken.configdemo.controller;

import com.alibaba.fastjson2.JSON;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 演示Nacos配置中心的使用
 *
 * @author ken
 */
@RestController
@RequestMapping("/nacos-config-demo")
@Slf4j
@Tag(name = "Nacos配置中心示例", description = "展示分布式环境下通过配置中心读取配置")
@RequiredArgsConstructor
@RefreshScope // 启用配置动态刷新
public class NacosConfigDemoController {

    @Value("${distributed.config.enable-monitor:false}")
    private boolean enableMonitor;

    @Value("${distributed.config.refresh-interval:3000}")
    private Integer refreshInterval;

    @Value("${distributed.config.server-list}")
    private String[] serverList;

    /**
     * 获取分布式配置
     *
     * @return 配置信息
     */
    @GetMapping
    @Operation(summary = "获取分布式配置", description = "返回从Nacos配置中心读取的配置,支持动态更新")
    public String getDistributedConfig() {
        Map<String, Object> configMap = new HashMap<>(3);
        configMap.put("enableMonitor", enableMonitor);
        configMap.put("refreshInterval", refreshInterval);
        configMap.put("serverList", Arrays.toString(serverList));

        log.info("分布式配置:{}", JSON.toJSONString(configMap));
        return JSON.toJSONString(configMap);
    }
}
代码语言:javascript
复制

动态更新原理

@RefreshScope注解会为 Bean 创建代理对象,当配置更新时,Spring 会销毁旧的 Bean 实例,创建新实例并注入最新配置。流程如下:

适用场景
  • 分布式系统(多实例部署)
  • 需要动态更新配置(无需重启应用)
  • 配置需要版本管理、权限控制的场景

2.7 自定义配置读取工具类:特殊场景的灵活处理

在某些场景(如工具类、非 Spring 管理的类)中,无法直接使用 Spring 的注解注入配置,此时需要自定义工具类读取配置。

实现示例

步骤 1:创建配置工具类

代码语言:javascript
复制
package com.ken.configdemo.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * 配置读取工具类(支持非Spring管理的类调用)
 *
 * @author ken
 */
@Component
@Slf4j
public class ConfigUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 设置ApplicationContext(Spring自动调用)
     *
     * @param context 应用上下文
     */
    @Override
    public void setApplicationContext(ApplicationContext context) {
        if (applicationContext == null) {
            applicationContext = context;
            log.info("ConfigUtils初始化完成");
        }
    }

    /**
     * 获取Environment对象
     *
     * @return Environment实例
     */
    private static Environment getEnvironment() {
        if (applicationContext == null) {
            throw new IllegalStateException("Spring上下文未初始化,无法获取配置");
        }
        return applicationContext.getEnvironment();
    }

    /**
     * 获取字符串类型配置
     *
     * @param key 配置键
     * @return 配置值(不存在返回null)
     */
    public static String getString(String key) {
        return getEnvironment().getProperty(key);
    }

    /**
     * 获取字符串类型配置(带默认值)
     *
     * @param key 配置键
     * @param defaultValue 默认值
     * @return 配置值(不存在返回默认值)
     */
    public static String getString(String key, String defaultValue) {
        String value = getEnvironment().getProperty(key);
        return StringUtils.hasText(value) ? value : defaultValue;
    }

    /**
     * 获取整数类型配置
     *
     * @param key 配置键
     * @return 配置值(不存在或转换失败返回null)
     */
    public static Integer getInteger(String key) {
        return getEnvironment().getProperty(key, Integer.class);
    }

    /**
     * 获取整数类型配置(带默认值)
     *
     * @param key 配置键
     * @param defaultValue 默认值
     * @return 配置值(不存在或转换失败返回默认值)
     */
    public static Integer getInteger(String key, Integer defaultValue) {
        return getEnvironment().getProperty(key, Integer.class, defaultValue);
    }

    /**
     * 获取布尔类型配置
     *
     * @param key 配置键
     * @param defaultValue 默认值
     * @return 配置值(不存在返回默认值)
     */
    public static boolean getBoolean(String key, boolean defaultValue) {
        return getEnvironment().getProperty(key, Boolean.class, defaultValue);
    }
}
代码语言:javascript
复制

步骤 2:在非 Spring 管理的类中使用

代码语言:javascript
复制
package com.ken.configdemo.util;

import lombok.extern.slf4j.Slf4j;

/**
 * 非Spring管理的工具类(演示如何使用ConfigUtils)
 *
 * @author ken
 */
@Slf4j
public class CommonUtils {

    /**
     * 生成业务编号
     *
     * @return 业务编号
     */
    public static String generateBizNo() {
        // 从配置获取前缀
        String prefix = ConfigUtils.getString("biz.no.prefix", "BIZ");
        // 从配置获取长度
        int length = ConfigUtils.getInteger("biz.no.length", 16);

        log.info("生成业务编号,前缀:{},长度:{}", prefix, length);

        // 简化的编号生成逻辑(实际项目需更复杂实现)
        return prefix + System.currentTimeMillis() % (long) Math.pow(10, length - prefix.length());
    }
}
代码语言:javascript
复制

步骤 3:添加配置(application.yml)

代码语言:javascript
复制
biz:
  no:
    prefix: ORDER
    length: 20
代码语言:javascript
复制

步骤 4:在控制器中使用

代码语言:javascript
复制
package com.ken.configdemo.controller;

import com.ken.configdemo.util.CommonUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 演示自定义配置工具类的使用
 *
 * @author ken
 */
@RestController
@RequestMapping("/config-utils-demo")
@Slf4j
@Tag(name = "配置工具类示例", description = "展示在非Spring管理类中读取配置")
public class ConfigUtilsDemoController {

    /**
     * 生成业务编号
     *
     * @return 业务编号
     */
    @GetMapping("/generate-biz-no")
    @Operation(summary = "生成业务编号", description = "通过自定义工具类读取配置生成业务编号")
    public String generateBizNo() {
        return CommonUtils.generateBizNo();
    }
}
代码语言:javascript
复制

适用场景
  • 非 Spring 管理的类(如静态工具类)
  • 需要在静态方法中读取配置
  • 框架或工具类开发(需脱离 Spring 注解)
注意事项
  • 工具类需在 Spring 初始化完成后使用(避免ApplicationContext为 null)
  • 不支持动态更新(若需动态更新,需结合@RefreshScope和事件监听)
  • 谨慎使用(优先考虑 Spring 的注入方式,工具类仅作为补充)

三、配置读取的最佳实践

3.1 配置方式的选择策略

  • 简单零散配置 → @Value
  • 批量相关配置 → @ConfigurationProperties
  • 动态查询或环境信息 → Environment
  • 自定义配置文件 → @PropertySource
  • 分布式环境 → 配置中心(Nacos/Apollo)
  • 非 Spring 类 → 自定义工具类
  • 任何场景 → 优先使用构造器注入

3.2 配置管理的注意事项

  1. 敏感配置加密:数据库密码、API 密钥等敏感信息需加密存储(可使用 Spring Cloud Config Server 或 Nacos 的加密功能)
  2. 配置分层:按功能划分配置(如database.ymlcache.yml),避免单一文件过大
  3. 默认值设置:关键配置需设置合理默认值,增强容错性
  4. 配置校验:使用@Validated确保配置合法性,提前暴露问题
  5. 避免硬编码:所有可能变化的参数都应通过配置注入,而非硬编码在代码中

四、总结

SpringBoot 提供了丰富的配置读取方式,每种方式都有其适用场景:从简单的@Value到批量绑定的@ConfigurationProperties,从底层的Environment到分布式的配置中心,再到自定义工具类,形成了完整的配置生态。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、SpringBoot 配置体系基础
    • 1.1 配置文件的类型与格式
    • 1.2 配置文件的加载顺序
    • 1.3 外部化配置的底层逻辑
  • 二、7 种配置读取方式全解析
    • 2.1 @Value 注解:简单配置的快速注入
      • 原理
      • 用法与示例
      • 关键特性
      • 适用场景
      • 注意事项
    • 2.2 @ConfigurationProperties:批量配置的优雅绑定
      • 原理
      • 用法与示例
      • 关键特性
      • 适用场景
      • 与 @Value 的核心区别
    • 2.3 Environment 接口:底层配置的灵活查询
      • 原理
      • 用法与示例
      • 关键方法
      • 适用场景
      • 注意事项
    • 2.4 @PropertySource:自定义配置文件的加载
      • 原理
      • 用法与示例
      • 加载 YAML 文件
      • 适用场景
      • 注意事项
    • 2.5 构造器注入:依赖注入的最佳实践
      • 原理
      • 用法与示例
      • 核心优势
      • 适用场景
    • 2.6 配置中心集成:分布式环境的配置管理
      • 以 Nacos 为例的集成示例
      • 动态更新原理
      • 适用场景
    • 2.7 自定义配置读取工具类:特殊场景的灵活处理
      • 实现示例
      • 适用场景
      • 注意事项
  • 三、配置读取的最佳实践
    • 3.1 配置方式的选择策略
    • 3.2 配置管理的注意事项
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档