Java Spring Boot 配置读取进阶篇-@ConfigurationProperties && @Value

发布时间 2023-12-23 10:38:12作者: 进击的davis

之前我们学习了在 Spring Boot 如何读取 application.properties/application.yaml 配置文件的配置信息,在上文中我们主要是简单地实践了些简单的设置,这次我们带着同样的问题,如果配置更加复杂,我们的配置读取又应该怎么处理呢。

本文的学习主要基于 Spring Boot 自带的库来解析配置,我们只是加了个 lombok 库方便我们 @Data,这样就不用在 Bean 上写那么多代码。

话不多说,开干。

环境:

  • Spring Boot:3.1.6
  • JDK: 17

依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
</dependency>

接下来的配置解析上,我们主要分是否嵌套的配置项,以及通过 @ConfigurationProperties 和 @Value 来处理。

1.简单配置

对于简单的配置,我们通常稍加代码,就解决了。

application.yaml

app:
  version: 1.0.0
  name: my config

通过@ConfigurationProperties读取配置

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 自定义配置读取
 */
@Configuration
@ConfigurationProperties(prefix = "app")
@Data
public class ConfigPropertiesLoad {
    private String version;

    private String name;
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.ConfigPropertiesLoad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class ConfigPropertiesLoadController {
    @Autowired
    private ConfigPropertiesLoad cfgProperties;

    @GetMapping("/configProperties")
    public Object configProperties() {
        Map<String, Object> info = new HashMap<>();
        info.put("name", cfgProperties.getName());
        info.put("version", cfgProperties.getVersion());
        return info;
    }
}

通过@Value读取配置

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:application.yaml")
@Data
public class ValueReadConfig {
    @Value("${app.version}")
    private String version;

    @Value("${app.name}")
    private String name;
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.ValueReadConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class ValueReadConfigController {

    @Autowired
    private ValueReadConfig config;

    @GetMapping("/valueConfig")
    public Object valueConfig() {
        Map<String, Object> info = new HashMap<>();
        info.put("name", config.getName());
        info.put("version", config.getVersion());
        return info;
    }
}

2.带嵌套的配置

自定义的配置项多级嵌套,application.yaml:

app1:
  name: app1 introduction
  info:
    author: admin
    desc: It's a config demo

通过@ConfigurationProperties读取配置

InfoConfig.java

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "app1.info")
public class InfoConfig {
    private String author;
    private String desc;
}

ConfigPropertiesMultiLevelLoad.java

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 自定义嵌套配置读取
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "app1")
public class ConfigPropertiesMultiLevelLoad {
    private String name;

    @Autowired
    private InfoConfig info;
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.ConfigPropertiesMultiLevelLoad;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class ConfigPropertiesMultiLevelController {

    @Autowired
    private ConfigPropertiesMultiLevelLoad config;

    @GetMapping("/configMultiLevel")
    public Object configMultiLevel() {
        Map<String, Object> ret = new HashMap<>();
        Map<String, Object> info = new HashMap<>();
        info.put("author", config.getInfo().getAuthor());
        info.put("desc", config.getInfo().getDesc());
        ret.put("name", config.getName());
        ret.put("info", info);

        return ret;
    }
}

通过@Value读取配置

配置读取

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource(value = "classpath:application.yaml")
@Data
public class ValueMultiReadConfig {
    @Value("${app1.name}")
    private String name;

    @Value("${app1.info.author}")
    private String author;

    @Value("${app1.info.desc}")
    private String desc;
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.ValueMultiReadConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class ValueMultiReadConfigController {

    @Autowired
    private ValueMultiReadConfig config;

    @GetMapping("/valueMultiLevel")
    public Object valueMultiReadConfig() {
        Map<String, Object> ret = new HashMap<>();
        Map<String, Object> info = new HashMap<>();
        info.put("author", config.getAuthor());
        info.put("desc", config.getDesc());
        ret.put("name", config.getName());
        ret.put("info", info);

        return ret;
    }
}

3.带多级嵌套的配置

如果嵌套级数更多呢,这里我们怎么处理呢,这里以 Redis配置 为例,返回 Redis的配置项参数,application.yaml:

spring:
  data:
    redis:
      port: 6379
      host: localhost
      database: 1
      jedis:
        pool:
          enabled: true
          max-active: 8
          max-idle: 8
          min-idle: 1

通过@ConfigurationProperties读取配置

由于是多级嵌套,我们从最内层到最外层开始编码实现,通过 @AutoWired 一层层自动装配。

Pool.java

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis.jedis.pool")
public class Pool {
    private boolean enabled;

    private int maxActive;

    private int maxIdle;

    private int minIdle;
}

Jedis.java

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis.jedis")
public class Jedis {
    @Autowired Pool pool;
}

Redis.java

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
public class Redis {
    private int port;

    private String host;

    private int database;

    @Autowired
    private Jedis jedis;

    @Override
    public String toString() {
        return String.format("Redis{host=%s, port=%d, database=%d, jedis{enabled=%b, maxActive=%d, maxIdle=%d, minIdle=%d}}",
                host, port, database, jedis.pool.isEnabled(), jedis.pool.getMaxActive(),
                jedis.pool.getMaxIdle(), jedis.pool.getMinIdle());
    }
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.Redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RedisConfigController {
    @Autowired
    private Redis redis;

    @GetMapping("/redisConfig")
    public Object redisConfig() {
        return redis.toString();
    }
}

通过@Value读取配置

package com.example.springbootconfigrddemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@Data
public class PoolConfig {
    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private int port;

    @Value("${spring.data.redis.database}")
    private int database;

    @Value("${spring.data.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Value("${spring.data.redis.jedis.pool.max-active}")
    private int maxActive;

    @Value("${spring.data.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.data.redis.jedis.pool.enabled}")
    private boolean enabled;

    @Override
    public String toString() {
        return String.format("Pool{host=%s, port=%d, database=%d, maxIdle=%d, maxActive=%d, minIdle=%d, enabled=%b}", host, port, database, maxIdle, maxActive, minIdle, enabled);
    }
}

测试读取配置

package com.example.springbootconfigrddemo.controller;

import com.example.springbootconfigrddemo.config.PoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PoolController {
    @Autowired
    private PoolConfig pool;

    @GetMapping("/poolConfig")
    public String poolConfig() {
        return pool.toString();
    }
}

总结

上面我们通过 @ConfigurationProperties 和 @Value 两种方法获取配置,前者读取配置需要注意嵌套的配置需要加一层,通过 @AutoWired 自动装配进来,后者则是通过字段全部展平赋值,如果很多级的情况下,用 @Value 实现的代码更加简洁,而使用 @ConfigurationProperties 则需要层层 AutoWired 。

总的来说,上面两种方法是我们日常开发中使用较多的配置读取的方案,有的方案是通过其他 yaml 三方库获取配置,当然也可以,方法不止一种,但从项目开发角度,怎么方便怎么简洁怎么来,毕竟快速开发业务更加重要。

参考: