@EnableAutoConfiguration处理逻辑

这个系列分为5篇

1. @Component,@Service等注解是如何被解析的

2. @Enable驱动原理

3. @EnableAutoConfiguration处理逻辑

4. spring,springBoot事件

5. 仅需四步,写一个springboot starter

引言

工作中,我们直接或间接的,用到@EnableAutoConfiguration注解。

今天,我们就聊聊@EnableAutoConfiguration的处理逻辑。

找核心方法

@Enable开头的的注解,上一般都有@Import用于指定要注解的逻辑实现类。

@EnableAutoConfiguration上的@Import,导入的是 AutoConfigurationImportSelector。

AutoConfigurationImportSelector就是要找的入口类。 类关系如下:

Aware系列都是用于注入响应的资源,Ordered用于排序。

值得关注的是 DeferredImportSelector,查看其类注释,简要翻译如下:

 importselector的变体,在所有@Configuration bean之后运行,可以实现Ordered进行排序。

 提供{getImportGroup(),它可以跨不同的选择器提供额外的排序和过滤逻辑。

DeferredImportSelector保证在所有@Configuration加载之后执行,也就说,如果有相关配置类已加载,则可以跳过自动装配类。

DeferredImportSelector是如何保证在@Configuration bean加载之后执行的呢???

带着这个疑问,我查看了ConfigurationClassPostProcessor#processConfigBeanDefinitions

(至于为什么要查看这个方法,请看上一篇 @Enable驱动原理)

浏览过程如下:

概要逻辑如下:

1. ImportSelector的解析在ConfigurationClassParser#processImports中处理

在其中this.deferredImportSelectorHandler.handle(..)j将DeferredImportSelector放入队列,延后处理。

2. DeferredImportSelector处理逻辑在

ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。

浏览this.deferredImportSelectorHandler.process()代码;

DeferredImportSelectorGrouping#getImports的代码如下:

1private static class DeferredImportSelectorGrouping { 2 private final DeferredImportSelector.Group group; 3 public Iterable<Group.Entry> getImports() { 4 for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { 5 this.group.process(deferredImport.getConfigurationClass().getMetadata(), 6 deferredImport.getImportSelector()); 7 } 8 return this.group.selectImports(); 9} 10} 11}

这里需要关注的是this.group.process,this.group.selectImports2个方法。

也就是AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我们需要关注的核心方法。

逐个分析

process

