Spring Boot源码解析(一)- @ConditionalOnProperty详解

1.前言

最近在学习Spring Boot的运作原理发现“自动配置”的功能都借助于@Conditional注解来完成,@Conditional根据满足某一特定条件创建一个特定的Bean。比如,当某一个类在一个路径下的时候,自动配置一个或多个Bean。即根据特定条件来控制Bean的创建行为。本文只讲解其中一个核心注解@ConditionalOnProperty

2.@ConditionalOnProperty说明

package org.springframework.boot.autoconfigure.condition;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Conditional;
import org.springframework.core.env.Environment;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {

	/**
	 * name的别名。
     *
	 * @return the names
	 */
	String[] value() default {};

	/**
	 * 属性前缀。
     *
	 * @return the prefix
	 */
	String prefix() default "";

	/**
	 * 属性名(字符串数组)。
     *   
	 * @return the names
	 */
	String[] name() default {};

	/**
	 * 预期值。
     *
	 * @return the expected value
	 */
	String havingValue() default "";

	/**
	 * 如果属性名不存在,条件是否成立。
     *
	 * @return if should match if the property is missing
	 */
	boolean matchIfMissing() default false;

	/**
	 * 属性名是否松散匹配。
     *
	 * @return if relaxed names are used
	 */
	boolean relaxedNames() default true;

}

3.使用方法

  1. name和value不能都为空,也不能都有值;
  2. 如果配置了prefix,在匹配时的属性名为prefix+name;
  3. 如果配置文件(eg: application.yml)中不包含属性名name,且matchIfMissing为false,返回false;
  4. 如果配置文件(eg: application.yml)中不包含属性名name,且matchIfMissing为true,返回true;
  5. 如果配置文件(eg: application.yml)中包含属性名name,且配置了havingValue,比较属性名name对应的属性值和havingValue是否相等,相等返回true,否则返回false;
  6. 如果配置文件(eg: application.yml)中包含属性名name,且没有配置havingValue,比较属性名name对应的属性值是否等于字符串false,相等返回false,否则返回true;
  7. 循环以上判断,有一项为false,则@Conditional不成立。

 

4.OnPropertyCondition条件说明

package org.springframework.boot.autoconfigure.condition;

import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.PropertyResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * {@link Condition} that checks if properties are defined in environment.
 *
 * @author Maciej Walkowiak
 * @author Phillip Webb
 * @author Stephane Nicoll
 * @author Andy Wilkinson
 * @see ConditionalOnProperty
 * @since 1.1.0
 */
@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {

    /**
     * 获取比对结果。
     *
     * 

ConditionOutcome待翻译,看源码可以理解为成功或失败的结果。TODO *

ConditionMessage待翻译,看源码可以理解为ConditionOutcome含有的一种描述。TODO * * @param context * @param metadata * @return */ @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()) // 获取@ConditionalOnProperty注解的所有属性值,属性值是MultiValueMap类型 List allAnnotationAttributes = annotationAttributesFromMultiValueMap( metadata.getAllAnnotationAttributes( ConditionalOnProperty.class.getName())); // 初始化【比对不成功】列表 List noMatch = new ArrayList(); // 初始化【比对成功】列表 List match = new ArrayList(); // 循环计算每一个属性的比对结果 for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) { // 计算比对结果 ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment()); // 判断匹配结果是否成功,如果成功将结果的ConditionMessage加入【比对成功】列表,否则加入【比对不成功】列表 (outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage()); } // 如果【比对不成功】列表不为空,返回“匹配失败”的ConditionOutcome,条件不成立 if (!noMatch.isEmpty()) { return ConditionOutcome.noMatch(ConditionMessage.of(noMatch)); } // 返回“匹配成功”的ConditionOutcome,条件成立 return ConditionOutcome.match(ConditionMessage.of(match)); } /** * 将MultiValueMap中的属性数据转成AnnotationAttributes类型。 * *

