@ConditionalOnProperty的使用与原理

    xiaoxiao2023-11-19  154

    一、宏观理解

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

    点击这个注解,我们可以看出这是一个基于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实例。

    最新回复(0)