1public class AutoConfigurationImportSelector { 2private static class AutoConfigurationGroup{ 3 //省略其他代码 4 private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>(); 5@Override 6public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) { 7 Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, 8 () -> String.format("Only %s implementations are supported, got %s", 9 AutoConfigurationImportSelector.class.getSimpleName(), 10 deferredImportSelector.getClass().getName())); 11 AutoConfigurationEntry autoConfigurationEntry = 12 ((AutoConfigurationImportSelector) deferredImportSelector) 13 .getAutoConfigurationEntry(getAutoConfigurationMetadata(), 14 annotationMetadata); 15 this.autoConfigurationEntries.add(autoConfigurationEntry); 16 for (String importClassName : autoConfigurationEntry.getConfigurations()) { 17 this.entries.putIfAbsent(importClassName, annotationMetadata); 18 } 19} 20}

概要逻辑:

1.getAutoConfigurationMetadata() 加载autoConfigurationMetadata,

2.getAutoConfigurationEntry()根据autoConfigurationMetadata获得AutoConfigurationEntry

3.通过AutoConfigurationEntry ,获得要导入的类的名称,存入内部的 autoConfigurationEntries中

1. getAutoConfigurationMetadata() 加载autoConfigurationMetadata

1public class AutoConfigurationImportSelector { 2private static class AutoConfigurationGroup{ 3private AutoConfigurationMetadata getAutoConfigurationMetadata() { 4 if (this.autoConfigurationMetadata == null) { 5 this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); 6 } 7 return this.autoConfigurationMetadata; 8} 9} 10}

核心就是AutoConfigurationMetadataLoader.loadMetadata 查看其源码如下:

1final class AutoConfigurationMetadataLoader { 2 protected static final String PATH 3 = "META-INF/" + "spring-autoconfigure-metadata.properties"; 4 private AutoConfigurationMetadataLoader() { 5 } 6 public static AutoConfigurationMetadata loadMetadata 7 (ClassLoader classLoader) { 8 return loadMetadata(classLoader, PATH); 9 } 10 static AutoConfigurationMetadata loadMetadata 11 (ClassLoader classLoader, String path) { 12 try { 13 Enumeration<URL> urls = (classLoader != null) 14 ? classLoader.getResources(path) 15 : ClassLoader.getSystemResources(path); 16 Properties properties = new Properties(); 17 while (urls.hasMoreElements()) { 18 properties.putAll( 19 PropertiesLoaderUtils.loadProperties( 20 new UrlResource(urls.nextElement()))); 21 } 22 return loadMetadata(properties); 23 } 24 catch (IOException ex) { 25 throw new 26 IllegalArgumentException( 27 "Unable to load @ConditionalOnClass location [" + path + "]", 28 ex); 29 } 30 } 31 //省略部分代码 32 }

加载autoConfigurationMetadata,就是读取META-INF/spring-autoconfigure-metadata.properties的配置文件,转换为autoConfigurationMetadata对象。

2.根据autoConfigurationMetadata获得AutoConfigurationEntry

getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)的实现如下

1public class AutoConfigurationImportSelector{ 2 //省略其他代码 3protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, 4 AnnotationMetadata annotationMetadata) { 5 if (!isEnabled(annotationMetadata)) { 6 return EMPTY_ENTRY; 7 } 8 AnnotationAttributes attributes = getAttributes(annotationMetadata); 9 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); 10 configurations = removeDuplicates(configurations); 11 Set<String> exclusions = getExclusions(annotationMetadata, attributes); 12 checkExcludedClasses(configurations, exclusions); 13 configurations.removeAll(exclusions); 14 configurations = filter(configurations, autoConfigurationMetadata); 15 fireAutoConfigurationImportEvents(configurations, exclusions); 16 return new AutoConfigurationEntry(configurations, exclusions); 17} 18}

概要逻辑:

  • getAttributes获取注解的属性
  • getCandidateConfiguration获取候选装配组件
  • removeDuplicates删除重复的配置项
  • getExclusions 获取排除的组件
  • spring.autoconfigure.exclude,exclude,excludeName对应的值存储到set中
  • checkExcludedClasses 当前类在classLoader中,但是不在候选列表中内抛出异常
  • configurations.removeAll(exclusions)移除需要排除的配置项
  • filter 过滤不满足条件的自动装配组件
  • fireAutoConfigurationImportEvents 发送@EnableAutoConfiguration的自动装配事件

getCandidateConfiguration获取候选装配组件

1protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 2 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 3 getBeanClassLoader()); 4 Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " 5 + "are using a custom packaging, make sure that file is correct."); 6 return configurations; 7}

查看 SpringFactoriesLoader源码如下:

1public final class SpringFactoriesLoader { 2 //省略部分代码 3 public static final String FACTORIES_RESOURCE_LOCATION 4 = "META-INF/spring.factories"; 5 6 public static List<String> loadFactoryNames( 7 Class<?> factoryClass, @Nullable ClassLoader classLoader) { 8 String factoryClassName = factoryClass.getName(); 9 return loadSpringFactories(classLoader) 10 .getOrDefault(factoryClassName, Collections.emptyList()); 11} 12 13private static Map<String, List<String>> loadSpringFactories( 14 @Nullable ClassLoader classLoader) { 15 MultiValueMap<String, String> result = cache.get(classLoader); 16 if (result != null) { 17 return result; 18 } 19 try { 20 Enumeration<URL> urls = (classLoader != null ? 21 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : 22 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); 23 result = new LinkedMultiValueMap<>(); 24 while (urls.hasMoreElements()) { 25 URL url = urls.nextElement(); 26 UrlResource resource = new UrlResource(url); 27 Properties properties = PropertiesLoaderUtils 28 .loadProperties(resource); 29 for (Map.Entry<?, ?> entry : properties.entrySet()) { 30 String factoryClassName = ((String) entry.getKey()).trim(); 31 for (String factoryName : StringUtils 32 .commaDelimitedListToStringArray( 33 (String) entry.getValue())) { 34 result.add(factoryClassName, factoryName.trim()); 35 } 36 } 37 } 38 cache.put(classLoader, result); 39 return result; 40 } 41 catch (IOException ex) { 42 throw new IllegalArgumentException( 43 "Unable to load factories from location [" + 44 FACTORIES_RESOURCE_LOCATION + "]", ex); 45 } 46} 47}

读取指定ClassLoader下的所有的,META-INF/spring.factories资源内容,合并一个key为全类名,Value为实现类名列表的Map,从Map中找到指定的key对应的实现类全类名列表

filter 过滤不满足条件的自动装配组件

1public class AutoConfigurationImportSelector{ 2private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { 3 long startTime = System.nanoTime(); 4 String[] candidates = StringUtils.toStringArray(configurations); 5 boolean[] skip = new boolean[candidates.length]; 6 boolean skipped = false; 7 for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { 8 invokeAwareMethods(filter); 9 boolean[] match = filter.match(candidates, autoConfigurationMetadata); 10 for (int i = 0; i < match.length; i++) { 11 if (!match[i]) { 12 skip[i] = true; 13 candidates[i] = null; 14 skipped = true; 15 } 16 } 17 } 18 if (!skipped) { 19 return configurations; 20 } 21 List<String> result = new ArrayList<>(candidates.length); 22 for (int i = 0; i < candidates.length; i++) { 23 if (!skip[i]) { 24 result.add(candidates[i]); 25 } 26 } 27 if (logger.isTraceEnabled()) { 28 int numberFiltered = configurations.size() - result.size(); 29 logger.trace("Filtered " + numberFiltered + " auto configuration class in " 30 + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); 31 } 32 return new ArrayList<>(result); 33} 34}

AutoConfigurationImportFilter#match没有匹配上的过滤掉

查看getAutoConfigurationImportFilters()源码如下:

1protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() { 2 return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); 3}

SpringFactoriesLoader.loadFactories(...),调用SpringFactoriesLoader.loadFactoryNames(...)后,将列表中的逐个实例化,排序后返回。

查看spring.factories

AutoConfigurationImportFilter的实现类,有

OnClassCondition类,onBeanCondition,OnWebApplicationCondition等。

扩展-查阅OnClassCondition的match方法

OnClassCondition代码大致逻辑如下:

OnClassCondition#match调用了autoConfigurationMetadata.getSet获取当前配置类的ConditionOnClass属性。

举例说明OnClassCondition#getOutcomes

例如:

    加载AConfigurationClass,AConfigurationClass的ConditionOnClass=XXX,

    如果XXX不在当前classloader下,排除 AConfigurationClass

selectImports

1public class AutoConfigurationImportSelector{ 2private static class AutoConfigurationGroup{ 3@Override 4public Iterable<Entry> selectImports() { 5 if (this.autoConfigurationEntries.isEmpty()) { 6 return Collections.emptyList(); 7 } 8 Set<String> allExclusions = this.autoConfigurationEntries.stream() 9 .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); 10 Set<String> processedConfigurations = 11 this.autoConfigurationEntries.stream() 12 .map(AutoConfigurationEntry::getConfigurations) 13 .flatMap(Collection::stream) 14 .collect(Collectors.toCollection(LinkedHashSet::new)); 15 processedConfigurations.removeAll(allExclusions); 16 17 return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() 18 .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName)) 19 .collect(Collectors.toList()); 20} 21} 22} 23} 24}

