springboot情操陶冶-@Conditional和@AutoConfigureAfter注解解析
本文内容纲要:
-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