springboot源码:@Profile与@Conditional相关注解解读

1.@Profile注解
  主要的实现逻辑是通过@Conditional实现,@Profile是一种粗粒度的条件控制并只针对于环境条件配置,匹配的方法在ProfileCondition.java中,实现原理下面讲解.
2.@Conditional注解
  使用场景:用于满足一定条件之后对指定的类进行加载到容器;比如说:对于是否启用日志打印功能,是否对指定类型请求进行过滤等等细粒度的控制;实现的方式同@Profile,具体步骤如下(以@Profile为例说明):
  2.1 自定义注解(@Profile主要作用在于指定环境加载指定的对象到容器中)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
	/**
	 *  该注解支持的属性数组中指定环境值,根据配置的环境标识进行注册到容器中(比如说value=dev一般表示从开发环境中需要进行注册)
	 */
	String[] value();
}

  2.2 自定义条件类,设置匹配逻辑(ProfileCondition.java 需要实现Condition的matches实现匹配逻辑)

class ProfileCondition implements Condition {
// 从已启动容器中的环境信息(比如说test、dev、pro、default)匹配标注@Profile注解的属性值是否一致,如果一致则加载到容器中,否则不加载到容器中
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
	// 获取Profile注解中的所有属性值,以key-value的方式存储到attrs中
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			for (Object value : attrs.get("value")) {
			// 判断当前容器是否与注解Profiles中的属性值匹配
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}
}

  2.3 使用注解(开发环境中此对象才会进行注入到容器中)

@Component
@Profile("dev")
public class ABean {

    private int age;

    private String name;
    }

3.@Conditional的衍生注解:
  @ConditionalOnProperty:如果有指定的配置,条件生效;
  @ConditionalOnBean:如果有指定的Bean,条件生效;
  @ConditionalOnMissingBean:如果没有指定的Bean,条件生效;
  @ConditionalOnMissingClass:如果没有指定的Class,条件生效;
  @ConditionalOnWebApplication:在Web环境中条件生效;
  @ConditionalOnExpression:根据表达式判断条件是否生效。
  以springboot中最常用的@ConditionalOnProperty为例讲解:
  场景:根据配置文件中spring.personal.object的配置true或是false决定PersonalBean是否加载到容器中.
PersonalBean.java

@Component
@ConditionalOnProperty(value = "spring.personal.object",matchIfMissing = true)
// matchIfMissing = true 表示如果条件属性值没有配置,默认true进行匹配
public class PersonalBean {
    public PersonalBean() {
        System.out.println("自定义bean创建");
    }
}

配置文件中内容:

spring:
  # 指定条件设置加载自定义bean
  personal:
    object: true

配置文件中配置true时控制台打印信息:
在这里插入图片描述
@ConditionalOnProperty注解:

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

关键看一下OnPropertyCondition.java实现逻辑:

@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {
	// 获取匹配结果对象
	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
	// 将@ConditionalOnProperty属性进行数据转换,转化为AnnotationAttributes集合,转化后的集合:  
		List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
	metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
		List<ConditionMessage> noMatch = new ArrayList<>();
		List<ConditionMessage> match = new ArrayList<>();
		for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
			// 获取匹配结果,主要是用于上层包扫描是否跳过添加到candidate(待加载bean定义对象集合)中
			ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
			(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
		}
		if (!noMatch.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
		}
		return ConditionOutcome.match(ConditionMessage.of(match));
	}
	// 省略部分代码..............
// 获取最终的匹配结果,ConditionOutcome属性:match==true,message=@ConditionalOnProperty (spring.personal.object) 
	private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
		Spec spec = new Spec(annotationAttributes);
		List<String> missingProperties = new ArrayList<>();
		List<String> nonMatchingProperties = new ArrayList<>();
		spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
		if (!missingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
		}
		if (!nonMatchingProperties.isEmpty()) {
			return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
					.found("different value in property", "different value in properties")
					.items(Style.QUOTE, nonMatchingProperties));
		}
		return ConditionOutcome
				.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
	}
   // 静态内部类实例化
	private static class Spec {

		private final String prefix;

		private final String havingValue;

		private final String[] names;

		private final boolean matchIfMissing;

		Spec(AnnotationAttributes annotationAttributes) {
			String prefix = annotationAttributes.getString("prefix").trim();
			if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
				prefix = prefix + ".";
			}
			this.prefix = prefix;
			this.havingValue = annotationAttributes.getString("havingValue");
			// 获取@ConditionalOnProperty中name或是value的属性值,如果value不为空返回value的属性值,如果value为空返回name的属性值
			this.names = getNames(annotationAttributes);
			this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
		}
	// 获取@ConditionalOnProperty中name或是value的属性值,如果value不为空返回value的属性值,如果value为空返回name的属性值
		private String[] getNames(Map<String, Object> annotationAttributes) {
			String[] value = (String[]) annotationAttributes.get("value");
			String[] name = (String[]) annotationAttributes.get("name");
			Assert.state(value.length > 0 || name.length > 0,
					"The name or value attribute of @ConditionalOnProperty must be specified");
			Assert.state(value.length == 0 || name.length == 0,
					"The name and value attributes of @ConditionalOnProperty are exclusive");
			return (value.length > 0) ? value : name;
		}
		// 根据@ConditionalOnProperty中name属性值或是values属性值读取
		private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
			for (String name : this.names) {
				String key = this.prefix + name;
				if (resolver.containsProperty(key)) {
					if (!isMatch(resolver.getProperty(key), this.havingValue)) {
						nonMatching.add(name);
					}
				}
				else {
					if (!this.matchIfMissing) {
						missing.add(name);
					}
				}
			}
		}

		// 省略部分代码..............................

	}

}

@ConditionalOnProperty涉及调用流程:
在这里插入图片描述
涉及类:
ClassPathBeanDefinitionScanner.java
ConditionEvaluator.java
OnPropertyCondition.java

你可能感兴趣的:(源码)