概要逻辑如下

排除不必要的项

sortAutoConfigurations排序自动配置项。

查看sortAutoConfigurations

1public class AutoConfigurationImportSelector{ 2private static class AutoConfigurationGroup{ 3private List<String> sortAutoConfigurations(Set<String> configurations, 4 AutoConfigurationMetadata autoConfigurationMetadata) { 5 return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata) 6 .getInPriorityOrder(configurations); 7} 8} 9}

核心在AutoConfigurationSorter#getInPriorityOrder中

1class AutoConfigurationSorter { 2 //省略其他代码 3public List<String> getInPriorityOrder(Collection<String> classNames) { 4 AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, 5 this.autoConfigurationMetadata, classNames); 6 List<String> orderedClassNames = new ArrayList<>(classNames); 7 // Initially sort alphabetically 8 Collections.sort(orderedClassNames); 9 // Then sort by order 10 orderedClassNames.sort((o1, o2) -> { 11 int i1 = classes.get(o1).getOrder(); 12 int i2 = classes.get(o2).getOrder(); 13 return Integer.compare(i1, i2); 14 }); 15 // Then respect @AutoConfigureBefore @AutoConfigureAfter 16 orderedClassNames = sortByAnnotation(classes, orderedClassNames); 17 return orderedClassNames; 18} 19}

排序规则如下:

按照字母顺序加载,否则先按AutoCOnfigureOrder,再按@AutoConfigureBefore,@AutoConfigureAfter进行排序。

注:

AutoCOnfigurationMetadata(META-INF/spring-autoconfigure-metadata.properties)中包含AutoConfigureOrder,AutoConfigureBefore,AutoConfigureAfter信息

名称

注释

是否推荐

@AutoCOnfigureOrder

绝对自动装配顺序

@AutoConfigureBefore,

@AutoConfigureAfter

相对自动装配顺序,建议使用name属性

整体流程到这里就看完了,但是自定义配置如何覆盖自动装配的呢?

查漏补缺

为了查找自定义配置如何覆盖自动装配,我再次翻阅代码

查看shouldSkip,结合各种Conditional注解进行过滤

@Conditional 是个多个条件注解的元注解,例如:@ConditionalOnMissingBean,@ConditionalOnClass等。

具体逻辑,就不在这里赘述了,如果想查阅,可以查看org.springframework.boot.autoconfigure.condition.OnClassCondition这个类,它是@ConditionalOnClass的逻辑处理类。

总结

  1. @EnableAutoConfiguration的核心处理类是AutoConfigurationImportSelector
  2. AutoConfigurationImportSelector实现了DeferredImportSelector
  3. ImportSelector的解析在ConfigurationClassParser#processImports中处理在其中this.deferredImportSelectorHandler.handle(..)j将DeferredImportSelector放入队列,延后处理。DeferredImportSelector处理逻辑在ConfigurationClassParser#parse中的this.deferredImportSelectorHandler.process()中。
  4. AutoConfigurationImportSelector.AutoConfigurationGroup的process,selectImports就是我们需要关注的核心方法。
  5. ConfigurationClassParser#doProcessConfigurationClass中的!this.conditionEvaluator.shouldSkip(...) 结合了各种Condition注解,实现了自定义配置覆盖自动装配。
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )