首页 文章资讯内容详情

springboot情操陶冶-@Conditional和@AutoConfigureAfter注解解析

2026-06-01 4 花语

本文内容纲要:

-1.@Conditional -@Conditional注解被解析入口 -2.@AutoConfigureAfter -小结

承接前文springboot情操陶冶-@Configuration注解解析,本文将在前文的基础上阐述@AutoConfigureAfter和@Conditional注解的作用与解析

1.@Conditional

根据单词来理解,其就是条件的意思。在分析之前我们可以看下其内部源码

@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceConditional{ /** *All{@linkCondition}sthatmust{@linkplainCondition#matchesmatch} *inorderforthecomponenttoberegistered. */ Class<?extendsCondition>[]value(); }

其作用于类、方法上,且指定的value值必须是org.springframework.context.annotation.Condition

的实现类,供条件判断。

以此为基础而扩展的注解还有@ConditionalBean、@ConditionalOnWebApplication、@ConditionalOnClass、@ConditionalOnMissingBean等等。

@Conditional注解被解析入口

那么我们肯定想知道,其中的注解是如何被解析的呢。其实在前文中的ConfigurationClassParser类中,在执行真正的*doProcessConfigurationClass()*方法前,会执行如下的代码

protectedvoidprocessConfigurationClass(ConfigurationClassconfigClass)throwsIOException{ //条件判断,满足则直接返回,不进行后续的解析 if(this.conditionEvaluator.shouldSkip(configClass.getMetadata(),ConfigurationPhase.PARSE_CONFIGURATION)){ return; } .... //Recursivelyprocesstheconfigurationclassanditssuperclasshierarchy. SourceClasssourceClass=asSourceClass(configClass); do{ sourceClass=doProcessConfigurationClass(configClass,sourceClass); } while(sourceClass!=null); this.configurationClasses.put(configClass,configClass); }

也就是会执行上述的ConditionEvaluator#shouldSkip()方法,只有条件不满足后才会继续往下执行真正的@Configuration注解解析。

ConditionEvaluator#shouldSkip()

废话不多说,直接上源码

//metadata为被注解的类元素,返回值为true表明条件满足应该被忽略 publicbooleanshouldSkip(@NullableAnnotatedTypeMetadatametadata,@NullableConfigurationPhasephase){ //1.判断类是否含有@Conditional注解,否则直接返回 if(metadata==null||!metadata.isAnnotated(Conditional.class.getName())){ returnfalse; } if(phase==null){ if(metadatainstanceofAnnotationMetadata&& ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata)metadata)){ returnshouldSkip(metadata,ConfigurationPhase.PARSE_CONFIGURATION); } returnshouldSkip(metadata,ConfigurationPhase.REGISTER_BEAN); } //2.获取类上所有含有@Conditional注解的value集合(其会递归找寻注解的注解) List<Condition>conditions=newArrayList<>(); for(String[]conditionClasses:getConditionClasses(metadata)){ for(StringconditionClass:conditionClasses){ Conditioncondition=getCondition(conditionClass,this.context.getClassLoader()); conditions.add(condition); } } //3.根据Order来进行排序 AnnotationAwareOrderComparator.sort(conditions); //4.对集合内的condition统一调用matches()方法,一旦遇到条件判断不满足的则返回true对此注解类元素进行忽略 for(Conditioncondition:conditions){ ConfigurationPhaserequiredPhase=null; if(conditioninstanceofConfigurationCondition){ requiredPhase=((ConfigurationCondition)condition).getConfigurationPhase(); } if((requiredPhase==null||requiredPhase==phase)&&!condition.matches(this.context,metadata)){ returntrue; } } returnfalse; }

具体的代码解释已经按照注释给出了,其实也很简单,读者稍微阅读就能明白了。另外额外的注解比如@ConditionalOnMissingBean等读者可自行去阅读代码分析,笔者此处就不展开了

2.@AutoConfigureAfter

与@AutoConfigureBefore类同,代表的含义就是自动注入在什么类加载前或者之后。先来看下其内部源码

@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented public@interfaceAutoConfigureAfter{ /** *Theauto-configureclassesthatshouldhavealreadybeenapplied. *@returntheclasses */ Class<?>[]value()default{}; /** *Thenamesoftheauto-configureclassesthatshouldhavealreadybeenapplied. *@returntheclassnames *@since1.2.2 */ String[]name()default{}; }

只作用于类上,内部属性name表明beanDefinition的类名;内部属性value表明beanDefinition的类。

那么其是如何被解析的呢,也是基于前文的ConfigurationClassParser#parse()方法,具体如下 publicvoidparse(Set<BeanDefinitionHolder>configCandidates){ this.deferredImportSelectors=newLinkedList<>(); //解析@Configuration注解 .... //解析DeferredImportSelector接口类,表面上也就是延迟解析的意思 processDeferredImportSelectors(); }

笔者此处只关注*processDeferredImportSelectors()*方法,通过此方法便可察觉到@AutoConfigureAfter等注解的蛛丝马迹

ConfigurationClassParser#processDeferredImportSelectors()

直接阅读源码

privatevoidprocessDeferredImportSelectors(){ //1.通过processImport()方法得到DeferredImportSelector接口集合,无则直接返回 List<DeferredImportSelectorHolder>deferredImports=this.deferredImportSelectors; this.deferredImportSelectors=null; if(deferredImports==null){ return; } //2.排序 deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); //3.遍历DeferredImportSelector接口集合,获取Group集合类,默认为DefaultDeferredImportSelectorGroup Map<Object,DeferredImportSelectorGrouping>groupings=newLinkedHashMap<>(); Map<AnnotationMetadata,ConfigurationClass>configurationClasses=newHashMap<>(); for(DeferredImportSelectorHolderdeferredImport:deferredImports){ //noticethis........ Class<?extendsGroup>group=deferredImport.getImportSelector().getImportGroup(); DeferredImportSelectorGroupinggrouping=groupings.computeIfAbsent( (group!=null?group:deferredImport), key->newDeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } //4.遍历Group集合,作用也是调用processImport()方法用于解析@Import for(DeferredImportSelectorGroupinggrouping:groupings.values()){ grouping.getImports().forEach(entry->{ ConfigurationClassconfigurationClass=configurationClasses.get(entry.getMetadata()); try{ processImports(configurationClass,asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()),false); } catch(BeanDefinitionStoreExceptionex){ throwex; } catch(Throwableex){ thrownewBeanDefinitionStoreException( "Failedtoprocessimportcandidatesforconfigurationclass["+ configurationClass.getMetadata().getClassName()+"]",ex); } }); } }

笔者和读者此处只需要关注deferredImport.getImportSelector().getImportGroup()这个方法即可,此处以AutoConfigurationImportSelector.class为例

AutoConfigurationImportSelector

首先看下其*getImportGroup()*方法

publicClass<?extendsGroup>getImportGroup(){ returnAutoConfigurationGroup.class; }

再观察下AutoConfigurationGroup此类的*selectImports()*方法

publicIterable<Entry>selectImports(){ returnsortAutoConfigurations().stream() .map((importClassName)->newEntry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }

关键点来了,就在sortAutoConfigurations()方法,其会通过AutoConfigurationSorter类来对导入的class类进行排序,至于如何排序我们继续往下看

AutoConfigurationSorter

排序方法getInPriorityOrder(),我们看下源码

publicList<String>getInPriorityOrder(Collection<String>classNames){ AutoConfigurationClassesclasses=newAutoConfigurationClasses( this.metadataReaderFactory,this.autoConfigurationMetadata,classNames); List<String>orderedClassNames=newArrayList<>(classNames); //Initiallysortalphabetically.首先根据ASCII来进行排序 Collections.sort(orderedClassNames); //Thensortbyorder,再根据Order来进行排序 orderedClassNames.sort((o1,o2)->{ inti1=classes.get(o1).getOrder(); inti2=classes.get(o2).getOrder(); returnInteger.compare(i1,i2); }); //Thenrespect@AutoConfigureBefore@AutoConfigureAfter orderedClassNames=sortByAnnotation(classes,orderedClassNames); returnorderedClassNames; }

可以得出,最关键的排序来自*sortByAnnotation()*方法,具体就不看了,无非是根据before/after,来对importClassName进行排序得出一个有序的集合。

1.最后再回到ConfigurationClassParser#processDeferredImportSelectors()方法的最后一段,其会对上述的有序的集合遍历操作processImports()方法,如果对应的class类不存在则会报错,也就满足了AutoConfigureBefore/AutoConfigureAfter的含义。

2.上述的@AutoConfigureAfter注解解析只作用于META-INF\spring.factories

文件中EnableAutoConfiguration属性对应的class类集合。

(v2.0版本以下支持用户使用该注解直接应用自定义类;v2.0版本以上,如果用户也使用了该注解,也需要在META-INF\spring.factories配置相应的EnableAutoConfiguration属性)

小结

针对**@Conditional@AutoConfigureAfter**的具体解析可见上文,本文也是对前文的补充。希望读者在阅读此文的同时务必阅读前文方可理解上述的代码含义。同时因为这两个注解具有条件性,所以springboot多用此两注解来相互搭配构建不同条件的依赖部署,对去配置化起到了很大的作用。以WebMvcAutoConfiguration类作为结尾

@Configuration @ConditionalOnWebApplication(type=Type.SERVLET) @ConditionalOnClass({Servlet.class,DispatcherServlet.class,WebMvcConfigurer.class}) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE+10) @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class}) publicclassWebMvcAutoConfiguration{ }

本文内容总结:1.@Conditional,@Conditional注解被解析入口,2.@AutoConfigureAfter,小结,

原文链接:https://www.cnblogs.com/question-sky/p/9427245.html