Spring 获取配置的三种方式

前言

最近在写框架时遇到需要根据特定配置(可能不存在)加载 bean 的需求,所以就学习了下 Spring 中如何获取配置的几种方式。

Spring 中获取配置的三种方式

  1. 通过 @Value 方式动态获取单个配置
  2. 通过 @ConfigurationProperties + 前缀方式批量获取配置
  3. 通过 Environment 动态获取单个配置

通过 @Value 动态获取单个配置

  1. 作用

    1. 可修饰到任一变量获取,使用较灵活
  2. 优点

    1. 使用简单,且使用关联的链路较短
  3. 缺点

    1. 配置名不能被有效枚举到

    2. 每一个配置的使用都需重新定义,使用较为麻烦

    3. 项目强依赖配置的定义,配置不存在则会导致项目无法启动

  4. 使用场景

    1. 项目强依赖该配置的加载,想要从源头避免因配置缺失导致的未知问题

    2. 只想使用少数几个配置

  5. 代码示例

    @Configuration
    public class ConfigByValueAnnotation {
    
        @Value("${server.port}")
        private String serverPort;
    
        public String getServerPort() {
            return serverPort;
        }
    }
    
  6. 测试代码

@DisplayName("multipart get config")
@SpringBootTest
public class MultipartGetConfigTest {

    private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);

    @Autowired
    private ConfigByValueAnnotation configByValueAnnotation;

    @Test
    public void getByValueAnnotation(){
        log.info("get by @Value, value: {}", configByValueAnnotation.getServerPort());
    }
}
  1. 测试结果
org.spring.demo.MultipartGetConfigTest   : get by @Value, value: 7100

通过 @ConfigurationProperties + 前缀方式批量获取

  1. 作用

    1. 用于配置类的修饰或批量配置的获取
  2. 优点

    1. 使用配置只需确定 key 的前缀即能使用,有利于批量获取场景的使用

    2. 因采用前缀匹配,所以在使用新的相同前缀 key 的配置时无需改动代码

  3. 缺点

    1. 使用复杂,需定义配置类或者手动创建 bean 后引入使用

    2. 增加新的前缀相同 key 时可能会引入不稳定因素

  4. 使用场景

    1. 需要同时使用多前缀相同 key 的配置

    2. 期望增加新配置但不修改代码的 properties 注入

  5. 代码示例

    @Component
    @ConfigurationProperties(prefix = "server", ignoreInvalidFields = true)
    public class ConfigByConfigurationProperties {
    
        private Integer port;
    
        public Integer getPort() {
            return port;
        }
    
        public ConfigByConfigurationProperties setPort(Integer port) {
            this.port = port;
            return this;
        }
    }
    
    @Configuration
    public class ConfigByConfigurationPropertiesV2 {
    
        @Bean("configByValueAnnotationV2")
        @ConfigurationProperties(prefix = "server2")
        public Properties properties(){
            return new Properties();
        }
    
    }
    
    1. 测试代码
@DisplayName("multipart get config")
@SpringBootTest
public class MultipartGetConfigTest {

       private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);
    
       @Autowired
       private ConfigByConfigurationProperties configByConfigurationProperties;
    
       @Autowired
       @Qualifier("configByValueAnnotationV2")
       private Properties properties;
    
       @Test
       public void getByConfigurationProperties(){
           log.info("get by @ConfigurationProperties, value: {}", configByConfigurationProperties.getPort());
           log.info("get by @ConfigurationProperties and manual create bean, value: {}", properties.getProperty("port"));
       }

}
  1. 测试结果
org.spring.demo.MultipartGetConfigTest   : get by @ConfigurationProperties, value: 7100
org.spring.demo.MultipartGetConfigTest   : get by @ConfigurationProperties and manual create bean, value: 7100

通过 Environment 动态获取单个配置

  1. 作用

    1. 用于动态在程序代码中获取配置,而配置 key 不需提前定义
  2. 优点

    1. 获取的配置的 key 可不提前定义,程序灵活性高

    2. 配置 key 可使用枚举统一放置与管理

  3. 缺点

    1. 使用较复杂,需继承 Environment 接口形成工具类进行获取

    2. 获取 key 对应的枚举与 key 定义分离,value 获取链路较长

  4. 使用场景

    1. 只需使用少量的配置

    2. 获取配置的 key 无提前定义,需要根据对配置的有无进行灵活使用

  5. 代码示例

@Component
public class ConfigByEnvironment implements EnvironmentAware {

     private static final Logger log = LoggerFactory.getLogger(ConfigByEnvironment.class);
  
     private Environment environment;
  
     public Optional<String> get(String configKey){
         String config = environment.getProperty(configKey);
         return Objects.isNull(config) ? Optional.empty() : Optional.of(config);
     }
  
     public void get(String configKey, Consumer<String> consumer){
         Optional<String> config = get(configKey);
         if(!config.isPresent()){
             log.warn("application config, get config by key fail, key: {}", configKey);
         }
         config.ifPresent(consumer);
     }
  
     @Override
     public void setEnvironment(@NonNull Environment environment) {
         this.environment = environment;
     }
}

public enum ConfigByEnvironmentKey {

     SERVER_PORT("server.port", "server port");
  
     private String key;
  
     private String description;
  
     ConfigByEnvironmentKey(String key, String description) {
         this.key = key;
         this.description = description;
     }
  
     public String getKey() {
         return key;
     }
  
     public String getDescription() {
         return description;
     }
}
  1. 测试代码
@DisplayName("multipart get config")
   @SpringBootTest
   public class MultipartGetConfigTest {

       private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class);
    
       @Autowired
       private ConfigByEnvironment configByEnvironment;
    
       @Test
       public void getByEnvironment(){
           configByEnvironment.get(ConfigByEnvironmentKey.SERVER_PORT.getKey()).ifPresent(value -> log.info("get by environment, value: {}", value));
       }

}
  1. 测试结果
org.spring.demo.MultipartGetConfigTest   : get by environment, value: 7100

总结

获取配置方式 优点 缺点 使用场景
通过 @Value 动态获取单个配置 使用简单,且使用关联的链路较短 1. 配置名不能被有效枚举到<br />2. 每一个配置的使用都需重新定义,使用较为麻烦<br />3. 项目强依赖配置的定义,配置不存在则会导致项目无法启动 1. 项目强依赖该配置的加载,想要从源头避免因配置缺失导致的未知问题<br />2. 只想使用少数几个配置
通过 @ConfigurationProperties + 前缀方式批量获取 1. 使用配置只需确定 key 的前缀即能使用,有利于批量获取场景的使用 <br />2. 因采用前缀匹配,所以在使用新的相同前缀 key 的配置时无需改动代码 1. 使用复杂,需定义配置类或者手动创建 bean 后引入使用<br />2. 增加新的前缀相同 key 时可能会引入不稳定因素 1. 需要同时使用多前缀相同 key 的配置<br />2. 期望增加新配置但不修改代码的 properties 注入
通过 Environment 动态获取单个配置 1. 获取的配置的 key 可不提前定义,程序灵活性高<br />2. 配置 key 可使用枚举统一放置与管理 1. 使用较复杂,需继承 Environment 接口形成工具类进行获取<br />2. 获取 key 对应的枚举与 key 定义分离,value 获取链路较长 1. 只需使用少量的配置<br />2. 获取配置的 key 无提前定义,需要根据对配置的有无进行灵活使用

本代码示例已放置于 github测试代码位置,有需要的可自取。
本文首发于 cartoon的博客

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容