@ConditionalOnProperty的使用与原理

一、宏观理解

通过字面意思可以看出,它依据配置文件的内容作为条件。那么作为条件后,他又有什么用处呢?

点击这个注解,我们可以看出这是一个基于springboot自动化配置的注解,它作用于接口、类、枚举、注解、方法之上。

本文以下面这个方法为例,也是真实项目中的一个例子:基本使用很简单,增加注解并且配置name和havingValue属性

目的是针对不同的配置,注册不同的配置bean。

如下图所示

同时它还组合了注解@Conditional({OnPropertyCondition.class}),conditional注解是springframework的功能,内部只有一个属性那就是一个class文件数组。通过上图和下面的图示我们可以看出,springboot的ConditionalOnProperty其实是组合了@Conditional({OnPropertyCondition.class}),OnPropertyCondition.class究竟是什么,我们一起来看看。

 

二、OnPropertyCondition.class

下面我们来看看这个类究竟要干些什么,我们开始不必很细致的精读每一行代码,先做一个粗略的认知

@Order(-2147483608)表示这是一个比较高优先级的bean,他要先于其他的bean被初始化。这也于是这他的用途,根据配置初始化合适的bean。


@Order(-2147483608)
class OnPropertyCondition extends SpringBootCondition {
    OnPropertyCondition() {
    }

    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
。。。
    }

    private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(MultiValueMap<String, Object> multiValueMap) {
。。。
        return annotationAttributes;
    }

    private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
。。。
    }

    private static class Spec {
        private final String prefix;
        private final String havingValue;
        private final String[] names;
        private final boolean matchIfMissing;

        Spec(AnnotationAttributes annotationAttributes) {
。。。
        }

        private String[] getNames(Map<String, Object> annotationAttributes) {
。。。
        }

        private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
。。。
        }

        private boolean isMatch(String value, String requiredValue) {
。。。
        }

        public String toString() {
。。。
            return result.toString();
        }
    }
}

先根据方法名猜测一下,这些方法大致是:

1、public getMatchOutcome获取匹配的结果
2、private annotationAttributesFromMultiValueMap根据多个值,映射注解属性
3、private determineOutcome决定匹配的结果
4、private getNames获取名称
5、private collectProperties收集属性
6、private isMatch判断是否匹配
7、public toString打印字符串

由此带着猜想我们在这些方法上打断点,首先进入方法1,参数里metadata就是我们加上@ConditionalOnProperty注解的类或方法的元数据信息。

按下F6,我们发现他进入了私有方法2:annotationAttributesFromMultiValueMap,如下图所示。我们发现这里的multiValueMap其实是一系列固定的键值对,这段代码的作用就是将MulitValueMap参数转换成AnnotationAttributes的list对象

最后的产出是一个annotationAttribute对象 ,可以看到这里的key value集合跟最开始我们配置注解的地方是吻合的。

 

下一步,当我们拿到了这个list,跳转至私有方法3:determineOutcome,

注意这里的第一个参数 annotationAttribute不必解释,就是那一串key value值,后面的context.getEnvironment是获取当前环境的配置信息,如下图所示:,可以看到都是我们熟悉的配置信息,其中红框中的信息便是springboot的application.properties配置文件。由此我们可以断定,这个方法就是根据配置文件与映射对象去探明,当前的这个condition条件是否匹配,是否成立。

继续按F6,我们发现他去构造了一个内部类

这个内部类里的属性就是我们注解中配置的内容,当生成这个内部类对象后,执行了方法4:getNames方法。也就是获取所有的属性名称,如下图所示:

组装完对象后,我们发现它继续执行方法5:collectProperties,注意这里的第一个参数resolver,他其实是当前系统的配置文件数据。而且这个方法是Spec(内部类)的方法,结合参数和方法名也基本猜到了这个方法的作用,那就是根据map去配置文件收集对应的属性,如果存在miss的配置或者不match的属性,那么missingProperties和nonMatchingProperties将不会为空,此时将执行下面的判断步骤

下面我们来看下collectProperties方法都执行了哪些步骤,如下图所示,我们看到了方法6:isMatch,我们发现这里的value是“dev”,requireValue是“product”,而这两个参数分别从配置文件和spec对象中获取。也印证了isMatch方法的意思,就是期望属性和配置文件中的属性是否对应。

如果不对应那么nonMatching.add将会被执行。

最后 determineOutcome返回ConditionOutcome对象,注意这里的三个操作

1、如果!missingProperties是空的,也就是说没有找到属性的情况下,执行noMatch方法

2、否则,当找到属性但是值没有匹配上的时候,执行noMatch方法

3、否则,执行match方法

        if (!missingProperties.isEmpty()) {
            return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, new Object[]{spec}).didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
        } else {
            return !nonMatchingProperties.isEmpty() ? ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, new Object[]{spec}).found("different value in property", "different value in properties").items(Style.QUOTE, nonMatchingProperties)) : ConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnProperty.class, new Object[]{spec}).because("matched"));
        }

其实match方法的逻辑很简单:主要是返回一个true状态位,一个ConditionMessage.forCondition(ConditionalOnProperty.class, new Object[]{spec}).because("matched")参数。这个参数返回的是ConditionMessage,也就是一段话,可以不用理会。

    public static ConditionOutcome match(ConditionMessage message) {
        return new ConditionOutcome(true, message);
    }

最终如果匹配,返回的这个true将会为类或者方法的返回值构建相应的bean实例。

  • 11
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值