美文网首页
@ConditionalOnProperty

@ConditionalOnProperty

作者: 周二倩你一生 | 来源:发表于2020-04-01 10:21 被阅读0次

    @ConditionalOnProperty源码解析

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Documented
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {
        // 数组,获取对应property名称的值,与name不可同时使用
        String[] value() default {};
    
        // 配置属性名称的前缀,比如spring.http.encoding
        String prefix() default "";
    
        // 数组,配置属性完整名称或部分名称
        // 可与prefix组合使用,组成完整的配置属性名称,与value不可同时使用
        String[] name() default {};
    
        // 可与name组合使用,比较获取到的属性值与havingValue给定的值是否相同,相同才加载配置
        String havingValue() default "";
    
        // 缺少该配置属性时是否可以加载。如果为true,没有该配置属性时也会正常加载;反之则不会生效
        boolean matchIfMissing() default false;
    }
    

    最近在写一个“启动时导入数据”的小功能。实现很简单, CommandLineRunner会在SpringBoot启动时运行,第一版长这样:

    @Order(1)
    @Component
    public class DictionaryInitializer implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
              //do import dictionary
        }
    }
    

    自然的,我们希望这个可配置化,只在需要的时候运行。我们使用

    @ConditionalOnProperty
    @Order(1)
    @Component
    @ConditionalOnProperty(name="app.initialize.dictionary", havingValue="true")
    public class DictionaryInitializer implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
              //do import dictionary
        }
    }
    

    然后在application.properties中加入

    app.initialize.dictionary=true
    

    随着功能的迭代,我们又更多类似的导入功能,例如导入Product和Parameter。依样画葫芦,application.properties中多了相似的配置

    app.initialize.dictionary=true
    app.initialize.product=true
    app.initialize.parameter=true
    

    进一步优化,随着导入功能的增多。我们可能需要一个一键导入的功能,同时也要保留原来的功能。

    app.initialize.all=true
    

    这个有点麻烦,涉及到多个条件的组合。ConditionalOnProperty是支持 “多个条件逻辑与”的

    @ConditionalOnProperty(name={"app.initialize.dictionary","app.initialize.all"}, havingValue="true")
    

    当然这个不符合我们的要求,我们需要“逻辑或”
    首先一个类是不能标注多个相同annotation的,编译通不过。

    @Order(1)
    @Component
    ✖️@ConditionalOnProperty(name="app.initialize.dictionary", havingValue="true")
    ✖️@ConditionalOnProperty(name="app.initialize.all", havingValue="true")
    public class DictionaryInitializer implements CommandLineRunner
    

    @ConditionalOnProperty 本身也并没有这样的功能
    一种繁琐的做法,是自定义条件,继承AnyNestedCondition

    class DicOrAllCondition extends AnyNestedCondition {
    
        public DicOrAllCondition() {
            super(ConfigurationPhase.PARSE_CONFIGURATION);
        }
    
        @ConditionalOnProperty(name = "app.initialize.dictionary", value = "true")
        static class DicCondition {
        }
    
        @ConditionalOnProperty(name = "app.initialize.all", value = "true")
        static class AllCondition {
        }
    }
    
    
    @Order(1)
    @Component
    @Conditional(DicOrAllCondition.class)
    public class DictionaryInitializer implements CommandLineRunner{}
    

    还有一种比较灵活的方式是使用@ConditionalOnExpression写一个表达式

    @Order(1)
    @Component
    @ConditionalOnExpression("${app.initialize.dictionary:false} || ${app.initialize.all:false}")
    public class DictionaryInitializer implements CommandLineRunner{}
    

    精益无止境,其实还有更灵活的配置方式:除了dictionary其他都导入:

    app.initialize.all=true
    app.initialize.dictionary=false
    

    换种说法就是:如果局部有配置,则按局部配置处理。如果局部没配置,则按全局配置处理。如果全局、局部都没配置,则默认不导入。这里可以利用“默认值”嵌套表达式实现。

    @ConditionalOnExpression("${app.initialize.dictionary:${app.initialize.all:false}}")
    

    相关文章

      网友评论

          本文标题:@ConditionalOnProperty

          本文链接:https://www.haomeiwen.com/subject/twrouhtx.html