AnnotationAttributes待解析 TODO。 * * @param multiValueMap @ConditionalOnProperty注解的所有属性值 * @return */ private List annotationAttributesFromMultiValueMap( MultiValueMap multiValueMap) { List> maps = new ArrayList>(); for (Entry> entry : multiValueMap.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { Map map; if (i < maps.size()) { map = maps.get(i); } else { map = new HashMap(); maps.add(map); } map.put(entry.getKey(), entry.getValue().get(i)); } } List annotationAttributes = new ArrayList( maps.size()); for (Map map : maps) { annotationAttributes.add(AnnotationAttributes.fromMap(map)); } return annotationAttributes; } /** * 计算比对结果。 * * @param annotationAttributes 转换后的注解属性值 * @param resolver * @return */ private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) { // 用属性值实例化一个内部类Spec Spec spec = new Spec(annotationAttributes); // 初始化【未配置属性】列表 List missingProperties = new ArrayList(); // 初始化【匹配失败属性】列表 List nonMatchingProperties = new ArrayList(); // 匹配属性(传入missingProperties和nonMatchingProperties) spec.collectProperties(resolver, missingProperties, nonMatchingProperties); // 如果【未配置属性】列表不为空,返回实例化匹配失败的ConditionOutcome if (!missingProperties.isEmpty()) { return ConditionOutcome.noMatch( ConditionMessage.forCondition(ConditionalOnProperty.class, spec) .didNotFind("property", "properties") .items(Style.QUOTE, missingProperties)); } // 如果【匹配失败属性】列表不为空,返回实例化匹配失败的ConditionOutcome if (!nonMatchingProperties.isEmpty()) { return ConditionOutcome.noMatch( ConditionMessage.forCondition(ConditionalOnProperty.class, spec) .found("different value in property", "different value in properties") .items(Style.QUOTE, nonMatchingProperties)); } // 返回实例化匹配成功的ConditionOutcome return ConditionOutcome.match(ConditionMessage .forCondition(ConditionalOnProperty.class, spec).because("matched")); } /** * 私有静态内部类。 */ private static class Spec { /** * 每个要匹配的属性前缀。如果没有指定前缀,前缀将自动以"."结尾。 * *

对应{@link ConditionalOnProperty#prefix()}。 */ private final String prefix; /** * 属性的预期值。如果没有指定预期值,属性不能等于false字符串。 * *

如果names中配置了多个属性要匹配,那么所有的name对应的预期值都是该值。 * *

对应{@link ConditionalOnProperty#havingValue()}。 */ private final String havingValue; /** * 要匹配的属性的名称。如果定义了前缀prefix,那么将使用prefix+name的形式进行匹配。 * *

names是字符串数组,可以设置多个匹配的条件。 * *

对应{@link ConditionalOnProperty#name()} */ private final String[] names; /** * names是否松散的,默认为true。 * *

对应{@link ConditionalOnProperty#relaxedNames()} */ private final boolean relaxedNames; /** * 如果没有设置要匹配的属性,那么指定条件是否应该匹配成功,默认为false。 * *

对应{@link ConditionalOnProperty#matchIfMissing()} */ private final boolean matchIfMissing; /** * 构造函数。 * * @param annotationAttributes */ Spec(AnnotationAttributes annotationAttributes) { // 前缀作trim处理。 String prefix = annotationAttributes.getString("prefix").trim(); // 如果前缀有值,且不是以"."结尾,在结尾加"." if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) { prefix = prefix + "."; } // 初始化前缀 this.prefix = prefix; // 初始化预期值 this.havingValue = annotationAttributes.getString("havingValue"); // 初始化属性名数组 this.names = getNames(annotationAttributes); // 初始化松散names this.relaxedNames = annotationAttributes.getBoolean("relaxedNames"); // 初始化如果没有配置,条件是否成功 this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing"); } /** * 初始化属性名数组。 * * @param annotationAttributes * @return */ private String[] getNames(Map annotationAttributes) { // 获取value数组(name数组的别名) String[] value = (String[]) annotationAttributes.get("value"); // 获取name数组 String[] name = (String[]) annotationAttributes.get("name"); // name和value必须指定,不能同时为空。 Assert.state(value.length > 0 || name.length > 0, "The name or value attribute of @ConditionalOnProperty must be specified"); // name和value是互斥的,不能同时有值。 Assert.state(value.length == 0 || name.length == 0, "The name and value attributes of @ConditionalOnProperty are exclusive"); // value和name,哪个有值就返回哪个 return (value.length > 0) ? value : name; } /** * 判断属性是否匹配 * * @param resolver * @param missing 【未配置属性】列表 * @param nonMatching 【匹配失败属性】列表 */ private void collectProperties(PropertyResolver resolver, List missing, List nonMatching) { // 如果是松散names if (this.relaxedNames) { // 实例化一个属性处理器,并传入前缀 resolver = new RelaxedPropertyResolver(resolver, this.prefix); } // 循环配置属性是否匹配 for (String name : this.names) { String key = (this.relaxedNames ? name : this.prefix + name); if (resolver.containsProperty(key)) { // 如果处理器(理解为配置文件application.yml)中包含这个属性名,判断属性名对应的值是否和预期值匹配 if (!isMatch(resolver.getProperty(key), this.havingValue)) { // 如果不匹配,将属性名加入【匹配失败属性】列表 nonMatching.add(name); } } else { // 如果处理器中(理解为配置文件application.yml)中未包含这个属性,且matchIfMissing为false,将属性名加入【未配置属性】列表 if (!this.matchIfMissing) { missing.add(name); } } } } /** * 判断属性对应的值是否和预期值匹配。 * * @param value 属性值 * @param requiredValue 预期值 * @return 是否匹配成功 true-成功 false-失败 */ private boolean isMatch(String value, String requiredValue) { // 如果预期值不为空,忽略大小写判断两者是否相等 if (StringUtils.hasLength(requiredValue)) { return requiredValue.equalsIgnoreCase(value); } // 预期值为空,忽略大小写判断属性值是否等于false return !"false".equalsIgnoreCase(value); } @Override public String toString() { StringBuilder result = new StringBuilder(); result.append("("); result.append(this.prefix); if (this.names.length == 1) { result.append(this.names[0]); } else { result.append("["); result.append(StringUtils.arrayToCommaDelimitedString(this.names)); result.append("]"); } if (StringUtils.hasLength(this.havingValue)) { result.append("=").append(this.havingValue); } result.append(")"); return result.toString(); } } }

 

你可能感兴趣的:(Spring,Boot)