承接前文,本文将在前文的基础上阐述
@AutoConfigureAfter
和@Conditional
注解的作用与解析
1.@Conditional
根据单词来理解,其就是条件的意思。在分析之前我们可以看下其内部源码
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Conditional { /** * All {@link Condition}s that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class [] value();}
其作用于类、方法上,且指定的value值必须是org.springframework.context.annotation.Condition的实现类,供条件判断。
以此为基础而扩展的注解还有@ConditionalBean
、@ConditionalOnWebApplication
、@ConditionalOnClass
、@ConditionalOnMissingBean
等等。 @Conditional注解被解析入口
那么我们肯定想知道,其中的注解是如何被解析的呢。其实在前文中的ConfigurationClassParser
类中,在执行真正的doProcessConfigurationClass()方法前,会执行如下的代码
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { // 条件判断,满足则直接返回,不进行后续的解析 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } .... // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
也就是会执行上述的ConditionEvaluator#shouldSkip()
方法,只有条件不满足后才会继续往下执行真正的@Configuration
注解解析。
ConditionEvaluator#shouldSkip()
废话不多说,直接上源码
// metadata为被注解的类元素,返回值为true表明条件满足应该被忽略 public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable 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.获取类上所有含有@Conditional注解的value集合(其会递归找寻注解的注解) Listconditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // 3.根据Order来进行排序 AnnotationAwareOrderComparator.sort(conditions); // 4.对集合内的condition统一调用matches()方法,一旦遇到条件判断不满足的则返回true对此注解类元素进行忽略 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; }
具体的代码解释已经按照注释给出了,其实也很简单,读者稍微阅读就能明白了。另外额外的注解比如@ConditionalOnMissingBean
等读者可自行去阅读代码分析,笔者此处就不展开了
2.@AutoConfigureAfter
与@AutoConfigureBefore
类同,代表的含义就是自动注入在什么类加载前或者之后。先来看下其内部源码
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.TYPE })@Documentedpublic @interface AutoConfigureAfter { /** * The auto-configure classes that should have already been applied. * @return the classes */ Class [] value() default {}; /** * The names of the auto-configure classes that should have already been applied. * @return the class names * @since 1.2.2 */ String[] name() default {};}
只作用于类上,内部属性name
表明beanDefinition的类名;内部属性value
表明beanDefinition的类。
ConfigurationClassParser#parse()
方法,具体如下 public void parse(SetconfigCandidates) { this.deferredImportSelectors = new LinkedList<>(); // 解析@Configuration注解 .... // 解析DeferredImportSelector接口类,表面上也就是延迟解析的意思 processDeferredImportSelectors(); }
笔者此处只关注processDeferredImportSelectors()方法,通过此方法便可察觉到@AutoConfigureAfter
等注解的蛛丝马迹
ConfigurationClassParser#processDeferredImportSelectors()
直接阅读源码
private void processDeferredImportSelectors() { // 1.通过processImport()方法得到DeferredImportSelector接口集合,无则直接返回 ListdeferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; if (deferredImports == null) { return; } // 2.排序 deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 3.遍历DeferredImportSelector接口集合,获取Group集合类,默认为DefaultDeferredImportSelectorGroup Map
笔者和读者此处只需要关注deferredImport.getImportSelector().getImportGroup()
这个方法即可,此处以AutoConfigurationImportSelector.class
为例
AutoConfigurationImportSelector
首先看下其getImportGroup()方法
public Class getImportGroup() { return AutoConfigurationGroup.class; }
再观察下AutoConfigurationGroup
此类的selectImports()方法
public IterableselectImports() { return sortAutoConfigurations().stream() .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }
关键点来了,就在sortAutoConfigurations()方法,其会通过AutoConfigurationSorter类来对导入的class类进行排序,至于如何排序我们继续往下看
AutoConfigurationSorter
排序方法getInPriorityOrder(),我们看下源码
public ListgetInPriorityOrder(Collection classNames) { AutoConfigurationClasses classes = new AutoConfigurationClasses( this.metadataReaderFactory, this.autoConfigurationMetadata, classNames); List orderedClassNames = new ArrayList<>(classNames); // Initially sort alphabetically.首先根据ASCII来进行排序 Collections.sort(orderedClassNames); // Then sort by order,再根据Order来进行排序 orderedClassNames.sort((o1, o2) -> { int i1 = classes.get(o1).getOrder(); int i2 = classes.get(o2).getOrder(); return Integer.compare(i1, i2); }); // Then respect @AutoConfigureBefore @AutoConfigureAfter orderedClassNames = sortByAnnotation(classes, orderedClassNames); return orderedClassNames; }
可以得出,最关键的排序来自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 })public class WebMvcAutoConfiguration {}