给网友们整理相关的编程文章,网友彭智勇根据主题投稿了本篇教程内容,涉及到Spring @Conditional注解、Spring @Conditional、Spring @Conditional注解相关内容,已被533网友关注,下面的电子资料对本篇知识点有更加详尽的解释。
Spring @Conditional注解
本文通过阅读@Conditional注解、Condition接口、ConditionEvaluator类以及@ConditionalOnProperty(Spring Boot提供)注解源码,深入分析Spring Conditional的实现原理。
源码版本
由于不同版本的spring代码实现细节可能存在差异,此处记录一下本文使用的源码版本:
- spring 5.2.12.RELEASE
- spring-boot 2.3.8.RELEASE
@Conditional注解
标注在类或方法上,当所有指定Condition都匹配时,才允许向spring容器注册组件。
如果一个@Configuration类标注了@Conditional注解,则与该类关联的所有@Bean方法、@Import注解和@ComponentScan注解都将受指定Condition约束。
注解定义:
@Target({ElementType.TYPE, ElementType.METHOD}) public @interface Conditional { /** * 当所有指定Condition都匹配时,才允许向spring容器注册组件。 * * 这个泛型的意思是接受所有Condition接口的实现类。 */ Class<? extends Condition>[] value(); }
Condition接口
Condition接口
匹配条件。
在注册BeanDefinition之前立即检查条件,并且可以根据当时可以确定的任何条件取消组件注册。
Condition必须遵循与BeanFactoryPostProcessor相同的限制,并注意不要与bean实例交互。如果需要对与@Configuration bean交互的Condition进行更细粒度的控制,请考虑实现ConfigurationCondition接口。
接口定义:
public interface Condition { /** * 确定条件是否匹配。 */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
ConditionContext接口
用于获取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); }
ConditionEvaluator类
用于处理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<Condition> 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步:
- 判断目标组件是否被Conditional标注
- 获取到所有的Condition并实例化
- Condition排序
- Condition匹配验证
下文将展开说明这4个步骤。
判断目标组件是否被Conditional标注
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()方法来实现的,此处不展开说明,后续会有专门的章节介绍。
获取到所有的Condition并实例化
// 2. 获取到所有的Condition并实例化 List<Condition> 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<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) { 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()); }
AnnotatedTypeMetadata接口getAllAnnotationAttributes方法,这是一个default方法,用于获取指定注解的Attribute集:
default MultiValueMap<String, Object> 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 <A extends Annotation> Stream<MergedAnnotation<A>> stream(String annotationType) { if (this.annotationFilter == AnnotationFilter.ALL) { return Stream.empty(); } // 这里使用java api创建Stream return StreamSupport.stream(spliterator(annotationType), false); } private <A extends Annotation> Spliterator<MergedAnnotation<A>> spliterator(Object annotationType) { return new AggregatesSpliterator<>(annotationType, getAggregates()); } private List<Aggregate> getAggregates() { List<Aggregate> 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 <C, R> R scan(C criteria, AnnotationsProcessor<C, R> 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以便获取到所有的注解信息,代码较多,此处不展开记录。
Condition排序
- 判断Condition类实现了PriorityOrdered接口,并使用PriorityOrdered进行排序
- 判断Condition类实现了Ordered接口,并使用Ordered进行排序
- 从Condition类获取Order注解,并使用Order注解的值进行排序
AnnotationAwareOrderComparator.sort(conditions); public static void sort(List<?> list) { if (list.size() > 1) { list.sort(INSTANCE); // 这里的Comparator使用的是AnnotationAwareOrderComparator实现类 } }
AnnotationAwareOrderComparator实现类:
public class AnnotationAwareOrderComparator extends OrderComparator { @Override protected 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; } @Override public 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<Object> { @Override public int compare(Object o1, Object o2) { return doCompare(o1, o2, null); } private int doCompare(Object o1, Object o2, OrderSourceProvider sourceProvider) { // 优先级排序判断 boolean p1 = (o1 instanceof PriorityOrdered); boolean p2 = (o2 instanceof PriorityOrdered); if (p1 && !p2) { return -1; } else if (p2 && !p1) { return 1; } // 分别获取到order并比较 int i1 = getOrder(o1, sourceProvider); int i2 = getOrder(o2, sourceProvider); return Integer.compare(i1, i2); } private int getOrder(Object obj, OrderSourceProvider sourceProvider) { Integer order = null; if (obj != null && sourceProvider != null) { Object orderSource = sourceProvider.getOrderSource(obj); if (orderSource != null) { if (orderSource.getClass().isArray()) { for (Object source : ObjectUtils.toObjectArray(orderSource)) { order = findOrder(source); if (order != null) { break; } } } else { order = findOrder(orderSource); } } } // 直接执行这里 return (order != null ? order : getOrder(obj)); } protected int getOrder(Object obj) { if (obj != null) { // 继续调用findOrder获取排序 // 在此处场景下调用AnnotationAwareOrderComparator的findOrder方法 Integer order = findOrder(obj); if (order != null) { return order; } } return Ordered.LOWEST_PRECEDENCE; } protected Integer findOrder(Object obj) { return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null); } public Integer getPriority(Object obj) { return null; } }
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; } }
@ConditionalOnProperty注解和OnPropertyCondition类
@ConditionalOnProperty注解
@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参数是否配置了指定的值,只有满足时才允许装配目标组件。
如何判断被@Conditional注解标注
前面已经介绍,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<Class<?>> exclude = new HashSet<Class<?>>() {{ add(Target.class); add(Retention.class); add(Documented.class); }}; // 存放所有注解 List<Annotation> annotationList = new ArrayList<>(); // 首先获取一次组件类直接标注的注解 Annotation[] annotations = ServiceAopConfig.class.getDeclaredAnnotations(); // 递归解析 while (annotations.length > 0) { annotationList.addAll(Arrays.asList(annotations)); // 递归解析 List<Annotation> 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); } }
AnnotatedTypeMetadata接口和StandardAnnotationMetadata类
上文介绍了,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 <C, R> R scan(C criteria, AnnotationsProcessor<C, R> 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 <C, R> R scan(C context, AnnotatedElement source, SearchStrategy searchStrategy, AnnotationsProcessor<C, R> processor) { R result = process(context, source, searchStrategy, processor); return processor.finish(result); } // 层层调用到processElement方法 private static <C, R> R processElement(C context, AnnotatedElement source, AnnotationsProcessor<C, R> 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<AnnotationTypeMapping> 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<AnnotationTypeMapping> 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(); }
OnPropertyCondition类
class OnPropertyCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // 获取所有的@ConditionalOnProperty注解属性 List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap( metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName())); List<ConditionMessage> noMatch = new ArrayList<>(); List<ConditionMessage> 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<String> missingProperties = new ArrayList<>(); List<String> 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 { @Override public 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<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); } } } }
到此这篇关于Spring @Conditional注解从源码层讲解的文章就介绍到这了,更多相关Spring @Conditional注解内容请搜索码农之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持码农之家!