本文通过阅读@Conditional注解、Condition接口、ConditionEvaluator类以及@ConditionalOnProperty(Spring Boot提供)注解源码,深入分析Spring Conditional的实现原理。
由于不同版本的spring代码实现细节可能存在差异,此处记录一下本文使用的源码版本:
标注在类或方法上,当所有指定Condition都匹配时,才允许向spring容器注册组件。
如果一个@Configuration类标注了@Conditional注解,则与该类关联的所有@Bean方法、@Import注解和@ComponentScan注解都将受指定Condition约束。
注解定义:
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {/*** 当所有指定Condition都匹配时,才允许向spring容器注册组件。** 这个泛型的意思是接受所有Condition接口的实现类。*/Class extends Condition>[] value();
}
匹配条件。
在注册BeanDefinition之前立即检查条件,并且可以根据当时可以确定的任何条件取消组件注册。
Condition必须遵循与BeanFactoryPostProcessor相同的限制,并注意不要与bean实例交互。如果需要对与@Configuration bean交互的Condition进行更细粒度的控制,请考虑实现ConfigurationCondition接口。
接口定义:
public interface Condition {/*** 确定条件是否匹配。*/boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
用于获取BeanDefinitionRegistry、BeanFactory、Environment等。
只有一个ConditionEvaluator.ConditionContextImpl实现类(一个内部类):
class ConditionEvaluator {private final ConditionContextImpl context;public ConditionEvaluator(BeanDefinitionRegistry registry,Environment environment, ResourceLoader resourceLoader) {this.context = new ConditionContextImpl(registry, environment, resourceLoader);}
用于处理Conditional相关注解。
核心的逻辑都在这个类里面:
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {// 1. 判断目标组件是否被Conditional标注if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {return false;}if (phase == null) {if (metadata instanceof AnnotationMetadata &&ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);}return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);}// 2. 获取到所有的Condition并实例化List conditions = new ArrayList<>();for (String[] conditionClasses : getConditionClasses(metadata)) {for (String conditionClass : conditionClasses) {Condition condition = getCondition(conditionClass, this.context.getClassLoader());conditions.add(condition);}}// 3. Condition排序AnnotationAwareOrderComparator.sort(conditions);// 4. Condition匹配验证for (Condition condition : conditions) {ConfigurationPhase requiredPhase = null;if (condition instanceof ConfigurationCondition) {requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();}if ((requiredPhase == null || requiredPhase == phase) &&!condition.matches(this.context, metadata)) {return true;}}return false;
}
核心功能4步:
下文将展开说明这4个步骤。
AnnotatedTypeMetadata中封装了目标组件的注解元信息,可以通过他获取到目标组件的注解相关信息,比如是否被某个注解标注、某个注解的属性等。
metadata.isAnnotated(Conditional.class.getName())
AnnotatedTypeMetadata中定义了该方法的实现方式:
default boolean isAnnotated(String annotationName) {// 通过MergedAnnotations来获取注解标注状态return getAnnotations().isPresent(annotationName);
}
我们在之前的《Spring-@Bean注解源码分析》中介绍过,此处使用的是StandardAnnotationMetadata实现类,而该类中的MergedAnnotations是使用以下方式创建和获取的:
public StandardAnnotationMetadata(Class> introspectedClass, boolean nestedAnnotationsAsMap) {super(introspectedClass);// 这里使用的是TypeMappedAnnotations实现类this.mergedAnnotations = MergedAnnotations.from(introspectedClass,SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}@Override
public MergedAnnotations getAnnotations() {return this.mergedAnnotations;
}
所以isAnnotated的核心判断逻辑在TypeMappedAnnotations的isPresent方法中:
public boolean isPresent(String annotationType) {if (this.annotationFilter.matches(annotationType)) {return false;}return Boolean.TRUE.equals(scan(annotationType,IsPresent.get(this.repeatableContainers, this.annotationFilter, false)));
}
scan方法的详细逻辑还是使用java反射的Class.getDeclaredAnnotations()方法来实现的,此处不展开说明,后续会有专门的章节介绍。
// 2. 获取到所有的Condition并实例化
List conditions = new ArrayList<>();
// 获取Condition集
for (String[] conditionClasses : getConditionClasses(metadata)) {for (String conditionClass : conditionClasses) {// 创建Condition实例Condition condition = getCondition(conditionClass, this.context.getClassLoader());conditions.add(condition);}
}// 这个方法从AnnotatedTypeMetadata中获取所有Conditional注解指定的Condition类名集
private List getConditionClasses(AnnotatedTypeMetadata metadata) {MultiValueMap attributes =metadata.getAllAnnotationAttributes(Conditional.class.getName(), true);Object values = (attributes != null ? attributes.get("value") : null);return (List) (values != null ? values : Collections.emptyList());
}
AnnotatedTypeMetadata接口getAllAnnotationAttributes方法,这是一个default方法,用于获取指定注解的Attribute集:
default MultiValueMap getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {Adapt[] adaptations = Adapt.values(classValuesAsString, true);return getAnnotations().stream(annotationName) // 获取到所有的注解信息.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)) // 过滤.map(MergedAnnotation::withNonMergedAttributes).collect(MergedAnnotationCollectors.toMultiValueMap(map ->map.isEmpty() ? null : map, adaptations));
}
getAnnotations()返回的是TypeMappedAnnotations类型对象,他的stream方法:
public Stream> stream(String annotationType) {if (this.annotationFilter == AnnotationFilter.ALL) {return Stream.empty();}// 这里使用java api创建Streamreturn StreamSupport.stream(spliterator(annotationType), false);
}private Spliterator> spliterator(Object annotationType) {return new AggregatesSpliterator<>(annotationType, getAggregates());
}private List getAggregates() {List aggregates = this.aggregates;if (aggregates == null) {// 这里扫描所有注解aggregates = scan(this, new AggregatesCollector());if (aggregates == null || aggregates.isEmpty()) {aggregates = Collections.emptyList();}this.aggregates = aggregates;}return aggregates;
}// 这里扫描所有注解
private R scan(C criteria, AnnotationsProcessor processor) {if (this.annotations != null) {R result = processor.doWithAnnotations(criteria, 0, this.source, this.annotations);return processor.finish(result);}if (this.element != null && this.searchStrategy != null) {return AnnotationsScanner.scan(criteria, this.element, this.searchStrategy, processor);}return null;
}
scan方法里面会通过递归方式使用java反射api从组件类getAnnotations以便获取到所有的注解信息,代码较多,此处不展开记录。
AnnotationAwareOrderComparator.sort(conditions);public static void sort(List> list) {if (list.size() > 1) {list.sort(INSTANCE); // 这里的Comparator使用的是AnnotationAwareOrderComparator实现类}
}
AnnotationAwareOrderComparator实现类:
public class AnnotationAwareOrderComparator extends OrderComparator {@Overrideprotected Integer findOrder(Object obj) {// 先使用父类方法获取排序,其实就是判断Ordered实现并获取排序,在本例中显然获取不到Integer order = super.findOrder(obj);if (order != null) {return order;}// 从类标注的注解中获取排序return findOrderFromAnnotation(obj);}private Integer findOrderFromAnnotation(Object obj) {AnnotatedElement element = (obj instanceof AnnotatedElement ?(AnnotatedElement) obj : obj.getClass());MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);// 从类标注的注解中获取排序,比如Order注解,此处不展开记录了Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);if (order == null && obj instanceof DecoratingProxy) {return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());}return order;}@Overridepublic Integer getPriority(Object obj) {if (obj instanceof Class) {return OrderUtils.getPriority((Class>) obj);}Integer priority = OrderUtils.getPriority(obj.getClass());if (priority == null && obj instanceof DecoratingProxy) {return getPriority(((DecoratingProxy) obj).getDecoratedClass());}return priority;}
}public class OrderComparator implements Comparator
for (Condition condition : conditions) {ConfigurationPhase requiredPhase = null;if (condition instanceof ConfigurationCondition) {requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();}if ((requiredPhase == null || requiredPhase == phase) &&!condition.matches(this.context, metadata)) {return true;}
}
@Conditional that checks if the specified properties have a specific value. By default the properties must be present in the Environment and not equal to false. The havingValue() and matchIfMissing() attributes allow further customizations.
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {String[] value() default {};String prefix() default "";String[] name() default {};String havingValue() default "";boolean matchIfMissing() default false;
}
OnPropertyCondition从env中检测指定env参数是否配置了指定的值,只有满足时才允许装配目标组件。
前面已经介绍,spring是使用(AnnotatedTypeMetadata)metadata.isAnnotated(Conditional.class.getName())来判断组件被@Conditional注解标注的。但是@Conditional标注在@ConditionalOnProperty注解上,使用普通的(Class)clazz.isAnnotationPresent(Conditional.class)方式无法判断,那么metadata.isAnnotated方法是如何判断的呢?
从上面@ConditionalOnProperty注解定义可以知道,目标组件类标注了@ConditionalOnProperty注解,@ConditionalOnProperty注解又标注了@Conditional注解,这显然是一个递归,只有从目标组件类开始递归解析,就可以解析出目标组件类上的所有注解,以下是一个我自己编写的简单示例:
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("org.net5ijy.mybatis.test.config.aop")
@ConditionalOnAop
public class ServiceAopConfig {}@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(AopCondition.class)
public @interface ConditionalOnAop {}@Test
public void testAllResolveAnnotations() {// 不解析以下三个注解Set> exclude = new HashSet>() {{add(Target.class);add(Retention.class);add(Documented.class);}};// 存放所有注解List annotationList = new ArrayList<>();// 首先获取一次组件类直接标注的注解Annotation[] annotations = ServiceAopConfig.class.getDeclaredAnnotations();// 递归解析while (annotations.length > 0) {annotationList.addAll(Arrays.asList(annotations));// 递归解析List tmp = new ArrayList<>();for (Annotation annotation : annotations) {Annotation[] list = annotation.annotationType().getDeclaredAnnotations();for (Annotation a : list) {if (!exclude.contains(a.annotationType())) {tmp.add(a);}}}annotations = tmp.toArray(new Annotation[0]);}// 循环结束之后,ServiceAopConfig直接和间接标注的所有注解就都解析出来了for (Annotation annotation : annotationList) {System.out.println(annotation);}
}
上文介绍了,spring是使用(AnnotatedTypeMetadata)metadata.isAnnotated(Conditional.class.getName())来判断组件被@Conditional注解标注的。
public interface AnnotatedTypeMetadata {/*** Return annotation details based on the direct annotations of the underlying element.* @return merged annotations based on the direct annotations* @since 5.2*/MergedAnnotations getAnnotations();/*** Determine whether the underlying element has an* annotation or meta-annotation of the given type defined.* @return whether a matching annotation is defined*/default boolean isAnnotated(String annotationName) {return getAnnotations().isPresent(annotationName);}// ...
}
在我们分析的场景,此处使用的是StandardAnnotationMetadata实现类。StandardAnnotationMetadata实现类getAnnotations()返回TypeMappedAnnotations类型对象。
TypeMappedAnnotations类isPresent方法:
public boolean isPresent(String annotationType) {if (this.annotationFilter.matches(annotationType)) {return false;}// 在scan方法中return Boolean.TRUE.equals(scan(annotationType,IsPresent.get(this.repeatableContainers, this.annotationFilter, false)));
}private R scan(C criteria, AnnotationsProcessor processor) {if (this.annotations != null) {R result = processor.doWithAnnotations(criteria, 0, this.source, this.annotations);return processor.finish(result);}if (this.element != null && this.searchStrategy != null) {// 执行到这里return AnnotationsScanner.scan(criteria, this.element, this.searchStrategy, processor);}return null;
}
AnnotationsScanner.scan方法:
static R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy,AnnotationsProcessor processor) {R result = process(context, source, searchStrategy, processor);return processor.finish(result);
}// 层层调用到processElement方法
private static R processElement(C context, AnnotatedElement source,AnnotationsProcessor processor) {try {R result = processor.doWithAggregate(context, 0);// 继续调用processor.doWithAnnotations方法// 这里的processor是TypeMappedAnnotations.IsPresent类型对象return (result != null ? result : processor.doWithAnnotations(context, 0, source, getDeclaredAnnotations(source, false)));} catch (Throwable ex) {AnnotationUtils.handleIntrospectionFailure(source, ex);}return null;
}
TypeMappedAnnotations.IsPresent类doWithAnnotations方法:
public Boolean doWithAnnotations(Object requiredType, int aggregateIndex,Object source, Annotation[] annotations) {for (Annotation annotation : annotations) {if (annotation != null) {Class extends Annotation> type = annotation.annotationType();if (type != null && !this.annotationFilter.matches(type)) {if (type == requiredType || type.getName().equals(requiredType)) {return Boolean.TRUE;}Annotation[] repeatedAnnotations =this.repeatableContainers.findRepeatedAnnotations(annotation);if (repeatedAnnotations != null) {Boolean result = doWithAnnotations(requiredType, aggregateIndex, source, repeatedAnnotations);if (result != null) {return result;}}if (!this.directOnly) {// 这里会进行递归解析AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(type);for (int i = 0; i < mappings.size(); i++) {AnnotationTypeMapping mapping = mappings.get(i);if (isMappingForType(mapping, this.annotationFilter, requiredType)) {return Boolean.TRUE;}}}}}}return null;
}
AnnotationTypeMappings.forAnnotationType方法:
static AnnotationTypeMappings forAnnotationType(Class extends Annotation> annotationType) {return forAnnotationType(annotationType, AnnotationFilter.PLAIN);
}static AnnotationTypeMappings forAnnotationType(Class extends Annotation> annotationType, AnnotationFilter annotationFilter) {return forAnnotationType(annotationType, RepeatableContainers.standardRepeatables(), annotationFilter);
}static AnnotationTypeMappings forAnnotationType(Class extends Annotation> annotationType,RepeatableContainers repeatableContainers, AnnotationFilter annotationFilter) {return new AnnotationTypeMappings(repeatableContainers, annotationFilter, annotationType);
}
AnnotationTypeMappings对象:
private AnnotationTypeMappings(RepeatableContainers repeatableContainers,AnnotationFilter filter, Class extends Annotation> annotationType) {this.repeatableContainers = repeatableContainers;this.filter = filter;this.mappings = new ArrayList<>();addAllMappings(annotationType);this.mappings.forEach(AnnotationTypeMapping::afterAllMappingsSet);
}private void addAllMappings(Class extends Annotation> annotationType) {Deque queue = new ArrayDeque<>();addIfPossible(queue, null, annotationType, null);// 递归解析while (!queue.isEmpty()) {AnnotationTypeMapping mapping = queue.removeFirst();this.mappings.add(mapping);addMetaAnnotationsToQueue(queue, mapping);}
}private void addMetaAnnotationsToQueue(Deque queue, AnnotationTypeMapping source) {Annotation[] metaAnnotations = AnnotationsScanner.getDeclaredAnnotations(source.getAnnotationType(), false);// ...
}static Annotation[] getDeclaredAnnotations(AnnotatedElement source, boolean defensive) {boolean cached = false;Annotation[] annotations = declaredAnnotationCache.get(source);if (annotations != null) {cached = true;} else {// 这里使用java反射api获取类标注的注解annotations = source.getDeclaredAnnotations();if (annotations.length != 0) {// ...}}if (!defensive || annotations.length == 0 || !cached) {return annotations;}return annotations.clone();
}
class OnPropertyCondition extends SpringBootCondition {@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取所有的@ConditionalOnProperty注解属性List allAnnotationAttributes = annotationAttributesFromMultiValueMap(metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));List noMatch = new ArrayList<>();List match = new ArrayList<>();// 遍历@ConditionalOnProperty注解属性for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {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));}private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,PropertyResolver resolver) {Spec spec = new Spec(annotationAttributes);List missingProperties = new ArrayList<>();List nonMatchingProperties = new ArrayList<>();// 这里在匹配env参数// Spec类封装了prefix, havingValue, names, matchIfMissing等配置// collectProperties方法将env参数与prefix, havingValue, names, matchIfMissing等进行匹配spec.collectProperties(resolver, missingProperties, nonMatchingProperties);if (!missingProperties.isEmpty()) {return ConditionOutcome.noMatch(...);}if (!nonMatchingProperties.isEmpty()) {return ConditionOutcome.noMatch(...);}return ConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));}
}public abstract class SpringBootCondition implements Condition {@Overridepublic final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {String classOrMethodName = getClassOrMethodName(metadata);try {// 此处需要子类实现ConditionOutcome outcome = getMatchOutcome(context, metadata);logOutcome(classOrMethodName, outcome);recordEvaluation(context, classOrMethodName, outcome);return outcome.isMatch();} catch (NoClassDefFoundError ex) {throw new IllegalStateException("", ex);} catch (RuntimeException ex) {throw new IllegalStateException("", ex);}}public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
}
(Spec)spec.collectProperties方法:
private void collectProperties(PropertyResolver resolver, List missing, List 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);}}}
}