【源码】Spring —— Condition 条件匹配解读

tech2022-08-14  148

【源码】Spring —— Condition 条件匹配解读

前言版本ConditionEvaluatorConditionContextImplshouldSkipConfigurationPhase 总结

前言

在注册 BeanDefinition 之前,会根据其 @Conditional 注解的条件进行过滤,@Conditional 是个 Spring内部 和 用户自定义 bean 之间依赖关系很实用的一个注解,该章节解读 Condition条件匹配 的相关类

版本

Spring 5.2.x

ConditionEvaluator

仅限 Spring内部 使用的类,判断目标 BeanDefinition 是否应该跳过注册

目标 BeanDefinition 的 上下文环境 由其内部类 ConditionContextImpl 维护

ConditionContextImpl

private static class ConditionContextImpl implements ConditionContext { @Nullable private final BeanDefinitionRegistry registry; @Nullable private final ConfigurableListableBeanFactory beanFactory; private final Environment environment; private final ResourceLoader resourceLoader; @Nullable private final ClassLoader classLoader; // 略 }

维护了 BeanDefinitionRegistry、ConfigurableListableBeanFactory 等相关 上下文属性

shouldSkip

public boolean shouldSkip(AnnotatedTypeMetadata metadata) { return shouldSkip(metadata, null); } public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { // 必须被 Conditional 注解标注 if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } // 分析给定 metadata 的 ConfigurationPhase if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); // 此处 getConditionClasses 方法返回的是所有 Conditional 注解的 value 属性 for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 排序 AnnotationAwareOrderComparator.sort(conditions); // 调用所有 Condition 的 match 方法进行匹配 // 有一个不匹配则返回 true,即 shouldSkip(应该跳过注册目标 BeanDefinition) for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // 过滤对应 ConfigurationPhase 的 Condition if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; } ------------------- getConditionClasses ------------------- @SuppressWarnings("unchecked") private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) { /** * getAllAnnotationAttributes 方法会追溯所有的注解 * 换句话说,即便定义在其注解下(例如 ConditionalOnXXX) * 也是可以追溯到并合并其属性的 */ MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(Conditional.class.getName(), true); Object values = (attributes != null ? attributes.get("value") : null); return (List<String[]>) (values != null ? values : Collections.emptyList()); } ---------------------- getCondition ---------------------- // 返回指定的 Condition 实例 private Condition getCondition(String conditionClassName, @Nullable ClassLoader classloader) { Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName, classloader); return (Condition) BeanUtils.instantiateClass(conditionClass); }

具体的细节给出了详细的注释,这里总结一下大体流程

整个方法可以理解为一个简单的 递归:

方法会在第一次 shouldSkip 判断目标 BeanDefinition 的 ConfigurationPhase 类型

再次进入 shouldSkip 方法,获取对应 @Conditional 注解上 value 属性的值,解析并获取对应的 Condition 实例,再以 ConfigurationPhase 过滤后,执行 match 方法检测条件是否匹配

但凡有一个 Condition 不匹配,则返回 true,标识该 BeanDefinition 跳过注册

ConfigurationPhase

public interface ConfigurationCondition extends Condition { ConfigurationPhase getConfigurationPhase(); enum ConfigurationPhase { PARSE_CONFIGURATION, REGISTER_BEAN } } ---------------------Condition-------------------------- @FunctionalInterface public interface Condition { boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }

ConfigurationPhase 是 Condition 的子接口 ConfigurationCondition 的一个内部 枚举类,定义了两种 Condition

PARSE_CONFIGURATION:表明该类 Condition 在解析 @Configuration 时检测匹配REGISTER_BEAN:表明该类 Condition 在注册 普通Bean 时检测匹配

总结

匹配 @Conditional 注解条件的 BeanDefinition 才有机会被注册,Spring内部 大量的使用了该注解来维护 bean 之间的依赖关系,我们平时的开发工作中也避免不了频繁的使用

最新回复(0)